aboutsummaryrefslogtreecommitdiff
path: root/example
diff options
context:
space:
mode:
Diffstat (limited to 'example')
-rw-r--r--example/MacOS/README.md18
-rw-r--r--example/MacOS/ndpiExample/ndpiExample.xcodeproj/project.pbxproj1136
-rw-r--r--example/MacOS/ndpiExample/ndpiExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata7
-rw-r--r--example/MacOS/ndpiExample/ndpiExample/AppDelegate.h29
-rw-r--r--example/MacOS/ndpiExample/ndpiExample/AppDelegate.m41
-rw-r--r--example/MacOS/ndpiExample/ndpiExample/Assets.xcassets/AppIcon.appiconset/Contents.json58
-rw-r--r--example/MacOS/ndpiExample/ndpiExample/Base.lproj/Main.storyboard732
-rw-r--r--example/MacOS/ndpiExample/ndpiExample/Info.plist32
-rw-r--r--example/MacOS/ndpiExample/ndpiExample/ViewController.h29
-rw-r--r--example/MacOS/ndpiExample/ndpiExample/ViewController.m106
-rw-r--r--example/MacOS/ndpiExample/ndpiExample/capture.pcapbin0 -> 80922 bytes
-rw-r--r--example/MacOS/ndpiExample/ndpiExample/main.m27
-rw-r--r--example/MacOS/ndpiExample/ndpiExample/ndpiExample.entitlements5
-rw-r--r--example/MacOS/ndpiExample/ndpiExample/ndpi_utils.h29
-rw-r--r--example/Makefile.am8
-rw-r--r--example/ndpiReader.c2182
-rw-r--r--example/ndpi_util.c553
-rw-r--r--example/ndpi_util.h51
-rw-r--r--example/protos.txt2
-rw-r--r--example/uthash.h1096
20 files changed, 5777 insertions, 364 deletions
diff --git a/example/MacOS/README.md b/example/MacOS/README.md
new file mode 100644
index 000000000..e7a9f91fc
--- /dev/null
+++ b/example/MacOS/README.md
@@ -0,0 +1,18 @@
+# How to use?
+
+You need to first compile the nDPI library as usual:
+
+- ./autogen.sh
+- ./configure
+- make
+
+Then open the Xcode project and you are ready to go. The default behavior is to analyze an embeded pcap file `capture.pcap`. You can change the behavior by changing command line input in `ViewController.m` file.
+
+# What does the XCode project do?
+
+It's a dummy Mac App project with a **Run** button. It doesn't modify any nDPI code except that it renamed the `main` function to `orginal_main` in `ndpiReader.c` (because the Mac App has it's own main function) and call the `orginal_main` with synthetic command line input from `ViewController.m` file when the **Run** button is clicked.
+
+It also fixes some problems when compiling with Xcode. Some are listed below:
+- Add missed `NDPI_LOG_DEBUG2` macro definition implementation (defined as `NDPI_LOG_DEBUG2_XCODE_PROJ` in `ViewController.m`)
+- Add an empty ndpi_utils.h file to make `protocols/attic/ftp.c` and `protocols/attic/secondlife.c` can compile
+- Specially treat `ndpi_patricia.c` by not adding it into compilation source, since it's directly included in `ndpi_main.c`
diff --git a/example/MacOS/ndpiExample/ndpiExample.xcodeproj/project.pbxproj b/example/MacOS/ndpiExample/ndpiExample.xcodeproj/project.pbxproj
new file mode 100644
index 000000000..d73d10c05
--- /dev/null
+++ b/example/MacOS/ndpiExample/ndpiExample.xcodeproj/project.pbxproj
@@ -0,0 +1,1136 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 48;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ E3953F5420254989000BBA0D /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E3953F5320254989000BBA0D /* AppDelegate.m */; };
+ E3953F5720254989000BBA0D /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E3953F5620254989000BBA0D /* ViewController.m */; };
+ E3953F5920254989000BBA0D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E3953F5820254989000BBA0D /* Assets.xcassets */; };
+ E3953F5C2025498A000BBA0D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E3953F5A2025498A000BBA0D /* Main.storyboard */; };
+ E3953F5F2025498A000BBA0D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E3953F5E2025498A000BBA0D /* main.m */; };
+ E395430B20255354000BBA0D /* Makefile.am in Resources */ = {isa = PBXBuildFile; fileRef = E39540A520255353000BBA0D /* Makefile.am */; };
+ E395430C20255354000BBA0D /* ndpi_define.h.in in Resources */ = {isa = PBXBuildFile; fileRef = E39540A720255353000BBA0D /* ndpi_define.h.in */; };
+ E395431720255354000BBA0D /* Makefile in Sources */ = {isa = PBXBuildFile; fileRef = E39540BC20255353000BBA0D /* Makefile */; };
+ E395431820255354000BBA0D /* Makefile.am in Resources */ = {isa = PBXBuildFile; fileRef = E39540BD20255353000BBA0D /* Makefile.am */; };
+ E395431920255354000BBA0D /* Makefile.in in Resources */ = {isa = PBXBuildFile; fileRef = E39540BE20255353000BBA0D /* Makefile.in */; };
+ E395431A20255354000BBA0D /* Makefile.simple in Resources */ = {isa = PBXBuildFile; fileRef = E39540BF20255353000BBA0D /* Makefile.simple */; };
+ E395431B20255354000BBA0D /* ndpi_content_match.c.inc in Sources */ = {isa = PBXBuildFile; fileRef = E39540C020255353000BBA0D /* ndpi_content_match.c.inc */; };
+ E395431C20255354000BBA0D /* ndpi_main.c in Sources */ = {isa = PBXBuildFile; fileRef = E39540C120255353000BBA0D /* ndpi_main.c */; };
+ E39543A320255354000BBA0D /* afp.c in Sources */ = {isa = PBXBuildFile; fileRef = E395414B20255353000BBA0D /* afp.c */; };
+ E39543A420255354000BBA0D /* aimini.c in Sources */ = {isa = PBXBuildFile; fileRef = E395414C20255353000BBA0D /* aimini.c */; };
+ E39543A520255354000BBA0D /* amqp.c in Sources */ = {isa = PBXBuildFile; fileRef = E395414D20255353000BBA0D /* amqp.c */; };
+ E39543A620255354000BBA0D /* apple_push.c in Sources */ = {isa = PBXBuildFile; fileRef = E395414E20255353000BBA0D /* apple_push.c */; };
+ E39543A720255354000BBA0D /* applejuice.c in Sources */ = {isa = PBXBuildFile; fileRef = E395414F20255353000BBA0D /* applejuice.c */; };
+ E39543A820255354000BBA0D /* armagetron.c in Sources */ = {isa = PBXBuildFile; fileRef = E395415020255353000BBA0D /* armagetron.c */; };
+ E39543A920255354000BBA0D /* flash.c in Sources */ = {isa = PBXBuildFile; fileRef = E395415220255353000BBA0D /* flash.c */; };
+ E39543AA20255354000BBA0D /* ftp.c in Sources */ = {isa = PBXBuildFile; fileRef = E395415320255353000BBA0D /* ftp.c */; };
+ E39543AB20255354000BBA0D /* manolito.c in Sources */ = {isa = PBXBuildFile; fileRef = E395415420255353000BBA0D /* manolito.c */; };
+ E39543AC20255354000BBA0D /* popo.c in Sources */ = {isa = PBXBuildFile; fileRef = E395415520255353000BBA0D /* popo.c */; };
+ E39543AD20255354000BBA0D /* secondlife.c in Sources */ = {isa = PBXBuildFile; fileRef = E395415620255353000BBA0D /* secondlife.c */; };
+ E39543AE20255354000BBA0D /* ayiya.c in Sources */ = {isa = PBXBuildFile; fileRef = E395415720255353000BBA0D /* ayiya.c */; };
+ E39543AF20255354000BBA0D /* battlefield.c in Sources */ = {isa = PBXBuildFile; fileRef = E395415820255353000BBA0D /* battlefield.c */; };
+ E39543B020255354000BBA0D /* bgp.c in Sources */ = {isa = PBXBuildFile; fileRef = E395415920255353000BBA0D /* bgp.c */; };
+ E39543B120255354000BBA0D /* bittorrent.c in Sources */ = {isa = PBXBuildFile; fileRef = E395415A20255353000BBA0D /* bittorrent.c */; };
+ E39543B220255354000BBA0D /* bjnp.c in Sources */ = {isa = PBXBuildFile; fileRef = E395415B20255353000BBA0D /* bjnp.c */; };
+ E39543B320255354000BBA0D /* btlib.c in Sources */ = {isa = PBXBuildFile; fileRef = E395415C20255353000BBA0D /* btlib.c */; };
+ E39543B420255354000BBA0D /* checkmk.c in Sources */ = {isa = PBXBuildFile; fileRef = E395415E20255353000BBA0D /* checkmk.c */; };
+ E39543B520255354000BBA0D /* ciscovpn.c in Sources */ = {isa = PBXBuildFile; fileRef = E395415F20255353000BBA0D /* ciscovpn.c */; };
+ E39543B620255354000BBA0D /* citrix.c in Sources */ = {isa = PBXBuildFile; fileRef = E395416020255353000BBA0D /* citrix.c */; };
+ E39543B720255354000BBA0D /* coap.c in Sources */ = {isa = PBXBuildFile; fileRef = E395416120255353000BBA0D /* coap.c */; };
+ E39543B820255354000BBA0D /* collectd.c in Sources */ = {isa = PBXBuildFile; fileRef = E395416220255353000BBA0D /* collectd.c */; };
+ E39543B920255354000BBA0D /* corba.c in Sources */ = {isa = PBXBuildFile; fileRef = E395416320255353000BBA0D /* corba.c */; };
+ E39543BA20255354000BBA0D /* crossfire.c in Sources */ = {isa = PBXBuildFile; fileRef = E395416420255353000BBA0D /* crossfire.c */; };
+ E39543BB20255354000BBA0D /* csgo.c in Sources */ = {isa = PBXBuildFile; fileRef = E395416520255353000BBA0D /* csgo.c */; };
+ E39543BC20255354000BBA0D /* dcerpc.c in Sources */ = {isa = PBXBuildFile; fileRef = E395416620255353000BBA0D /* dcerpc.c */; };
+ E39543BD20255354000BBA0D /* dhcp.c in Sources */ = {isa = PBXBuildFile; fileRef = E395416720255353000BBA0D /* dhcp.c */; };
+ E39543BE20255354000BBA0D /* dhcpv6.c in Sources */ = {isa = PBXBuildFile; fileRef = E395416820255353000BBA0D /* dhcpv6.c */; };
+ E39543BF20255354000BBA0D /* diameter.c in Sources */ = {isa = PBXBuildFile; fileRef = E395416920255353000BBA0D /* diameter.c */; };
+ E39543C020255354000BBA0D /* directconnect.c in Sources */ = {isa = PBXBuildFile; fileRef = E395416A20255353000BBA0D /* directconnect.c */; };
+ E39543C120255354000BBA0D /* directdownloadlink.c in Sources */ = {isa = PBXBuildFile; fileRef = E395416B20255353000BBA0D /* directdownloadlink.c */; };
+ E39543C220255354000BBA0D /* dns.c in Sources */ = {isa = PBXBuildFile; fileRef = E395416C20255353000BBA0D /* dns.c */; };
+ E39543C320255354000BBA0D /* dofus.c in Sources */ = {isa = PBXBuildFile; fileRef = E395416D20255353000BBA0D /* dofus.c */; };
+ E39543C420255354000BBA0D /* drda.c in Sources */ = {isa = PBXBuildFile; fileRef = E395416E20255353000BBA0D /* drda.c */; };
+ E39543C520255354000BBA0D /* dropbox.c in Sources */ = {isa = PBXBuildFile; fileRef = E395416F20255353000BBA0D /* dropbox.c */; };
+ E39543C620255354000BBA0D /* eaq.c in Sources */ = {isa = PBXBuildFile; fileRef = E395417020255353000BBA0D /* eaq.c */; };
+ E39543C720255354000BBA0D /* edonkey.c in Sources */ = {isa = PBXBuildFile; fileRef = E395417120255353000BBA0D /* edonkey.c */; };
+ E39543C820255354000BBA0D /* fasttrack.c in Sources */ = {isa = PBXBuildFile; fileRef = E395417220255353000BBA0D /* fasttrack.c */; };
+ E39543C920255354000BBA0D /* fiesta.c in Sources */ = {isa = PBXBuildFile; fileRef = E395417320255353000BBA0D /* fiesta.c */; };
+ E39543CA20255354000BBA0D /* filetopia.c in Sources */ = {isa = PBXBuildFile; fileRef = E395417420255353000BBA0D /* filetopia.c */; };
+ E39543CB20255354000BBA0D /* fix.c in Sources */ = {isa = PBXBuildFile; fileRef = E395417520255353000BBA0D /* fix.c */; };
+ E39543CC20255354000BBA0D /* florensia.c in Sources */ = {isa = PBXBuildFile; fileRef = E395417620255353000BBA0D /* florensia.c */; };
+ E39543CD20255354000BBA0D /* ftp_control.c in Sources */ = {isa = PBXBuildFile; fileRef = E395417720255353000BBA0D /* ftp_control.c */; };
+ E39543CE20255354000BBA0D /* ftp_data.c in Sources */ = {isa = PBXBuildFile; fileRef = E395417820255353000BBA0D /* ftp_data.c */; };
+ E39543CF20255354000BBA0D /* git.c in Sources */ = {isa = PBXBuildFile; fileRef = E395417920255353000BBA0D /* git.c */; };
+ E39543D020255354000BBA0D /* gnutella.c in Sources */ = {isa = PBXBuildFile; fileRef = E395417A20255353000BBA0D /* gnutella.c */; };
+ E39543D120255354000BBA0D /* gtp.c in Sources */ = {isa = PBXBuildFile; fileRef = E395417B20255353000BBA0D /* gtp.c */; };
+ E39543D220255354000BBA0D /* guildwars.c in Sources */ = {isa = PBXBuildFile; fileRef = E395417C20255353000BBA0D /* guildwars.c */; };
+ E39543D320255354000BBA0D /* h323.c in Sources */ = {isa = PBXBuildFile; fileRef = E395417D20255353000BBA0D /* h323.c */; };
+ E39543D420255354000BBA0D /* halflife2_and_mods.c in Sources */ = {isa = PBXBuildFile; fileRef = E395417E20255353000BBA0D /* halflife2_and_mods.c */; };
+ E39543D520255354000BBA0D /* hangout.c in Sources */ = {isa = PBXBuildFile; fileRef = E395417F20255353000BBA0D /* hangout.c */; };
+ E39543D620255354000BBA0D /* hep.c in Sources */ = {isa = PBXBuildFile; fileRef = E395418020255353000BBA0D /* hep.c */; };
+ E39543D720255354000BBA0D /* http.c in Sources */ = {isa = PBXBuildFile; fileRef = E395418120255353000BBA0D /* http.c */; };
+ E39543D820255354000BBA0D /* http_activesync.c in Sources */ = {isa = PBXBuildFile; fileRef = E395418220255353000BBA0D /* http_activesync.c */; };
+ E39543D920255354000BBA0D /* iax.c in Sources */ = {isa = PBXBuildFile; fileRef = E395418320255353000BBA0D /* iax.c */; };
+ E39543DA20255354000BBA0D /* icecast.c in Sources */ = {isa = PBXBuildFile; fileRef = E395418420255353000BBA0D /* icecast.c */; };
+ E39543DB20255354000BBA0D /* ipp.c in Sources */ = {isa = PBXBuildFile; fileRef = E395418520255353000BBA0D /* ipp.c */; };
+ E39543DC20255354000BBA0D /* irc.c in Sources */ = {isa = PBXBuildFile; fileRef = E395418620255353000BBA0D /* irc.c */; };
+ E39543DD20255354000BBA0D /* jabber.c in Sources */ = {isa = PBXBuildFile; fileRef = E395418720255353000BBA0D /* jabber.c */; };
+ E39543DE20255354000BBA0D /* kakaotalk_voice.c in Sources */ = {isa = PBXBuildFile; fileRef = E395418820255353000BBA0D /* kakaotalk_voice.c */; };
+ E39543DF20255354000BBA0D /* kerberos.c in Sources */ = {isa = PBXBuildFile; fileRef = E395418920255353000BBA0D /* kerberos.c */; };
+ E39543E020255354000BBA0D /* kontiki.c in Sources */ = {isa = PBXBuildFile; fileRef = E395418A20255353000BBA0D /* kontiki.c */; };
+ E39543E120255354000BBA0D /* ldap.c in Sources */ = {isa = PBXBuildFile; fileRef = E395418B20255353000BBA0D /* ldap.c */; };
+ E39544EA20255354000BBA0D /* lisp.c in Sources */ = {isa = PBXBuildFile; fileRef = E395429420255354000BBA0D /* lisp.c */; };
+ E39544EB20255354000BBA0D /* lotus_notes.c in Sources */ = {isa = PBXBuildFile; fileRef = E395429520255354000BBA0D /* lotus_notes.c */; };
+ E39544EC20255354000BBA0D /* mail_imap.c in Sources */ = {isa = PBXBuildFile; fileRef = E395429620255354000BBA0D /* mail_imap.c */; };
+ E39544ED20255354000BBA0D /* mail_pop.c in Sources */ = {isa = PBXBuildFile; fileRef = E395429720255354000BBA0D /* mail_pop.c */; };
+ E39544EE20255354000BBA0D /* mail_smtp.c in Sources */ = {isa = PBXBuildFile; fileRef = E395429820255354000BBA0D /* mail_smtp.c */; };
+ E39544EF20255354000BBA0D /* maplestory.c in Sources */ = {isa = PBXBuildFile; fileRef = E395429920255354000BBA0D /* maplestory.c */; };
+ E39544F020255354000BBA0D /* mdns.c in Sources */ = {isa = PBXBuildFile; fileRef = E395429A20255354000BBA0D /* mdns.c */; };
+ E39544F120255354000BBA0D /* megaco.c in Sources */ = {isa = PBXBuildFile; fileRef = E395429B20255354000BBA0D /* megaco.c */; };
+ E39544F220255354000BBA0D /* mgcp.c in Sources */ = {isa = PBXBuildFile; fileRef = E395429C20255354000BBA0D /* mgcp.c */; };
+ E39544F320255354000BBA0D /* mms.c in Sources */ = {isa = PBXBuildFile; fileRef = E395429D20255354000BBA0D /* mms.c */; };
+ E39544F420255354000BBA0D /* mpegts.c in Sources */ = {isa = PBXBuildFile; fileRef = E395429E20255354000BBA0D /* mpegts.c */; };
+ E39544F520255354000BBA0D /* mqtt.c in Sources */ = {isa = PBXBuildFile; fileRef = E395429F20255354000BBA0D /* mqtt.c */; };
+ E39544F620255354000BBA0D /* msn.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542A020255354000BBA0D /* msn.c */; };
+ E39544F720255354000BBA0D /* mssql_tds.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542A120255354000BBA0D /* mssql_tds.c */; };
+ E39544F820255354000BBA0D /* mysql.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542A220255354000BBA0D /* mysql.c */; };
+ E39544F920255354000BBA0D /* netbios.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542A320255354000BBA0D /* netbios.c */; };
+ E39544FA20255354000BBA0D /* netflow.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542A420255354000BBA0D /* netflow.c */; };
+ E39544FB20255354000BBA0D /* nfs.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542A520255354000BBA0D /* nfs.c */; };
+ E39544FC20255354000BBA0D /* nintendo.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542A620255354000BBA0D /* nintendo.c */; };
+ E39544FD20255354000BBA0D /* noe.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542A720255354000BBA0D /* noe.c */; };
+ E39544FE20255354000BBA0D /* non_tcp_udp.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542A820255354000BBA0D /* non_tcp_udp.c */; };
+ E39544FF20255354000BBA0D /* ntp.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542A920255354000BBA0D /* ntp.c */; };
+ E395450020255354000BBA0D /* openft.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542AA20255354000BBA0D /* openft.c */; };
+ E395450120255354000BBA0D /* openvpn.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542AB20255354000BBA0D /* openvpn.c */; };
+ E395450220255354000BBA0D /* oracle.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542AC20255354000BBA0D /* oracle.c */; };
+ E395450320255354000BBA0D /* oscar.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542AD20255354000BBA0D /* oscar.c */; };
+ E395450420255354000BBA0D /* pando.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542AE20255354000BBA0D /* pando.c */; };
+ E395450520255354000BBA0D /* pcanywhere.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542AF20255354000BBA0D /* pcanywhere.c */; };
+ E395450620255354000BBA0D /* postgres.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542B020255354000BBA0D /* postgres.c */; };
+ E395450720255354000BBA0D /* pplive.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542B120255354000BBA0D /* pplive.c */; };
+ E395450820255354000BBA0D /* ppstream.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542B220255354000BBA0D /* ppstream.c */; };
+ E395450920255354000BBA0D /* pptp.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542B320255354000BBA0D /* pptp.c */; };
+ E395450A20255354000BBA0D /* qq.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542B420255354000BBA0D /* qq.c */; };
+ E395450B20255354000BBA0D /* quic.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542B520255354000BBA0D /* quic.c */; };
+ E395450C20255354000BBA0D /* radius.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542B620255354000BBA0D /* radius.c */; };
+ E395450D20255354000BBA0D /* rdp.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542B720255354000BBA0D /* rdp.c */; };
+ E395450E20255354000BBA0D /* redis_net.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542B820255354000BBA0D /* redis_net.c */; };
+ E395450F20255354000BBA0D /* rsync.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542B920255354000BBA0D /* rsync.c */; };
+ E395451020255354000BBA0D /* rtcp.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542BA20255354000BBA0D /* rtcp.c */; };
+ E395451120255354000BBA0D /* rtmp.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542BB20255354000BBA0D /* rtmp.c */; };
+ E395451220255354000BBA0D /* rtp.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542BC20255354000BBA0D /* rtp.c */; };
+ E395451320255354000BBA0D /* rtsp.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542BD20255354000BBA0D /* rtsp.c */; };
+ E395451420255354000BBA0D /* rx.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542BE20255354000BBA0D /* rx.c */; };
+ E395451520255354000BBA0D /* sflow.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542BF20255354000BBA0D /* sflow.c */; };
+ E395451620255354000BBA0D /* shoutcast.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542C020255354000BBA0D /* shoutcast.c */; };
+ E395451720255354000BBA0D /* sip.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542C120255354000BBA0D /* sip.c */; };
+ E395451820255354000BBA0D /* skinny.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542C220255354000BBA0D /* skinny.c */; };
+ E395451920255354000BBA0D /* skype.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542C320255354000BBA0D /* skype.c */; };
+ E395451A20255354000BBA0D /* smb.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542C420255354000BBA0D /* smb.c */; };
+ E395451B20255354000BBA0D /* smpp.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542C520255354000BBA0D /* smpp.c */; };
+ E395451C20255354000BBA0D /* snmp.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542C620255354000BBA0D /* snmp.c */; };
+ E395451D20255354000BBA0D /* socks45.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542C720255354000BBA0D /* socks45.c */; };
+ E395451E20255354000BBA0D /* socrates.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542C820255354000BBA0D /* socrates.c */; };
+ E395451F20255354000BBA0D /* someip.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542C920255354000BBA0D /* someip.c */; };
+ E395452020255354000BBA0D /* sopcast.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542CA20255354000BBA0D /* sopcast.c */; };
+ E395452120255354000BBA0D /* soulseek.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542CB20255354000BBA0D /* soulseek.c */; };
+ E395452220255354000BBA0D /* spotify.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542CC20255354000BBA0D /* spotify.c */; };
+ E395452320255354000BBA0D /* ssdp.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542CD20255354000BBA0D /* ssdp.c */; };
+ E395452420255354000BBA0D /* ssh.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542CE20255354000BBA0D /* ssh.c */; };
+ E395452520255354000BBA0D /* ssl.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542CF20255354000BBA0D /* ssl.c */; };
+ E395452620255354000BBA0D /* starcraft.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542D020255354000BBA0D /* starcraft.c */; };
+ E395452720255354000BBA0D /* stealthnet.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542D120255354000BBA0D /* stealthnet.c */; };
+ E395452820255354000BBA0D /* steam.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542D220255354000BBA0D /* steam.c */; };
+ E395452920255354000BBA0D /* stun.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542D320255354000BBA0D /* stun.c */; };
+ E395452A20255354000BBA0D /* syslog.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542D420255354000BBA0D /* syslog.c */; };
+ E395452B20255354000BBA0D /* tcp_udp.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542D520255354000BBA0D /* tcp_udp.c */; };
+ E395452C20255354000BBA0D /* teamspeak.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542D620255354000BBA0D /* teamspeak.c */; };
+ E395452D20255354000BBA0D /* teamviewer.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542D720255354000BBA0D /* teamviewer.c */; };
+ E395452E20255354000BBA0D /* telegram.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542D820255354000BBA0D /* telegram.c */; };
+ E395452F20255354000BBA0D /* telnet.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542D920255354000BBA0D /* telnet.c */; };
+ E395453020255354000BBA0D /* teredo.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542DA20255354000BBA0D /* teredo.c */; };
+ E395453120255354000BBA0D /* tftp.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542DB20255354000BBA0D /* tftp.c */; };
+ E395453220255354000BBA0D /* thunder.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542DC20255354000BBA0D /* thunder.c */; };
+ E395453320255354000BBA0D /* tinc.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542DD20255354000BBA0D /* tinc.c */; };
+ E395453420255354000BBA0D /* tor.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542DE20255354000BBA0D /* tor.c */; };
+ E395453520255354000BBA0D /* tvants.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542DF20255354000BBA0D /* tvants.c */; };
+ E395453620255354000BBA0D /* tvuplayer.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542E020255354000BBA0D /* tvuplayer.c */; };
+ E395453720255354000BBA0D /* ubntac2.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542E120255354000BBA0D /* ubntac2.c */; };
+ E395453820255354000BBA0D /* usenet.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542E220255354000BBA0D /* usenet.c */; };
+ E395453920255354000BBA0D /* vhua.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542E320255354000BBA0D /* vhua.c */; };
+ E395453A20255354000BBA0D /* viber.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542E420255354000BBA0D /* viber.c */; };
+ E395453B20255355000BBA0D /* vmware.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542E520255354000BBA0D /* vmware.c */; };
+ E395453C20255355000BBA0D /* vnc.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542E620255354000BBA0D /* vnc.c */; };
+ E395453D20255355000BBA0D /* warcraft3.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542E720255354000BBA0D /* warcraft3.c */; };
+ E395453E20255355000BBA0D /* whoisdas.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542E820255354000BBA0D /* whoisdas.c */; };
+ E395453F20255355000BBA0D /* world_of_kung_fu.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542E920255354000BBA0D /* world_of_kung_fu.c */; };
+ E395454020255355000BBA0D /* world_of_warcraft.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542EA20255354000BBA0D /* world_of_warcraft.c */; };
+ E395454120255355000BBA0D /* xbox.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542EB20255354000BBA0D /* xbox.c */; };
+ E395454220255355000BBA0D /* xdmcp.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542EC20255354000BBA0D /* xdmcp.c */; };
+ E395454320255355000BBA0D /* yahoo.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542ED20255354000BBA0D /* yahoo.c */; };
+ E395454420255355000BBA0D /* zattoo.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542EE20255354000BBA0D /* zattoo.c */; };
+ E395454520255355000BBA0D /* zeromq.c in Sources */ = {isa = PBXBuildFile; fileRef = E39542EF20255354000BBA0D /* zeromq.c */; };
+ E395454C20255355000BBA0D /* libcache.c in Sources */ = {isa = PBXBuildFile; fileRef = E395430120255354000BBA0D /* libcache.c */; };
+ E395455420255355000BBA0D /* node.c in Sources */ = {isa = PBXBuildFile; fileRef = E395430920255354000BBA0D /* node.c */; };
+ E395455520255355000BBA0D /* sort.c in Sources */ = {isa = PBXBuildFile; fileRef = E395430A20255354000BBA0D /* sort.c */; };
+ E395455D202558E6000BBA0D /* protos.txt in Resources */ = {isa = PBXBuildFile; fileRef = E3954559202558E5000BBA0D /* protos.txt */; };
+ E395455E202558E6000BBA0D /* ndpi_util.c in Sources */ = {isa = PBXBuildFile; fileRef = E395455A202558E5000BBA0D /* ndpi_util.c */; };
+ E395455F202558E6000BBA0D /* ndpiReader.c in Sources */ = {isa = PBXBuildFile; fileRef = E395455C202558E5000BBA0D /* ndpiReader.c */; };
+ E395478F20269F43000BBA0D /* libpcap.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E395478E20269F43000BBA0D /* libpcap.tbd */; };
+ E39547902026A51A000BBA0D /* ahocorasick.c in Sources */ = {isa = PBXBuildFile; fileRef = E395430020255354000BBA0D /* ahocorasick.c */; };
+ E39547942026B2AA000BBA0D /* capture.pcap in Resources */ = {isa = PBXBuildFile; fileRef = E39547932026B2A9000BBA0D /* capture.pcap */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ E3953F4F20254989000BBA0D /* ndpiExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ndpiExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ E3953F5220254989000BBA0D /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
+ E3953F5320254989000BBA0D /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
+ E3953F5520254989000BBA0D /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
+ E3953F5620254989000BBA0D /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
+ E3953F5820254989000BBA0D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+ E3953F5B2025498A000BBA0D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+ E3953F5D2025498A000BBA0D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ E3953F5E2025498A000BBA0D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ E3953F602025498A000BBA0D /* ndpiExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ndpiExample.entitlements; sourceTree = "<group>"; };
+ E39540A520255353000BBA0D /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Makefile.am; sourceTree = "<group>"; };
+ E39540A620255353000BBA0D /* ndpi_api.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ndpi_api.h; sourceTree = "<group>"; };
+ E39540A720255353000BBA0D /* ndpi_define.h.in */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ndpi_define.h.in; sourceTree = "<group>"; };
+ E39540A820255353000BBA0D /* ndpi_includes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ndpi_includes.h; sourceTree = "<group>"; };
+ E39540A920255353000BBA0D /* ndpi_main.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ndpi_main.h; sourceTree = "<group>"; };
+ E39540AA20255353000BBA0D /* ndpi_protocol_ids.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ndpi_protocol_ids.h; sourceTree = "<group>"; };
+ E39540AB20255353000BBA0D /* ndpi_protocols.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ndpi_protocols.h; sourceTree = "<group>"; };
+ E39540AC20255353000BBA0D /* ndpi_typedefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ndpi_typedefs.h; sourceTree = "<group>"; };
+ E39540AD20255353000BBA0D /* ndpi_unix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ndpi_unix.h; sourceTree = "<group>"; };
+ E39540AE20255353000BBA0D /* ndpi_win32.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ndpi_win32.h; sourceTree = "<group>"; };
+ E39540BC20255353000BBA0D /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
+ E39540BD20255353000BBA0D /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Makefile.am; sourceTree = "<group>"; };
+ E39540BE20255353000BBA0D /* Makefile.in */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Makefile.in; sourceTree = "<group>"; };
+ E39540BF20255353000BBA0D /* Makefile.simple */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Makefile.simple; sourceTree = "<group>"; };
+ E39540C020255353000BBA0D /* ndpi_content_match.c.inc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.pascal; path = ndpi_content_match.c.inc; sourceTree = "<group>"; };
+ E39540C120255353000BBA0D /* ndpi_main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ndpi_main.c; sourceTree = "<group>"; };
+ E395414B20255353000BBA0D /* afp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = afp.c; sourceTree = "<group>"; };
+ E395414C20255353000BBA0D /* aimini.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = aimini.c; sourceTree = "<group>"; };
+ E395414D20255353000BBA0D /* amqp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = amqp.c; sourceTree = "<group>"; };
+ E395414E20255353000BBA0D /* apple_push.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = apple_push.c; sourceTree = "<group>"; };
+ E395414F20255353000BBA0D /* applejuice.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = applejuice.c; sourceTree = "<group>"; };
+ E395415020255353000BBA0D /* armagetron.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = armagetron.c; sourceTree = "<group>"; };
+ E395415220255353000BBA0D /* flash.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = flash.c; sourceTree = "<group>"; };
+ E395415320255353000BBA0D /* ftp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ftp.c; sourceTree = "<group>"; };
+ E395415420255353000BBA0D /* manolito.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = manolito.c; sourceTree = "<group>"; };
+ E395415520255353000BBA0D /* popo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = popo.c; sourceTree = "<group>"; };
+ E395415620255353000BBA0D /* secondlife.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = secondlife.c; sourceTree = "<group>"; };
+ E395415720255353000BBA0D /* ayiya.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ayiya.c; sourceTree = "<group>"; };
+ E395415820255353000BBA0D /* battlefield.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = battlefield.c; sourceTree = "<group>"; };
+ E395415920255353000BBA0D /* bgp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bgp.c; sourceTree = "<group>"; };
+ E395415A20255353000BBA0D /* bittorrent.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bittorrent.c; sourceTree = "<group>"; };
+ E395415B20255353000BBA0D /* bjnp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bjnp.c; sourceTree = "<group>"; };
+ E395415C20255353000BBA0D /* btlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = btlib.c; sourceTree = "<group>"; };
+ E395415D20255353000BBA0D /* btlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = btlib.h; sourceTree = "<group>"; };
+ E395415E20255353000BBA0D /* checkmk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = checkmk.c; sourceTree = "<group>"; };
+ E395415F20255353000BBA0D /* ciscovpn.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ciscovpn.c; sourceTree = "<group>"; };
+ E395416020255353000BBA0D /* citrix.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = citrix.c; sourceTree = "<group>"; };
+ E395416120255353000BBA0D /* coap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = coap.c; sourceTree = "<group>"; };
+ E395416220255353000BBA0D /* collectd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = collectd.c; sourceTree = "<group>"; };
+ E395416320255353000BBA0D /* corba.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = corba.c; sourceTree = "<group>"; };
+ E395416420255353000BBA0D /* crossfire.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = crossfire.c; sourceTree = "<group>"; };
+ E395416520255353000BBA0D /* csgo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = csgo.c; sourceTree = "<group>"; };
+ E395416620255353000BBA0D /* dcerpc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dcerpc.c; sourceTree = "<group>"; };
+ E395416720255353000BBA0D /* dhcp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dhcp.c; sourceTree = "<group>"; };
+ E395416820255353000BBA0D /* dhcpv6.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dhcpv6.c; sourceTree = "<group>"; };
+ E395416920255353000BBA0D /* diameter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = diameter.c; sourceTree = "<group>"; };
+ E395416A20255353000BBA0D /* directconnect.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = directconnect.c; sourceTree = "<group>"; };
+ E395416B20255353000BBA0D /* directdownloadlink.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = directdownloadlink.c; sourceTree = "<group>"; };
+ E395416C20255353000BBA0D /* dns.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dns.c; sourceTree = "<group>"; };
+ E395416D20255353000BBA0D /* dofus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dofus.c; sourceTree = "<group>"; };
+ E395416E20255353000BBA0D /* drda.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = drda.c; sourceTree = "<group>"; };
+ E395416F20255353000BBA0D /* dropbox.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dropbox.c; sourceTree = "<group>"; };
+ E395417020255353000BBA0D /* eaq.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = eaq.c; sourceTree = "<group>"; };
+ E395417120255353000BBA0D /* edonkey.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = edonkey.c; sourceTree = "<group>"; };
+ E395417220255353000BBA0D /* fasttrack.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fasttrack.c; sourceTree = "<group>"; };
+ E395417320255353000BBA0D /* fiesta.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fiesta.c; sourceTree = "<group>"; };
+ E395417420255353000BBA0D /* filetopia.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = filetopia.c; sourceTree = "<group>"; };
+ E395417520255353000BBA0D /* fix.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fix.c; sourceTree = "<group>"; };
+ E395417620255353000BBA0D /* florensia.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = florensia.c; sourceTree = "<group>"; };
+ E395417720255353000BBA0D /* ftp_control.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ftp_control.c; sourceTree = "<group>"; };
+ E395417820255353000BBA0D /* ftp_data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ftp_data.c; sourceTree = "<group>"; };
+ E395417920255353000BBA0D /* git.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = git.c; sourceTree = "<group>"; };
+ E395417A20255353000BBA0D /* gnutella.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gnutella.c; sourceTree = "<group>"; };
+ E395417B20255353000BBA0D /* gtp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gtp.c; sourceTree = "<group>"; };
+ E395417C20255353000BBA0D /* guildwars.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = guildwars.c; sourceTree = "<group>"; };
+ E395417D20255353000BBA0D /* h323.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = h323.c; sourceTree = "<group>"; };
+ E395417E20255353000BBA0D /* halflife2_and_mods.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = halflife2_and_mods.c; sourceTree = "<group>"; };
+ E395417F20255353000BBA0D /* hangout.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hangout.c; sourceTree = "<group>"; };
+ E395418020255353000BBA0D /* hep.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hep.c; sourceTree = "<group>"; };
+ E395418120255353000BBA0D /* http.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = http.c; sourceTree = "<group>"; };
+ E395418220255353000BBA0D /* http_activesync.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = http_activesync.c; sourceTree = "<group>"; };
+ E395418320255353000BBA0D /* iax.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = iax.c; sourceTree = "<group>"; };
+ E395418420255353000BBA0D /* icecast.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = icecast.c; sourceTree = "<group>"; };
+ E395418520255353000BBA0D /* ipp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ipp.c; sourceTree = "<group>"; };
+ E395418620255353000BBA0D /* irc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = irc.c; sourceTree = "<group>"; };
+ E395418720255353000BBA0D /* jabber.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = jabber.c; sourceTree = "<group>"; };
+ E395418820255353000BBA0D /* kakaotalk_voice.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = kakaotalk_voice.c; sourceTree = "<group>"; };
+ E395418920255353000BBA0D /* kerberos.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = kerberos.c; sourceTree = "<group>"; };
+ E395418A20255353000BBA0D /* kontiki.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = kontiki.c; sourceTree = "<group>"; };
+ E395418B20255353000BBA0D /* ldap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ldap.c; sourceTree = "<group>"; };
+ E395429420255354000BBA0D /* lisp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lisp.c; sourceTree = "<group>"; };
+ E395429520255354000BBA0D /* lotus_notes.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lotus_notes.c; sourceTree = "<group>"; };
+ E395429620255354000BBA0D /* mail_imap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mail_imap.c; sourceTree = "<group>"; };
+ E395429720255354000BBA0D /* mail_pop.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mail_pop.c; sourceTree = "<group>"; };
+ E395429820255354000BBA0D /* mail_smtp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mail_smtp.c; sourceTree = "<group>"; };
+ E395429920255354000BBA0D /* maplestory.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = maplestory.c; sourceTree = "<group>"; };
+ E395429A20255354000BBA0D /* mdns.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mdns.c; sourceTree = "<group>"; };
+ E395429B20255354000BBA0D /* megaco.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = megaco.c; sourceTree = "<group>"; };
+ E395429C20255354000BBA0D /* mgcp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mgcp.c; sourceTree = "<group>"; };
+ E395429D20255354000BBA0D /* mms.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mms.c; sourceTree = "<group>"; };
+ E395429E20255354000BBA0D /* mpegts.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mpegts.c; sourceTree = "<group>"; };
+ E395429F20255354000BBA0D /* mqtt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mqtt.c; sourceTree = "<group>"; };
+ E39542A020255354000BBA0D /* msn.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = msn.c; sourceTree = "<group>"; };
+ E39542A120255354000BBA0D /* mssql_tds.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mssql_tds.c; sourceTree = "<group>"; };
+ E39542A220255354000BBA0D /* mysql.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mysql.c; sourceTree = "<group>"; };
+ E39542A320255354000BBA0D /* netbios.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = netbios.c; sourceTree = "<group>"; };
+ E39542A420255354000BBA0D /* netflow.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = netflow.c; sourceTree = "<group>"; };
+ E39542A520255354000BBA0D /* nfs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nfs.c; sourceTree = "<group>"; };
+ E39542A620255354000BBA0D /* nintendo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nintendo.c; sourceTree = "<group>"; };
+ E39542A720255354000BBA0D /* noe.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = noe.c; sourceTree = "<group>"; };
+ E39542A820255354000BBA0D /* non_tcp_udp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = non_tcp_udp.c; sourceTree = "<group>"; };
+ E39542A920255354000BBA0D /* ntp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ntp.c; sourceTree = "<group>"; };
+ E39542AA20255354000BBA0D /* openft.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = openft.c; sourceTree = "<group>"; };
+ E39542AB20255354000BBA0D /* openvpn.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = openvpn.c; sourceTree = "<group>"; };
+ E39542AC20255354000BBA0D /* oracle.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = oracle.c; sourceTree = "<group>"; };
+ E39542AD20255354000BBA0D /* oscar.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = oscar.c; sourceTree = "<group>"; };
+ E39542AE20255354000BBA0D /* pando.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pando.c; sourceTree = "<group>"; };
+ E39542AF20255354000BBA0D /* pcanywhere.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcanywhere.c; sourceTree = "<group>"; };
+ E39542B020255354000BBA0D /* postgres.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = postgres.c; sourceTree = "<group>"; };
+ E39542B120255354000BBA0D /* pplive.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pplive.c; sourceTree = "<group>"; };
+ E39542B220255354000BBA0D /* ppstream.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ppstream.c; sourceTree = "<group>"; };
+ E39542B320255354000BBA0D /* pptp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pptp.c; sourceTree = "<group>"; };
+ E39542B420255354000BBA0D /* qq.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = qq.c; sourceTree = "<group>"; };
+ E39542B520255354000BBA0D /* quic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = quic.c; sourceTree = "<group>"; };
+ E39542B620255354000BBA0D /* radius.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = radius.c; sourceTree = "<group>"; };
+ E39542B720255354000BBA0D /* rdp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rdp.c; sourceTree = "<group>"; };
+ E39542B820255354000BBA0D /* redis_net.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = redis_net.c; sourceTree = "<group>"; };
+ E39542B920255354000BBA0D /* rsync.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rsync.c; sourceTree = "<group>"; };
+ E39542BA20255354000BBA0D /* rtcp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rtcp.c; sourceTree = "<group>"; };
+ E39542BB20255354000BBA0D /* rtmp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rtmp.c; sourceTree = "<group>"; };
+ E39542BC20255354000BBA0D /* rtp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rtp.c; sourceTree = "<group>"; };
+ E39542BD20255354000BBA0D /* rtsp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rtsp.c; sourceTree = "<group>"; };
+ E39542BE20255354000BBA0D /* rx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rx.c; sourceTree = "<group>"; };
+ E39542BF20255354000BBA0D /* sflow.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sflow.c; sourceTree = "<group>"; };
+ E39542C020255354000BBA0D /* shoutcast.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = shoutcast.c; sourceTree = "<group>"; };
+ E39542C120255354000BBA0D /* sip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sip.c; sourceTree = "<group>"; };
+ E39542C220255354000BBA0D /* skinny.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = skinny.c; sourceTree = "<group>"; };
+ E39542C320255354000BBA0D /* skype.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = skype.c; sourceTree = "<group>"; };
+ E39542C420255354000BBA0D /* smb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = smb.c; sourceTree = "<group>"; };
+ E39542C520255354000BBA0D /* smpp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = smpp.c; sourceTree = "<group>"; };
+ E39542C620255354000BBA0D /* snmp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = snmp.c; sourceTree = "<group>"; };
+ E39542C720255354000BBA0D /* socks45.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = socks45.c; sourceTree = "<group>"; };
+ E39542C820255354000BBA0D /* socrates.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = socrates.c; sourceTree = "<group>"; };
+ E39542C920255354000BBA0D /* someip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = someip.c; sourceTree = "<group>"; };
+ E39542CA20255354000BBA0D /* sopcast.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sopcast.c; sourceTree = "<group>"; };
+ E39542CB20255354000BBA0D /* soulseek.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = soulseek.c; sourceTree = "<group>"; };
+ E39542CC20255354000BBA0D /* spotify.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = spotify.c; sourceTree = "<group>"; };
+ E39542CD20255354000BBA0D /* ssdp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ssdp.c; sourceTree = "<group>"; };
+ E39542CE20255354000BBA0D /* ssh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ssh.c; sourceTree = "<group>"; };
+ E39542CF20255354000BBA0D /* ssl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ssl.c; sourceTree = "<group>"; };
+ E39542D020255354000BBA0D /* starcraft.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = starcraft.c; sourceTree = "<group>"; };
+ E39542D120255354000BBA0D /* stealthnet.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = stealthnet.c; sourceTree = "<group>"; };
+ E39542D220255354000BBA0D /* steam.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = steam.c; sourceTree = "<group>"; };
+ E39542D320255354000BBA0D /* stun.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = stun.c; sourceTree = "<group>"; };
+ E39542D420255354000BBA0D /* syslog.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = syslog.c; sourceTree = "<group>"; };
+ E39542D520255354000BBA0D /* tcp_udp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tcp_udp.c; sourceTree = "<group>"; };
+ E39542D620255354000BBA0D /* teamspeak.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = teamspeak.c; sourceTree = "<group>"; };
+ E39542D720255354000BBA0D /* teamviewer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = teamviewer.c; sourceTree = "<group>"; };
+ E39542D820255354000BBA0D /* telegram.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = telegram.c; sourceTree = "<group>"; };
+ E39542D920255354000BBA0D /* telnet.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = telnet.c; sourceTree = "<group>"; };
+ E39542DA20255354000BBA0D /* teredo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = teredo.c; sourceTree = "<group>"; };
+ E39542DB20255354000BBA0D /* tftp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tftp.c; sourceTree = "<group>"; };
+ E39542DC20255354000BBA0D /* thunder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = thunder.c; sourceTree = "<group>"; };
+ E39542DD20255354000BBA0D /* tinc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tinc.c; sourceTree = "<group>"; };
+ E39542DE20255354000BBA0D /* tor.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tor.c; sourceTree = "<group>"; };
+ E39542DF20255354000BBA0D /* tvants.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tvants.c; sourceTree = "<group>"; };
+ E39542E020255354000BBA0D /* tvuplayer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tvuplayer.c; sourceTree = "<group>"; };
+ E39542E120255354000BBA0D /* ubntac2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ubntac2.c; sourceTree = "<group>"; };
+ E39542E220255354000BBA0D /* usenet.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = usenet.c; sourceTree = "<group>"; };
+ E39542E320255354000BBA0D /* vhua.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vhua.c; sourceTree = "<group>"; };
+ E39542E420255354000BBA0D /* viber.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = viber.c; sourceTree = "<group>"; };
+ E39542E520255354000BBA0D /* vmware.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vmware.c; sourceTree = "<group>"; };
+ E39542E620255354000BBA0D /* vnc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vnc.c; sourceTree = "<group>"; };
+ E39542E720255354000BBA0D /* warcraft3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = warcraft3.c; sourceTree = "<group>"; };
+ E39542E820255354000BBA0D /* whoisdas.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = whoisdas.c; sourceTree = "<group>"; };
+ E39542E920255354000BBA0D /* world_of_kung_fu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = world_of_kung_fu.c; sourceTree = "<group>"; };
+ E39542EA20255354000BBA0D /* world_of_warcraft.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = world_of_warcraft.c; sourceTree = "<group>"; };
+ E39542EB20255354000BBA0D /* xbox.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xbox.c; sourceTree = "<group>"; };
+ E39542EC20255354000BBA0D /* xdmcp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xdmcp.c; sourceTree = "<group>"; };
+ E39542ED20255354000BBA0D /* yahoo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = yahoo.c; sourceTree = "<group>"; };
+ E39542EE20255354000BBA0D /* zattoo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = zattoo.c; sourceTree = "<group>"; };
+ E39542EF20255354000BBA0D /* zeromq.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = zeromq.c; sourceTree = "<group>"; };
+ E39542F220255354000BBA0D /* actypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = actypes.h; sourceTree = "<group>"; };
+ E39542F320255354000BBA0D /* ahocorasick.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ahocorasick.h; sourceTree = "<group>"; };
+ E39542F420255354000BBA0D /* libcache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libcache.h; sourceTree = "<group>"; };
+ E39542F520255354000BBA0D /* ndpi_patricia.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ndpi_patricia.h; sourceTree = "<group>"; };
+ E39542F620255354000BBA0D /* node.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = node.h; sourceTree = "<group>"; };
+ E39542F720255354000BBA0D /* sort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sort.h; sourceTree = "<group>"; };
+ E395430020255354000BBA0D /* ahocorasick.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ahocorasick.c; sourceTree = "<group>"; };
+ E395430120255354000BBA0D /* libcache.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = libcache.c; sourceTree = "<group>"; };
+ E395430820255354000BBA0D /* ndpi_patricia.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ndpi_patricia.c; sourceTree = "<group>"; };
+ E395430920255354000BBA0D /* node.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = node.c; sourceTree = "<group>"; };
+ E395430A20255354000BBA0D /* sort.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sort.c; sourceTree = "<group>"; };
+ E395455620255734000BBA0D /* ndpi_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ndpi_config.h; sourceTree = "<group>"; };
+ E395455720255734000BBA0D /* ndpi_define.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ndpi_define.h; sourceTree = "<group>"; };
+ E3954558202558E5000BBA0D /* uthash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = uthash.h; path = ../../../uthash.h; sourceTree = "<group>"; };
+ E3954559202558E5000BBA0D /* protos.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = protos.txt; path = ../../../protos.txt; sourceTree = "<group>"; };
+ E395455A202558E5000BBA0D /* ndpi_util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ndpi_util.c; path = ../../../ndpi_util.c; sourceTree = "<group>"; };
+ E395455B202558E5000BBA0D /* ndpi_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ndpi_util.h; path = ../../../ndpi_util.h; sourceTree = "<group>"; };
+ E395455C202558E5000BBA0D /* ndpiReader.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ndpiReader.c; path = ../../../ndpiReader.c; sourceTree = "<group>"; };
+ E395478E20269F43000BBA0D /* libpcap.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libpcap.tbd; path = usr/lib/libpcap.tbd; sourceTree = SDKROOT; };
+ E39547922026AB75000BBA0D /* ndpi_utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ndpi_utils.h; sourceTree = "<group>"; };
+ E39547932026B2A9000BBA0D /* capture.pcap */ = {isa = PBXFileReference; lastKnownFileType = file; path = capture.pcap; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ E3953F4C20254989000BBA0D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ E395478F20269F43000BBA0D /* libpcap.tbd in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ E3953F4620254989000BBA0D = {
+ isa = PBXGroup;
+ children = (
+ E3953F5120254989000BBA0D /* ndpiExample */,
+ E3953F5020254989000BBA0D /* Products */,
+ E395478D20269F43000BBA0D /* Frameworks */,
+ );
+ sourceTree = "<group>";
+ };
+ E3953F5020254989000BBA0D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ E3953F4F20254989000BBA0D /* ndpiExample.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ E3953F5120254989000BBA0D /* ndpiExample */ = {
+ isa = PBXGroup;
+ children = (
+ E3953F5220254989000BBA0D /* AppDelegate.h */,
+ E3953F5320254989000BBA0D /* AppDelegate.m */,
+ E3953F5520254989000BBA0D /* ViewController.h */,
+ E3953F5620254989000BBA0D /* ViewController.m */,
+ E395455A202558E5000BBA0D /* ndpi_util.c */,
+ E395455B202558E5000BBA0D /* ndpi_util.h */,
+ E39547922026AB75000BBA0D /* ndpi_utils.h */,
+ E395455C202558E5000BBA0D /* ndpiReader.c */,
+ E3954559202558E5000BBA0D /* protos.txt */,
+ E39547932026B2A9000BBA0D /* capture.pcap */,
+ E3954558202558E5000BBA0D /* uthash.h */,
+ E39540A320255353000BBA0D /* src */,
+ E3953F5820254989000BBA0D /* Assets.xcassets */,
+ E3953F5A2025498A000BBA0D /* Main.storyboard */,
+ E3953F5D2025498A000BBA0D /* Info.plist */,
+ E3953F5E2025498A000BBA0D /* main.m */,
+ E3953F602025498A000BBA0D /* ndpiExample.entitlements */,
+ );
+ path = ndpiExample;
+ sourceTree = "<group>";
+ };
+ E39540A320255353000BBA0D /* src */ = {
+ isa = PBXGroup;
+ children = (
+ E39540A420255353000BBA0D /* include */,
+ E39540AF20255353000BBA0D /* lib */,
+ );
+ name = src;
+ path = ../../../../src;
+ sourceTree = "<group>";
+ };
+ E39540A420255353000BBA0D /* include */ = {
+ isa = PBXGroup;
+ children = (
+ E395455620255734000BBA0D /* ndpi_config.h */,
+ E395455720255734000BBA0D /* ndpi_define.h */,
+ E39540A520255353000BBA0D /* Makefile.am */,
+ E39540A620255353000BBA0D /* ndpi_api.h */,
+ E39540A720255353000BBA0D /* ndpi_define.h.in */,
+ E39540A820255353000BBA0D /* ndpi_includes.h */,
+ E39540A920255353000BBA0D /* ndpi_main.h */,
+ E39540AA20255353000BBA0D /* ndpi_protocol_ids.h */,
+ E39540AB20255353000BBA0D /* ndpi_protocols.h */,
+ E39540AC20255353000BBA0D /* ndpi_typedefs.h */,
+ E39540AD20255353000BBA0D /* ndpi_unix.h */,
+ E39540AE20255353000BBA0D /* ndpi_win32.h */,
+ );
+ path = include;
+ sourceTree = "<group>";
+ };
+ E39540AF20255353000BBA0D /* lib */ = {
+ isa = PBXGroup;
+ children = (
+ E39540BC20255353000BBA0D /* Makefile */,
+ E39540BD20255353000BBA0D /* Makefile.am */,
+ E39540BE20255353000BBA0D /* Makefile.in */,
+ E39540BF20255353000BBA0D /* Makefile.simple */,
+ E39540C020255353000BBA0D /* ndpi_content_match.c.inc */,
+ E39540C120255353000BBA0D /* ndpi_main.c */,
+ E39540C220255353000BBA0D /* protocols */,
+ E39542F020255354000BBA0D /* third_party */,
+ );
+ path = lib;
+ sourceTree = "<group>";
+ };
+ E39540C220255353000BBA0D /* protocols */ = {
+ isa = PBXGroup;
+ children = (
+ E395414B20255353000BBA0D /* afp.c */,
+ E395414C20255353000BBA0D /* aimini.c */,
+ E395414D20255353000BBA0D /* amqp.c */,
+ E395414E20255353000BBA0D /* apple_push.c */,
+ E395414F20255353000BBA0D /* applejuice.c */,
+ E395415020255353000BBA0D /* armagetron.c */,
+ E395415120255353000BBA0D /* attic */,
+ E395415720255353000BBA0D /* ayiya.c */,
+ E395415820255353000BBA0D /* battlefield.c */,
+ E395415920255353000BBA0D /* bgp.c */,
+ E395415A20255353000BBA0D /* bittorrent.c */,
+ E395415B20255353000BBA0D /* bjnp.c */,
+ E395415C20255353000BBA0D /* btlib.c */,
+ E395415D20255353000BBA0D /* btlib.h */,
+ E395415E20255353000BBA0D /* checkmk.c */,
+ E395415F20255353000BBA0D /* ciscovpn.c */,
+ E395416020255353000BBA0D /* citrix.c */,
+ E395416120255353000BBA0D /* coap.c */,
+ E395416220255353000BBA0D /* collectd.c */,
+ E395416320255353000BBA0D /* corba.c */,
+ E395416420255353000BBA0D /* crossfire.c */,
+ E395416520255353000BBA0D /* csgo.c */,
+ E395416620255353000BBA0D /* dcerpc.c */,
+ E395416720255353000BBA0D /* dhcp.c */,
+ E395416820255353000BBA0D /* dhcpv6.c */,
+ E395416920255353000BBA0D /* diameter.c */,
+ E395416A20255353000BBA0D /* directconnect.c */,
+ E395416B20255353000BBA0D /* directdownloadlink.c */,
+ E395416C20255353000BBA0D /* dns.c */,
+ E395416D20255353000BBA0D /* dofus.c */,
+ E395416E20255353000BBA0D /* drda.c */,
+ E395416F20255353000BBA0D /* dropbox.c */,
+ E395417020255353000BBA0D /* eaq.c */,
+ E395417120255353000BBA0D /* edonkey.c */,
+ E395417220255353000BBA0D /* fasttrack.c */,
+ E395417320255353000BBA0D /* fiesta.c */,
+ E395417420255353000BBA0D /* filetopia.c */,
+ E395417520255353000BBA0D /* fix.c */,
+ E395417620255353000BBA0D /* florensia.c */,
+ E395417720255353000BBA0D /* ftp_control.c */,
+ E395417820255353000BBA0D /* ftp_data.c */,
+ E395417920255353000BBA0D /* git.c */,
+ E395417A20255353000BBA0D /* gnutella.c */,
+ E395417B20255353000BBA0D /* gtp.c */,
+ E395417C20255353000BBA0D /* guildwars.c */,
+ E395417D20255353000BBA0D /* h323.c */,
+ E395417E20255353000BBA0D /* halflife2_and_mods.c */,
+ E395417F20255353000BBA0D /* hangout.c */,
+ E395418020255353000BBA0D /* hep.c */,
+ E395418120255353000BBA0D /* http.c */,
+ E395418220255353000BBA0D /* http_activesync.c */,
+ E395418320255353000BBA0D /* iax.c */,
+ E395418420255353000BBA0D /* icecast.c */,
+ E395418520255353000BBA0D /* ipp.c */,
+ E395418620255353000BBA0D /* irc.c */,
+ E395418720255353000BBA0D /* jabber.c */,
+ E395418820255353000BBA0D /* kakaotalk_voice.c */,
+ E395418920255353000BBA0D /* kerberos.c */,
+ E395418A20255353000BBA0D /* kontiki.c */,
+ E395418B20255353000BBA0D /* ldap.c */,
+ E395429420255354000BBA0D /* lisp.c */,
+ E395429520255354000BBA0D /* lotus_notes.c */,
+ E395429620255354000BBA0D /* mail_imap.c */,
+ E395429720255354000BBA0D /* mail_pop.c */,
+ E395429820255354000BBA0D /* mail_smtp.c */,
+ E395429920255354000BBA0D /* maplestory.c */,
+ E395429A20255354000BBA0D /* mdns.c */,
+ E395429B20255354000BBA0D /* megaco.c */,
+ E395429C20255354000BBA0D /* mgcp.c */,
+ E395429D20255354000BBA0D /* mms.c */,
+ E395429E20255354000BBA0D /* mpegts.c */,
+ E395429F20255354000BBA0D /* mqtt.c */,
+ E39542A020255354000BBA0D /* msn.c */,
+ E39542A120255354000BBA0D /* mssql_tds.c */,
+ E39542A220255354000BBA0D /* mysql.c */,
+ E39542A320255354000BBA0D /* netbios.c */,
+ E39542A420255354000BBA0D /* netflow.c */,
+ E39542A520255354000BBA0D /* nfs.c */,
+ E39542A620255354000BBA0D /* nintendo.c */,
+ E39542A720255354000BBA0D /* noe.c */,
+ E39542A820255354000BBA0D /* non_tcp_udp.c */,
+ E39542A920255354000BBA0D /* ntp.c */,
+ E39542AA20255354000BBA0D /* openft.c */,
+ E39542AB20255354000BBA0D /* openvpn.c */,
+ E39542AC20255354000BBA0D /* oracle.c */,
+ E39542AD20255354000BBA0D /* oscar.c */,
+ E39542AE20255354000BBA0D /* pando.c */,
+ E39542AF20255354000BBA0D /* pcanywhere.c */,
+ E39542B020255354000BBA0D /* postgres.c */,
+ E39542B120255354000BBA0D /* pplive.c */,
+ E39542B220255354000BBA0D /* ppstream.c */,
+ E39542B320255354000BBA0D /* pptp.c */,
+ E39542B420255354000BBA0D /* qq.c */,
+ E39542B520255354000BBA0D /* quic.c */,
+ E39542B620255354000BBA0D /* radius.c */,
+ E39542B720255354000BBA0D /* rdp.c */,
+ E39542B820255354000BBA0D /* redis_net.c */,
+ E39542B920255354000BBA0D /* rsync.c */,
+ E39542BA20255354000BBA0D /* rtcp.c */,
+ E39542BB20255354000BBA0D /* rtmp.c */,
+ E39542BC20255354000BBA0D /* rtp.c */,
+ E39542BD20255354000BBA0D /* rtsp.c */,
+ E39542BE20255354000BBA0D /* rx.c */,
+ E39542BF20255354000BBA0D /* sflow.c */,
+ E39542C020255354000BBA0D /* shoutcast.c */,
+ E39542C120255354000BBA0D /* sip.c */,
+ E39542C220255354000BBA0D /* skinny.c */,
+ E39542C320255354000BBA0D /* skype.c */,
+ E39542C420255354000BBA0D /* smb.c */,
+ E39542C520255354000BBA0D /* smpp.c */,
+ E39542C620255354000BBA0D /* snmp.c */,
+ E39542C720255354000BBA0D /* socks45.c */,
+ E39542C820255354000BBA0D /* socrates.c */,
+ E39542C920255354000BBA0D /* someip.c */,
+ E39542CA20255354000BBA0D /* sopcast.c */,
+ E39542CB20255354000BBA0D /* soulseek.c */,
+ E39542CC20255354000BBA0D /* spotify.c */,
+ E39542CD20255354000BBA0D /* ssdp.c */,
+ E39542CE20255354000BBA0D /* ssh.c */,
+ E39542CF20255354000BBA0D /* ssl.c */,
+ E39542D020255354000BBA0D /* starcraft.c */,
+ E39542D120255354000BBA0D /* stealthnet.c */,
+ E39542D220255354000BBA0D /* steam.c */,
+ E39542D320255354000BBA0D /* stun.c */,
+ E39542D420255354000BBA0D /* syslog.c */,
+ E39542D520255354000BBA0D /* tcp_udp.c */,
+ E39542D620255354000BBA0D /* teamspeak.c */,
+ E39542D720255354000BBA0D /* teamviewer.c */,
+ E39542D820255354000BBA0D /* telegram.c */,
+ E39542D920255354000BBA0D /* telnet.c */,
+ E39542DA20255354000BBA0D /* teredo.c */,
+ E39542DB20255354000BBA0D /* tftp.c */,
+ E39542DC20255354000BBA0D /* thunder.c */,
+ E39542DD20255354000BBA0D /* tinc.c */,
+ E39542DE20255354000BBA0D /* tor.c */,
+ E39542DF20255354000BBA0D /* tvants.c */,
+ E39542E020255354000BBA0D /* tvuplayer.c */,
+ E39542E120255354000BBA0D /* ubntac2.c */,
+ E39542E220255354000BBA0D /* usenet.c */,
+ E39542E320255354000BBA0D /* vhua.c */,
+ E39542E420255354000BBA0D /* viber.c */,
+ E39542E520255354000BBA0D /* vmware.c */,
+ E39542E620255354000BBA0D /* vnc.c */,
+ E39542E720255354000BBA0D /* warcraft3.c */,
+ E39542E820255354000BBA0D /* whoisdas.c */,
+ E39542E920255354000BBA0D /* world_of_kung_fu.c */,
+ E39542EA20255354000BBA0D /* world_of_warcraft.c */,
+ E39542EB20255354000BBA0D /* xbox.c */,
+ E39542EC20255354000BBA0D /* xdmcp.c */,
+ E39542ED20255354000BBA0D /* yahoo.c */,
+ E39542EE20255354000BBA0D /* zattoo.c */,
+ E39542EF20255354000BBA0D /* zeromq.c */,
+ );
+ path = protocols;
+ sourceTree = "<group>";
+ };
+ E395415120255353000BBA0D /* attic */ = {
+ isa = PBXGroup;
+ children = (
+ E395415220255353000BBA0D /* flash.c */,
+ E395415320255353000BBA0D /* ftp.c */,
+ E395415420255353000BBA0D /* manolito.c */,
+ E395415520255353000BBA0D /* popo.c */,
+ E395415620255353000BBA0D /* secondlife.c */,
+ );
+ path = attic;
+ sourceTree = "<group>";
+ };
+ E39542F020255354000BBA0D /* third_party */ = {
+ isa = PBXGroup;
+ children = (
+ E39542F120255354000BBA0D /* include */,
+ E39542F820255354000BBA0D /* src */,
+ );
+ path = third_party;
+ sourceTree = "<group>";
+ };
+ E39542F120255354000BBA0D /* include */ = {
+ isa = PBXGroup;
+ children = (
+ E39542F220255354000BBA0D /* actypes.h */,
+ E39542F320255354000BBA0D /* ahocorasick.h */,
+ E39542F420255354000BBA0D /* libcache.h */,
+ E39542F520255354000BBA0D /* ndpi_patricia.h */,
+ E39542F620255354000BBA0D /* node.h */,
+ E39542F720255354000BBA0D /* sort.h */,
+ );
+ path = include;
+ sourceTree = "<group>";
+ };
+ E39542F820255354000BBA0D /* src */ = {
+ isa = PBXGroup;
+ children = (
+ E395430020255354000BBA0D /* ahocorasick.c */,
+ E395430120255354000BBA0D /* libcache.c */,
+ E395430820255354000BBA0D /* ndpi_patricia.c */,
+ E395430920255354000BBA0D /* node.c */,
+ E395430A20255354000BBA0D /* sort.c */,
+ );
+ path = src;
+ sourceTree = "<group>";
+ };
+ E395478D20269F43000BBA0D /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ E395478E20269F43000BBA0D /* libpcap.tbd */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ E3953F4E20254989000BBA0D /* ndpiExample */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = E3953F632025498A000BBA0D /* Build configuration list for PBXNativeTarget "ndpiExample" */;
+ buildPhases = (
+ E3953F4B20254989000BBA0D /* Sources */,
+ E3953F4C20254989000BBA0D /* Frameworks */,
+ E3953F4D20254989000BBA0D /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = ndpiExample;
+ productName = ndpiExample;
+ productReference = E3953F4F20254989000BBA0D /* ndpiExample.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ E3953F4720254989000BBA0D /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0920;
+ ORGANIZATIONNAME = ZengYingpei;
+ TargetAttributes = {
+ E3953F4E20254989000BBA0D = {
+ CreatedOnToolsVersion = 9.2;
+ ProvisioningStyle = Automatic;
+ SystemCapabilities = {
+ com.apple.Sandbox = {
+ enabled = 0;
+ };
+ };
+ };
+ };
+ };
+ buildConfigurationList = E3953F4A20254989000BBA0D /* Build configuration list for PBXProject "ndpiExample" */;
+ compatibilityVersion = "Xcode 8.0";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = E3953F4620254989000BBA0D;
+ productRefGroup = E3953F5020254989000BBA0D /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ E3953F4E20254989000BBA0D /* ndpiExample */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ E3953F4D20254989000BBA0D /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ E395430C20255354000BBA0D /* ndpi_define.h.in in Resources */,
+ E395431A20255354000BBA0D /* Makefile.simple in Resources */,
+ E395430B20255354000BBA0D /* Makefile.am in Resources */,
+ E395431920255354000BBA0D /* Makefile.in in Resources */,
+ E3953F5920254989000BBA0D /* Assets.xcassets in Resources */,
+ E39547942026B2AA000BBA0D /* capture.pcap in Resources */,
+ E395455D202558E6000BBA0D /* protos.txt in Resources */,
+ E3953F5C2025498A000BBA0D /* Main.storyboard in Resources */,
+ E395431820255354000BBA0D /* Makefile.am in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ E3953F4B20254989000BBA0D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ E39544F320255354000BBA0D /* mms.c in Sources */,
+ E39544F220255354000BBA0D /* mgcp.c in Sources */,
+ E39543B420255354000BBA0D /* checkmk.c in Sources */,
+ E39543D720255354000BBA0D /* http.c in Sources */,
+ E39543A420255354000BBA0D /* aimini.c in Sources */,
+ E39543DD20255354000BBA0D /* jabber.c in Sources */,
+ E395452820255354000BBA0D /* steam.c in Sources */,
+ E39543C720255354000BBA0D /* edonkey.c in Sources */,
+ E39543B520255354000BBA0D /* ciscovpn.c in Sources */,
+ E39543B120255354000BBA0D /* bittorrent.c in Sources */,
+ E395452E20255354000BBA0D /* telegram.c in Sources */,
+ E39543DA20255354000BBA0D /* icecast.c in Sources */,
+ E395450620255354000BBA0D /* postgres.c in Sources */,
+ E395451E20255354000BBA0D /* socrates.c in Sources */,
+ E395450B20255354000BBA0D /* quic.c in Sources */,
+ E39543A820255354000BBA0D /* armagetron.c in Sources */,
+ E39543D120255354000BBA0D /* gtp.c in Sources */,
+ E395452120255354000BBA0D /* soulseek.c in Sources */,
+ E39543B220255354000BBA0D /* bjnp.c in Sources */,
+ E39543C820255354000BBA0D /* fasttrack.c in Sources */,
+ E395452F20255354000BBA0D /* telnet.c in Sources */,
+ E395452620255354000BBA0D /* starcraft.c in Sources */,
+ E39543B620255354000BBA0D /* citrix.c in Sources */,
+ E395451320255354000BBA0D /* rtsp.c in Sources */,
+ E39543CA20255354000BBA0D /* filetopia.c in Sources */,
+ E39544F120255354000BBA0D /* megaco.c in Sources */,
+ E39543A920255354000BBA0D /* flash.c in Sources */,
+ E39544F820255354000BBA0D /* mysql.c in Sources */,
+ E395453020255354000BBA0D /* teredo.c in Sources */,
+ E39543B020255354000BBA0D /* bgp.c in Sources */,
+ E395454320255355000BBA0D /* yahoo.c in Sources */,
+ E395453A20255354000BBA0D /* viber.c in Sources */,
+ E39543A320255354000BBA0D /* afp.c in Sources */,
+ E39544FB20255354000BBA0D /* nfs.c in Sources */,
+ E39543D520255354000BBA0D /* hangout.c in Sources */,
+ E395452A20255354000BBA0D /* syslog.c in Sources */,
+ E39543C420255354000BBA0D /* drda.c in Sources */,
+ E39543CD20255354000BBA0D /* ftp_control.c in Sources */,
+ E395453C20255355000BBA0D /* vnc.c in Sources */,
+ E39543D820255354000BBA0D /* http_activesync.c in Sources */,
+ E39544FC20255354000BBA0D /* nintendo.c in Sources */,
+ E395450220255354000BBA0D /* oracle.c in Sources */,
+ E395451420255354000BBA0D /* rx.c in Sources */,
+ E39543DC20255354000BBA0D /* irc.c in Sources */,
+ E395450320255354000BBA0D /* oscar.c in Sources */,
+ E39543BC20255354000BBA0D /* dcerpc.c in Sources */,
+ E395454020255355000BBA0D /* world_of_warcraft.c in Sources */,
+ E39543A620255354000BBA0D /* apple_push.c in Sources */,
+ E395451920255354000BBA0D /* skype.c in Sources */,
+ E39543AC20255354000BBA0D /* popo.c in Sources */,
+ E39543BF20255354000BBA0D /* diameter.c in Sources */,
+ E39544FF20255354000BBA0D /* ntp.c in Sources */,
+ E395453220255354000BBA0D /* thunder.c in Sources */,
+ E39543C620255354000BBA0D /* eaq.c in Sources */,
+ E395454C20255355000BBA0D /* libcache.c in Sources */,
+ E39543E120255354000BBA0D /* ldap.c in Sources */,
+ E39543C120255354000BBA0D /* directdownloadlink.c in Sources */,
+ E39544EC20255354000BBA0D /* mail_imap.c in Sources */,
+ E395450C20255354000BBA0D /* radius.c in Sources */,
+ E395455F202558E6000BBA0D /* ndpiReader.c in Sources */,
+ E395451C20255354000BBA0D /* snmp.c in Sources */,
+ E395452520255354000BBA0D /* ssl.c in Sources */,
+ E39543D420255354000BBA0D /* halflife2_and_mods.c in Sources */,
+ E39544F920255354000BBA0D /* netbios.c in Sources */,
+ E395455520255355000BBA0D /* sort.c in Sources */,
+ E39543CB20255354000BBA0D /* fix.c in Sources */,
+ E395450420255354000BBA0D /* pando.c in Sources */,
+ E395453F20255355000BBA0D /* world_of_kung_fu.c in Sources */,
+ E39543A520255354000BBA0D /* amqp.c in Sources */,
+ E395453520255354000BBA0D /* tvants.c in Sources */,
+ E395450020255354000BBA0D /* openft.c in Sources */,
+ E39543B720255354000BBA0D /* coap.c in Sources */,
+ E39543DB20255354000BBA0D /* ipp.c in Sources */,
+ E39544ED20255354000BBA0D /* mail_pop.c in Sources */,
+ E395450F20255354000BBA0D /* rsync.c in Sources */,
+ E395452B20255354000BBA0D /* tcp_udp.c in Sources */,
+ E39544EE20255354000BBA0D /* mail_smtp.c in Sources */,
+ E395453820255354000BBA0D /* usenet.c in Sources */,
+ E39543BA20255354000BBA0D /* crossfire.c in Sources */,
+ E39544F420255354000BBA0D /* mpegts.c in Sources */,
+ E395450D20255354000BBA0D /* rdp.c in Sources */,
+ E39544EA20255354000BBA0D /* lisp.c in Sources */,
+ E39544EF20255354000BBA0D /* maplestory.c in Sources */,
+ E39544EB20255354000BBA0D /* lotus_notes.c in Sources */,
+ E395451F20255354000BBA0D /* someip.c in Sources */,
+ E39543DF20255354000BBA0D /* kerberos.c in Sources */,
+ E39543A720255354000BBA0D /* applejuice.c in Sources */,
+ E395452020255354000BBA0D /* sopcast.c in Sources */,
+ E39543AD20255354000BBA0D /* secondlife.c in Sources */,
+ E395450720255354000BBA0D /* pplive.c in Sources */,
+ E395453120255354000BBA0D /* tftp.c in Sources */,
+ E39543AF20255354000BBA0D /* battlefield.c in Sources */,
+ E395451020255354000BBA0D /* rtcp.c in Sources */,
+ E39543D620255354000BBA0D /* hep.c in Sources */,
+ E39543E020255354000BBA0D /* kontiki.c in Sources */,
+ E39544FA20255354000BBA0D /* netflow.c in Sources */,
+ E395454220255355000BBA0D /* xdmcp.c in Sources */,
+ E39544F720255354000BBA0D /* mssql_tds.c in Sources */,
+ E395451A20255354000BBA0D /* smb.c in Sources */,
+ E39543B820255354000BBA0D /* collectd.c in Sources */,
+ E395450520255354000BBA0D /* pcanywhere.c in Sources */,
+ E39547902026A51A000BBA0D /* ahocorasick.c in Sources */,
+ E395452320255354000BBA0D /* ssdp.c in Sources */,
+ E395431B20255354000BBA0D /* ndpi_content_match.c.inc in Sources */,
+ E395450120255354000BBA0D /* openvpn.c in Sources */,
+ E395453920255354000BBA0D /* vhua.c in Sources */,
+ E39544F020255354000BBA0D /* mdns.c in Sources */,
+ E39543C920255354000BBA0D /* fiesta.c in Sources */,
+ E395454120255355000BBA0D /* xbox.c in Sources */,
+ E395453D20255355000BBA0D /* warcraft3.c in Sources */,
+ E39543D220255354000BBA0D /* guildwars.c in Sources */,
+ E39543AA20255354000BBA0D /* ftp.c in Sources */,
+ E395450E20255354000BBA0D /* redis_net.c in Sources */,
+ E395455420255355000BBA0D /* node.c in Sources */,
+ E39543CE20255354000BBA0D /* ftp_data.c in Sources */,
+ E395451D20255354000BBA0D /* socks45.c in Sources */,
+ E395451820255354000BBA0D /* skinny.c in Sources */,
+ E395453620255354000BBA0D /* tvuplayer.c in Sources */,
+ E39543CC20255354000BBA0D /* florensia.c in Sources */,
+ E3953F5720254989000BBA0D /* ViewController.m in Sources */,
+ E39544F520255354000BBA0D /* mqtt.c in Sources */,
+ E39543C220255354000BBA0D /* dns.c in Sources */,
+ E39543B920255354000BBA0D /* corba.c in Sources */,
+ E39543B320255354000BBA0D /* btlib.c in Sources */,
+ E3953F5F2025498A000BBA0D /* main.m in Sources */,
+ E39543CF20255354000BBA0D /* git.c in Sources */,
+ E395451B20255354000BBA0D /* smpp.c in Sources */,
+ E395452420255354000BBA0D /* ssh.c in Sources */,
+ E395451520255354000BBA0D /* sflow.c in Sources */,
+ E395452220255354000BBA0D /* spotify.c in Sources */,
+ E39544F620255354000BBA0D /* msn.c in Sources */,
+ E395451220255354000BBA0D /* rtp.c in Sources */,
+ E39543BE20255354000BBA0D /* dhcpv6.c in Sources */,
+ E395453B20255355000BBA0D /* vmware.c in Sources */,
+ E395452C20255354000BBA0D /* teamspeak.c in Sources */,
+ E395452D20255354000BBA0D /* teamviewer.c in Sources */,
+ E39543AB20255354000BBA0D /* manolito.c in Sources */,
+ E395453E20255355000BBA0D /* whoisdas.c in Sources */,
+ E39543BB20255354000BBA0D /* csgo.c in Sources */,
+ E395450A20255354000BBA0D /* qq.c in Sources */,
+ E395431C20255354000BBA0D /* ndpi_main.c in Sources */,
+ E39543BD20255354000BBA0D /* dhcp.c in Sources */,
+ E395454520255355000BBA0D /* zeromq.c in Sources */,
+ E395453420255354000BBA0D /* tor.c in Sources */,
+ E39543AE20255354000BBA0D /* ayiya.c in Sources */,
+ E395451120255354000BBA0D /* rtmp.c in Sources */,
+ E39544FE20255354000BBA0D /* non_tcp_udp.c in Sources */,
+ E39543C020255354000BBA0D /* directconnect.c in Sources */,
+ E39543D020255354000BBA0D /* gnutella.c in Sources */,
+ E39543D320255354000BBA0D /* h323.c in Sources */,
+ E395453720255354000BBA0D /* ubntac2.c in Sources */,
+ E395452720255354000BBA0D /* stealthnet.c in Sources */,
+ E395453320255354000BBA0D /* tinc.c in Sources */,
+ E39543D920255354000BBA0D /* iax.c in Sources */,
+ E395452920255354000BBA0D /* stun.c in Sources */,
+ E395450920255354000BBA0D /* pptp.c in Sources */,
+ E39543C320255354000BBA0D /* dofus.c in Sources */,
+ E395451720255354000BBA0D /* sip.c in Sources */,
+ E39543DE20255354000BBA0D /* kakaotalk_voice.c in Sources */,
+ E395431720255354000BBA0D /* Makefile in Sources */,
+ E395454420255355000BBA0D /* zattoo.c in Sources */,
+ E39544FD20255354000BBA0D /* noe.c in Sources */,
+ E395451620255354000BBA0D /* shoutcast.c in Sources */,
+ E395455E202558E6000BBA0D /* ndpi_util.c in Sources */,
+ E3953F5420254989000BBA0D /* AppDelegate.m in Sources */,
+ E39543C520255354000BBA0D /* dropbox.c in Sources */,
+ E395450820255354000BBA0D /* ppstream.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ E3953F5A2025498A000BBA0D /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ E3953F5B2025498A000BBA0D /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ E3953F612025498A000BBA0D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "Mac Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.12;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ };
+ name = Debug;
+ };
+ E3953F622025498A000BBA0D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "Mac Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.12;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ };
+ name = Release;
+ };
+ E3953F642025498A000BBA0D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ DEVELOPMENT_TEAM = 5NEA8474R4;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ "APP_HAS_OWN_MAIN=1",
+ "NDPI_LOG_DEBUG2=NDPI_LOG_DEBUG2_XCODE_PROJ",
+ );
+ INFOPLIST_FILE = ndpiExample/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = com.zyingp.ndpiExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ E3953F652025498A000BBA0D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ DEVELOPMENT_TEAM = 5NEA8474R4;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "APP_HAS_OWN_MAIN=1",
+ "NDPI_LOG_DEBUG2=NDPI_LOG_DEBUG2_XCODE_PROJ",
+ );
+ INFOPLIST_FILE = ndpiExample/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = com.zyingp.ndpiExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ E3953F4A20254989000BBA0D /* Build configuration list for PBXProject "ndpiExample" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ E3953F612025498A000BBA0D /* Debug */,
+ E3953F622025498A000BBA0D /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ E3953F632025498A000BBA0D /* Build configuration list for PBXNativeTarget "ndpiExample" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ E3953F642025498A000BBA0D /* Debug */,
+ E3953F652025498A000BBA0D /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = E3953F4720254989000BBA0D /* Project object */;
+}
diff --git a/example/MacOS/ndpiExample/ndpiExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example/MacOS/ndpiExample/ndpiExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 000000000..d2df2968d
--- /dev/null
+++ b/example/MacOS/ndpiExample/ndpiExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "self:ndpiExample.xcodeproj">
+ </FileRef>
+</Workspace>
diff --git a/example/MacOS/ndpiExample/ndpiExample/AppDelegate.h b/example/MacOS/ndpiExample/ndpiExample/AppDelegate.h
new file mode 100644
index 000000000..73b18fcdc
--- /dev/null
+++ b/example/MacOS/ndpiExample/ndpiExample/AppDelegate.h
@@ -0,0 +1,29 @@
+/*
+ *
+ * Copyright (C) 2011-18 - ntop.org
+ *
+ * This file is part of nDPI, an open source deep packet inspection
+ * library based on the OpenDPI and PACE technology by ipoque GmbH
+ *
+ * nDPI is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * nDPI is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with nDPI. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#import <Cocoa/Cocoa.h>
+
+@interface AppDelegate : NSObject <NSApplicationDelegate>
+
+
+@end
+
diff --git a/example/MacOS/ndpiExample/ndpiExample/AppDelegate.m b/example/MacOS/ndpiExample/ndpiExample/AppDelegate.m
new file mode 100644
index 000000000..93f794446
--- /dev/null
+++ b/example/MacOS/ndpiExample/ndpiExample/AppDelegate.m
@@ -0,0 +1,41 @@
+/*
+ *
+ * Copyright (C) 2011-18 - ntop.org
+ *
+ * This file is part of nDPI, an open source deep packet inspection
+ * library based on the OpenDPI and PACE technology by ipoque GmbH
+ *
+ * nDPI is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * nDPI is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with nDPI. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#import "AppDelegate.h"
+
+@interface AppDelegate ()
+
+@end
+
+@implementation AppDelegate
+
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
+ // Insert code here to initialize your application
+}
+
+
+- (void)applicationWillTerminate:(NSNotification *)aNotification {
+ // Insert code here to tear down your application
+}
+
+
+@end
diff --git a/example/MacOS/ndpiExample/ndpiExample/Assets.xcassets/AppIcon.appiconset/Contents.json b/example/MacOS/ndpiExample/ndpiExample/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 000000000..2db2b1c7c
--- /dev/null
+++ b/example/MacOS/ndpiExample/ndpiExample/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,58 @@
+{
+ "images" : [
+ {
+ "idiom" : "mac",
+ "size" : "16x16",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "16x16",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "32x32",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "32x32",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "128x128",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "128x128",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "256x256",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "256x256",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "512x512",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "512x512",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+} \ No newline at end of file
diff --git a/example/MacOS/ndpiExample/ndpiExample/Base.lproj/Main.storyboard b/example/MacOS/ndpiExample/ndpiExample/Base.lproj/Main.storyboard
new file mode 100644
index 000000000..69e02f235
--- /dev/null
+++ b/example/MacOS/ndpiExample/ndpiExample/Base.lproj/Main.storyboard
@@ -0,0 +1,732 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
+ <dependencies>
+ <deployment identifier="macosx"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13771"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+ </dependencies>
+ <scenes>
+ <!--Application-->
+ <scene sceneID="JPo-4y-FX3">
+ <objects>
+ <application id="hnw-xV-0zn" sceneMemberID="viewController">
+ <menu key="mainMenu" title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
+ <items>
+ <menuItem title="ndpiExample" id="1Xt-HY-uBw">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="ndpiExample" systemMenu="apple" id="uQy-DD-JDr">
+ <items>
+ <menuItem title="About ndpiExample" id="5kV-Vb-QxS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="orderFrontStandardAboutPanel:" target="Ady-hI-5gd" id="Exp-CZ-Vem"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
+ <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
+ <menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
+ <menuItem title="Services" id="NMo-om-nkz">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
+ <menuItem title="Hide ndpiExample" keyEquivalent="h" id="Olw-nP-bQN">
+ <connections>
+ <action selector="hide:" target="Ady-hI-5gd" id="PnN-Uc-m68"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="hideOtherApplications:" target="Ady-hI-5gd" id="VT4-aY-XCT"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Show All" id="Kd2-mp-pUS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="unhideAllApplications:" target="Ady-hI-5gd" id="Dhg-Le-xox"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
+ <menuItem title="Quit ndpiExample" keyEquivalent="q" id="4sb-4s-VLi">
+ <connections>
+ <action selector="terminate:" target="Ady-hI-5gd" id="Te7-pn-YzF"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="File" id="dMs-cI-mzQ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="File" id="bib-Uj-vzu">
+ <items>
+ <menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
+ <connections>
+ <action selector="newDocument:" target="Ady-hI-5gd" id="4Si-XN-c54"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
+ <connections>
+ <action selector="openDocument:" target="Ady-hI-5gd" id="bVn-NM-KNZ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Open Recent" id="tXI-mr-wws">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
+ <items>
+ <menuItem title="Clear Menu" id="vNY-rz-j42">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="clearRecentDocuments:" target="Ady-hI-5gd" id="Daa-9d-B3U"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
+ <menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
+ <connections>
+ <action selector="performClose:" target="Ady-hI-5gd" id="HmO-Ls-i7Q"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
+ <connections>
+ <action selector="saveDocument:" target="Ady-hI-5gd" id="teZ-XB-qJY"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
+ <connections>
+ <action selector="saveDocumentAs:" target="Ady-hI-5gd" id="mDf-zr-I0C"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Revert to Saved" keyEquivalent="r" id="KaW-ft-85H">
+ <connections>
+ <action selector="revertDocumentToSaved:" target="Ady-hI-5gd" id="iJ3-Pv-kwq"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
+ <menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
+ <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+ <connections>
+ <action selector="runPageLayout:" target="Ady-hI-5gd" id="Din-rz-gC5"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
+ <connections>
+ <action selector="print:" target="Ady-hI-5gd" id="qaZ-4w-aoO"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Edit" id="5QF-Oa-p0T">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Edit" id="W48-6f-4Dl">
+ <items>
+ <menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
+ <connections>
+ <action selector="undo:" target="Ady-hI-5gd" id="M6e-cu-g7V"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
+ <connections>
+ <action selector="redo:" target="Ady-hI-5gd" id="oIA-Rs-6OD"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
+ <menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
+ <connections>
+ <action selector="cut:" target="Ady-hI-5gd" id="YJe-68-I9s"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
+ <connections>
+ <action selector="copy:" target="Ady-hI-5gd" id="G1f-GL-Joy"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
+ <connections>
+ <action selector="paste:" target="Ady-hI-5gd" id="UvS-8e-Qdg"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="pasteAsPlainText:" target="Ady-hI-5gd" id="cEh-KX-wJQ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Delete" id="pa3-QI-u2k">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="delete:" target="Ady-hI-5gd" id="0Mk-Ml-PaM"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
+ <connections>
+ <action selector="selectAll:" target="Ady-hI-5gd" id="VNm-Mi-diN"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
+ <menuItem title="Find" id="4EN-yA-p0u">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Find" id="1b7-l0-nxx">
+ <items>
+ <menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
+ <connections>
+ <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="cD7-Qs-BN4"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="WD3-Gg-5AJ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
+ <connections>
+ <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="NDo-RZ-v9R"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
+ <connections>
+ <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="HOh-sY-3ay"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
+ <connections>
+ <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="U76-nv-p5D"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
+ <connections>
+ <action selector="centerSelectionInVisibleArea:" target="Ady-hI-5gd" id="IOG-6D-g5B"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
+ <items>
+ <menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
+ <connections>
+ <action selector="showGuessPanel:" target="Ady-hI-5gd" id="vFj-Ks-hy3"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
+ <connections>
+ <action selector="checkSpelling:" target="Ady-hI-5gd" id="fz7-VC-reM"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
+ <menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleContinuousSpellChecking:" target="Ady-hI-5gd" id="7w6-Qz-0kB"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleGrammarChecking:" target="Ady-hI-5gd" id="muD-Qn-j4w"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticSpellingCorrection:" target="Ady-hI-5gd" id="2lM-Qi-WAP"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Substitutions" id="9ic-FL-obx">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
+ <items>
+ <menuItem title="Show Substitutions" id="z6F-FW-3nz">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="orderFrontSubstitutionsPanel:" target="Ady-hI-5gd" id="oku-mr-iSq"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
+ <menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleSmartInsertDelete:" target="Ady-hI-5gd" id="3IJ-Se-DZD"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Quotes" id="hQb-2v-fYv">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticQuoteSubstitution:" target="Ady-hI-5gd" id="ptq-xd-QOA"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Dashes" id="rgM-f4-ycn">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticDashSubstitution:" target="Ady-hI-5gd" id="oCt-pO-9gS"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Links" id="cwL-P1-jid">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticLinkDetection:" target="Ady-hI-5gd" id="Gip-E3-Fov"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Data Detectors" id="tRr-pd-1PS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticDataDetection:" target="Ady-hI-5gd" id="R1I-Nq-Kbl"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Text Replacement" id="HFQ-gK-NFA">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticTextReplacement:" target="Ady-hI-5gd" id="DvP-Fe-Py6"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Transformations" id="2oI-Rn-ZJC">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Transformations" id="c8a-y6-VQd">
+ <items>
+ <menuItem title="Make Upper Case" id="vmV-6d-7jI">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="uppercaseWord:" target="Ady-hI-5gd" id="sPh-Tk-edu"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Make Lower Case" id="d9M-CD-aMd">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="lowercaseWord:" target="Ady-hI-5gd" id="iUZ-b5-hil"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Capitalize" id="UEZ-Bs-lqG">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="capitalizeWord:" target="Ady-hI-5gd" id="26H-TL-nsh"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Speech" id="xrE-MZ-jX0">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Speech" id="3rS-ZA-NoH">
+ <items>
+ <menuItem title="Start Speaking" id="Ynk-f8-cLZ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="startSpeaking:" target="Ady-hI-5gd" id="654-Ng-kyl"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Stop Speaking" id="Oyz-dy-DGm">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="stopSpeaking:" target="Ady-hI-5gd" id="dX8-6p-jy9"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Format" id="jxT-CU-nIS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Format" id="GEO-Iw-cKr">
+ <items>
+ <menuItem title="Font" id="Gi5-1S-RQB">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
+ <items>
+ <menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
+ <connections>
+ <action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
+ <connections>
+ <action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
+ <connections>
+ <action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
+ <connections>
+ <action selector="underline:" target="Ady-hI-5gd" id="FYS-2b-JAY"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
+ <menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
+ <connections>
+ <action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
+ <connections>
+ <action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
+ <menuItem title="Kern" id="jBQ-r6-VK2">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Kern" id="tlD-Oa-oAM">
+ <items>
+ <menuItem title="Use Default" id="GUa-eO-cwY">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="useStandardKerning:" target="Ady-hI-5gd" id="6dk-9l-Ckg"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Use None" id="cDB-IK-hbR">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="turnOffKerning:" target="Ady-hI-5gd" id="U8a-gz-Maa"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Tighten" id="46P-cB-AYj">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="tightenKerning:" target="Ady-hI-5gd" id="hr7-Nz-8ro"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Loosen" id="ogc-rX-tC1">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="loosenKerning:" target="Ady-hI-5gd" id="8i4-f9-FKE"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Ligatures" id="o6e-r0-MWq">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
+ <items>
+ <menuItem title="Use Default" id="agt-UL-0e3">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="useStandardLigatures:" target="Ady-hI-5gd" id="7uR-wd-Dx6"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Use None" id="J7y-lM-qPV">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="turnOffLigatures:" target="Ady-hI-5gd" id="iX2-gA-Ilz"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Use All" id="xQD-1f-W4t">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="useAllLigatures:" target="Ady-hI-5gd" id="KcB-kA-TuK"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Baseline" id="OaQ-X3-Vso">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Baseline" id="ijk-EB-dga">
+ <items>
+ <menuItem title="Use Default" id="3Om-Ey-2VK">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="unscript:" target="Ady-hI-5gd" id="0vZ-95-Ywn"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Superscript" id="Rqc-34-cIF">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="superscript:" target="Ady-hI-5gd" id="3qV-fo-wpU"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Subscript" id="I0S-gh-46l">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="subscript:" target="Ady-hI-5gd" id="Q6W-4W-IGz"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Raise" id="2h7-ER-AoG">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="raiseBaseline:" target="Ady-hI-5gd" id="4sk-31-7Q9"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Lower" id="1tx-W0-xDw">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="lowerBaseline:" target="Ady-hI-5gd" id="OF1-bc-KW4"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
+ <menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
+ <connections>
+ <action selector="orderFrontColorPanel:" target="Ady-hI-5gd" id="mSX-Xz-DV3"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
+ <menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="copyFont:" target="Ady-hI-5gd" id="GJO-xA-L4q"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="pasteFont:" target="Ady-hI-5gd" id="JfD-CL-leO"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Text" id="Fal-I4-PZk">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Text" id="d9c-me-L2H">
+ <items>
+ <menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
+ <connections>
+ <action selector="alignLeft:" target="Ady-hI-5gd" id="zUv-R1-uAa"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
+ <connections>
+ <action selector="alignCenter:" target="Ady-hI-5gd" id="spX-mk-kcS"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Justify" id="J5U-5w-g23">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="alignJustified:" target="Ady-hI-5gd" id="ljL-7U-jND"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
+ <connections>
+ <action selector="alignRight:" target="Ady-hI-5gd" id="r48-bG-YeY"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
+ <menuItem title="Writing Direction" id="H1b-Si-o9J">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
+ <items>
+ <menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ </menuItem>
+ <menuItem id="YGs-j5-SAR">
+ <string key="title"> Default</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeBaseWritingDirectionNatural:" target="Ady-hI-5gd" id="qtV-5e-UBP"/>
+ </connections>
+ </menuItem>
+ <menuItem id="Lbh-J2-qVU">
+ <string key="title"> Left to Right</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeBaseWritingDirectionLeftToRight:" target="Ady-hI-5gd" id="S0X-9S-QSf"/>
+ </connections>
+ </menuItem>
+ <menuItem id="jFq-tB-4Kx">
+ <string key="title"> Right to Left</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeBaseWritingDirectionRightToLeft:" target="Ady-hI-5gd" id="5fk-qB-AqJ"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
+ <menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ </menuItem>
+ <menuItem id="Nop-cj-93Q">
+ <string key="title"> Default</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeTextWritingDirectionNatural:" target="Ady-hI-5gd" id="lPI-Se-ZHp"/>
+ </connections>
+ </menuItem>
+ <menuItem id="BgM-ve-c93">
+ <string key="title"> Left to Right</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeTextWritingDirectionLeftToRight:" target="Ady-hI-5gd" id="caW-Bv-w94"/>
+ </connections>
+ </menuItem>
+ <menuItem id="RB4-Sm-HuC">
+ <string key="title"> Right to Left</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeTextWritingDirectionRightToLeft:" target="Ady-hI-5gd" id="EXD-6r-ZUu"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
+ <menuItem title="Show Ruler" id="vLm-3I-IUL">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleRuler:" target="Ady-hI-5gd" id="FOx-HJ-KwY"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
+ <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+ <connections>
+ <action selector="copyRuler:" target="Ady-hI-5gd" id="71i-fW-3W2"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
+ <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+ <connections>
+ <action selector="pasteRuler:" target="Ady-hI-5gd" id="cSh-wd-qM2"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="View" id="H8h-7b-M4v">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="View" id="HyV-fh-RgO">
+ <items>
+ <menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="toggleToolbarShown:" target="Ady-hI-5gd" id="BXY-wc-z0C"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="runToolbarCustomizationPalette:" target="Ady-hI-5gd" id="pQI-g3-MTW"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="hB3-LF-h0Y"/>
+ <menuItem title="Show Sidebar" keyEquivalent="s" id="kIP-vf-haE">
+ <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+ <connections>
+ <action selector="toggleSourceList:" target="Ady-hI-5gd" id="iwa-gc-5KM"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
+ <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+ <connections>
+ <action selector="toggleFullScreen:" target="Ady-hI-5gd" id="dU3-MA-1Rq"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Window" id="aUF-d1-5bR">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
+ <items>
+ <menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
+ <connections>
+ <action selector="performMiniaturize:" target="Ady-hI-5gd" id="VwT-WD-YPe"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Zoom" id="R4o-n2-Eq4">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="performZoom:" target="Ady-hI-5gd" id="DIl-cC-cCs"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
+ <menuItem title="Bring All to Front" id="LE2-aR-0XJ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="arrangeInFront:" target="Ady-hI-5gd" id="DRN-fu-gQh"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Help" id="wpr-3q-Mcd">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
+ <items>
+ <menuItem title="ndpiExample Help" keyEquivalent="?" id="FKE-Sm-Kum">
+ <connections>
+ <action selector="showHelp:" target="Ady-hI-5gd" id="y7X-2Q-9no"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ </menu>
+ <connections>
+ <outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/>
+ </connections>
+ </application>
+ <customObject id="Voe-Tx-rLC" customClass="AppDelegate"/>
+ <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
+ <customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="75" y="0.0"/>
+ </scene>
+ <!--Window Controller-->
+ <scene sceneID="R2V-B0-nI4">
+ <objects>
+ <windowController id="B8D-0N-5wS" sceneMemberID="viewController">
+ <window key="window" title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="IQv-IB-iLA">
+ <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
+ <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
+ <rect key="contentRect" x="196" y="240" width="480" height="270"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
+ <connections>
+ <outlet property="delegate" destination="B8D-0N-5wS" id="98r-iN-zZc"/>
+ </connections>
+ </window>
+ <connections>
+ <segue destination="XfG-lQ-9wD" kind="relationship" relationship="window.shadowedContentViewController" id="cq2-FE-JQM"/>
+ </connections>
+ </windowController>
+ <customObject id="Oky-zY-oP4" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="75" y="250"/>
+ </scene>
+ <!--View Controller-->
+ <scene sceneID="hIz-AP-VOD">
+ <objects>
+ <viewController id="XfG-lQ-9wD" customClass="ViewController" sceneMemberID="viewController">
+ <view key="view" wantsLayer="YES" id="m2S-Jp-Qdl">
+ <rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
+ <autoresizingMask key="autoresizingMask"/>
+ <subviews>
+ <button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Jdf-YK-4JB">
+ <rect key="frame" x="23" y="173" width="64" height="32"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <buttonCell key="cell" type="push" title="Run" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="1eG-Fb-Ybk">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="system"/>
+ </buttonCell>
+ <connections>
+ <action selector="onRunButtonClicked:" target="XfG-lQ-9wD" id="zyK-t7-vwt"/>
+ </connections>
+ </button>
+ </subviews>
+ </view>
+ </viewController>
+ <customObject id="rPt-NT-nkU" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="75" y="655"/>
+ </scene>
+ </scenes>
+</document>
diff --git a/example/MacOS/ndpiExample/ndpiExample/Info.plist b/example/MacOS/ndpiExample/ndpiExample/Info.plist
new file mode 100644
index 000000000..38bef7f2b
--- /dev/null
+++ b/example/MacOS/ndpiExample/ndpiExample/Info.plist
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>$(MACOSX_DEPLOYMENT_TARGET)</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>Copyright © 2018年 ZengYingpei. All rights reserved.</string>
+ <key>NSMainStoryboardFile</key>
+ <string>Main</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
diff --git a/example/MacOS/ndpiExample/ndpiExample/ViewController.h b/example/MacOS/ndpiExample/ndpiExample/ViewController.h
new file mode 100644
index 000000000..7aeb1fa2d
--- /dev/null
+++ b/example/MacOS/ndpiExample/ndpiExample/ViewController.h
@@ -0,0 +1,29 @@
+/*
+ *
+ * Copyright (C) 2011-18 - ntop.org
+ *
+ * This file is part of nDPI, an open source deep packet inspection
+ * library based on the OpenDPI and PACE technology by ipoque GmbH
+ *
+ * nDPI is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * nDPI is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with nDPI. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#import <Cocoa/Cocoa.h>
+
+@interface ViewController : NSViewController
+
+
+@end
+
diff --git a/example/MacOS/ndpiExample/ndpiExample/ViewController.m b/example/MacOS/ndpiExample/ndpiExample/ViewController.m
new file mode 100644
index 000000000..b18e21e19
--- /dev/null
+++ b/example/MacOS/ndpiExample/ndpiExample/ViewController.m
@@ -0,0 +1,106 @@
+/*
+ *
+ * Copyright (C) 2011-18 - ntop.org
+ *
+ * This file is part of nDPI, an open source deep packet inspection
+ * library based on the OpenDPI and PACE technology by ipoque GmbH
+ *
+ * nDPI is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * nDPI is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with nDPI. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#import "ViewController.h"
+#include "ndpi_api.h"
+
+// Declare the orginal_main defined in ndpiReader.c here
+extern int orginal_main(int argc, char **argv);
+
+@implementation ViewController
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ // Do any additional setup after loading the view.
+}
+
+
+- (void)setRepresentedObject:(id)representedObject {
+ [super setRepresentedObject:representedObject];
+
+ // Update the view, if already loaded.
+}
+
+
+- (IBAction)onRunButtonClicked:(id)sender
+{
+ char* args[10];
+
+ extern int optind;
+ optind = 1; // reset the parse of getopt_long
+
+ // Check the "nDPI_QuickStartGuide.pdf" for comand option explanation.
+
+ /* Following code it to execute below command (remember to change args[2] to
+ * absolute path):
+ * ./ndpiReader -i capture.pcap
+ */
+ args[0] = (char*)"ndpiReader";
+ args[1] = (char*)"-i";
+ NSString* pcap_file = [[NSBundle mainBundle]pathForResource:@"capture" ofType:@"pcap"];
+ args[2] = (char*)[pcap_file cStringUsingEncoding:NSUTF8StringEncoding];
+ // Change to you pcap file path if you want.
+ //args[2] = (char*)"/Users/zengyingpei/Documents/code/nDPI/example/MacOS/ndpiExample/ndpiExample/capture.pcap";
+ // Remember to change below number of args when you change to other command inputs.
+ orginal_main(3, args);
+
+
+
+ /* Following code it to execute below command:
+ * ./ndpiReader -i en1 -s 10 -p protos.txt
+ * The process seems to be not support re-entering. You may have to re-run the App.
+ */
+ /*
+ args[0] = (char*)"ndpiReader";
+ args[1] = (char*)"-i";
+ args[2] = (char*)"en0";
+ args[3] = (char*)"-s";
+ args[4] = (char*)"10";
+ args[5] = (char*)"-p";
+ NSString* proto_file = [[NSBundle mainBundle]pathForResource:@"protos" ofType:@"txt"];
+ args[6] = (char*)[proto_file cStringUsingEncoding:NSUTF8StringEncoding];
+ //args[6] = (char*)"/Users/zengyingpei/Documents/code/nDPI/example/protos.txt";
+ orginal_main(7, args);
+ */
+}
+
+
+// In order to fix the missing of NDPI_LOG_DEBUG2 (used in ndpi_main.c), we define
+// NDPI_LOG_DEBUG2 as NDPI_LOG_DEBUG2_XCODE_PROJ.
+
+void vNDPI_LOG_DEBUG2_XCODE_PROJ(struct ndpi_detection_module_struct * ndpi_struct,
+ const char *format, va_list ap)
+{
+ vprintf(format, ap);
+}
+
+void NDPI_LOG_DEBUG2_XCODE_PROJ(struct ndpi_detection_module_struct * ndpi_struct,
+ const char *format, ...)
+{
+ va_list ap;
+ va_start (ap, format);
+ vNDPI_LOG_DEBUG2_XCODE_PROJ(ndpi_struct, format, ap);
+ va_end (ap);
+}
+
+@end
diff --git a/example/MacOS/ndpiExample/ndpiExample/capture.pcap b/example/MacOS/ndpiExample/ndpiExample/capture.pcap
new file mode 100644
index 000000000..133df898b
--- /dev/null
+++ b/example/MacOS/ndpiExample/ndpiExample/capture.pcap
Binary files differ
diff --git a/example/MacOS/ndpiExample/ndpiExample/main.m b/example/MacOS/ndpiExample/ndpiExample/main.m
new file mode 100644
index 000000000..85eb5f7e7
--- /dev/null
+++ b/example/MacOS/ndpiExample/ndpiExample/main.m
@@ -0,0 +1,27 @@
+/*
+ *
+ * Copyright (C) 2011-18 - ntop.org
+ *
+ * This file is part of nDPI, an open source deep packet inspection
+ * library based on the OpenDPI and PACE technology by ipoque GmbH
+ *
+ * nDPI is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * nDPI is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with nDPI. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#import <Cocoa/Cocoa.h>
+
+int main(int argc, const char * argv[]) {
+ return NSApplicationMain(argc, argv);
+}
diff --git a/example/MacOS/ndpiExample/ndpiExample/ndpiExample.entitlements b/example/MacOS/ndpiExample/ndpiExample/ndpiExample.entitlements
new file mode 100644
index 000000000..0c67376eb
--- /dev/null
+++ b/example/MacOS/ndpiExample/ndpiExample/ndpiExample.entitlements
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict/>
+</plist>
diff --git a/example/MacOS/ndpiExample/ndpiExample/ndpi_utils.h b/example/MacOS/ndpiExample/ndpiExample/ndpi_utils.h
new file mode 100644
index 000000000..54bb7970c
--- /dev/null
+++ b/example/MacOS/ndpiExample/ndpiExample/ndpi_utils.h
@@ -0,0 +1,29 @@
+/*
+ *
+ * Copyright (C) 2011-18 - ntop.org
+ *
+ * This file is part of nDPI, an open source deep packet inspection
+ * library based on the OpenDPI and PACE technology by ipoque GmbH
+ *
+ * nDPI is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * nDPI is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with nDPI. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef ndpi_utils_h
+#define ndpi_utils_h
+
+// Empty placeholder head file. Just to make the compilations of protocols/attic/ftp.c and
+// protocols/attic/secondlife.c don't break.
+
+#endif /* ndpi_utils_h */
diff --git a/example/Makefile.am b/example/Makefile.am
index 662f5a3ba..9eedc21d8 100644
--- a/example/Makefile.am
+++ b/example/Makefile.am
@@ -1,12 +1,12 @@
bin_PROGRAMS = ndpiReader
-AM_CPPFLAGS = -I$(top_srcdir)/src/include @PCAP_INC@
+AM_CPPFLAGS = -I$(top_srcdir)/src/include -I$(top_srcdir)/src/lib/third_party/include @PCAP_INC@ @HS_INC@
AM_CFLAGS = @PTHREAD_CFLAGS@ # --coverage
-LDADD = $(top_builddir)/src/lib/libndpi.la @JSON_C_LIB@ @PTHREAD_LIBS@ @PCAP_LIB@ -ldl
-AM_LDFLAGS = -static @DL_LIB@
+LDADD = $(top_builddir)/src/lib/libndpi.la @JSON_C_LIB@ @PTHREAD_LIBS@ @PCAP_LIB@ @DL_LIB@ @HS_LIB@ -lm
+AM_LDFLAGS = -static @DL_LIB@ @HS_LIB@
-ndpiReader_SOURCES = ndpiReader.c ndpi_util.c ndpi_util.h
+ndpiReader_SOURCES = ndpiReader.c ndpi_util.c ndpi_util.h uthash.h
ndpiReader.o: ndpiReader.c
diff --git a/example/ndpiReader.c b/example/ndpiReader.c
index e1777d7da..38ce75b14 100644
--- a/example/ndpiReader.c
+++ b/example/ndpiReader.c
@@ -1,7 +1,7 @@
/*
* ndpiReader.c
*
- * Copyright (C) 2011-16 - ntop.org
+ * Copyright (C) 2011-17 - ntop.org
*
* nDPI is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -24,11 +24,11 @@
#endif
#include <stdio.h>
#include <stdlib.h>
+#include <getopt.h>
#ifdef WIN32
#include <winsock2.h> /* winsock.h is included automatically */
#include <process.h>
#include <io.h>
-#include <getopt.h>
#define getopt getopt____
#else
#include <unistd.h>
@@ -42,8 +42,13 @@
#include <pthread.h>
#include <sys/socket.h>
#include <assert.h>
-#include "../config.h"
+#include <math.h>
#include "ndpi_api.h"
+#include "uthash.h"
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <libgen.h>
#ifdef HAVE_JSON_C
#include <json.h>
@@ -52,26 +57,38 @@
#include "ndpi_util.h"
/** Client parameters **/
-static char *_pcap_file[MAX_NUM_READER_THREADS]; /**< Ingress pcap file/interafaces */
+static char *_pcap_file[MAX_NUM_READER_THREADS]; /**< Ingress pcap file/interfaces */
static FILE *playlist_fp[MAX_NUM_READER_THREADS] = { NULL }; /**< Ingress playlist */
-static FILE *results_file = NULL;
-static char *results_path = NULL;
-static char *_bpf_filter = NULL; /**< bpf filter */
-static char *_protoFilePath = NULL; /**< Protocol file path */
+static FILE *results_file = NULL;
+static char *results_path = NULL;
+static char * bpfFilter = NULL; /**< bpf filter */
+static char *_protoFilePath = NULL; /**< Protocol file path */
#ifdef HAVE_JSON_C
-static char *_jsonFilePath = NULL; /**< JSON file path */
+static char *_statsFilePath = NULL; /**< Top stats file path */
+static char *_diagnoseFilePath = NULL; /**< Top stats file path */
+static char *_jsonFilePath = NULL; /**< JSON file path */
+static FILE *stats_fp = NULL; /**< for Top Stats JSON file */
#endif
#ifdef HAVE_JSON_C
static json_object *jArray_known_flows, *jArray_unknown_flows;
+static json_object *jArray_topStats;
#endif
static u_int8_t live_capture = 0;
static u_int8_t undetected_flows_deleted = 0;
/** User preferences **/
-static u_int8_t enable_protocol_guess = 1, verbose = 0, nDPI_traceLevel = 0, json_flag = 0;
+static u_int8_t enable_protocol_guess = 1, verbose = 0, json_flag = 0;
+int nDPI_LogLevel = 0;
+char *_debug_protocols = NULL;
+static u_int8_t stats_flag = 0, bpf_filter_flag = 0;
+#ifdef HAVE_JSON_C
+static u_int8_t file_first_time = 1;
+#endif
+static u_int32_t pcap_analysis_duration = (u_int32_t)-1;
static u_int16_t decode_tunnels = 0;
static u_int16_t num_loops = 1;
static u_int8_t shutdown_app = 0, quiet_mode = 0;
static u_int8_t num_threads = 1;
+static struct timeval begin, end;
#ifdef linux
static int core_affinity[MAX_NUM_READER_THREADS];
#endif
@@ -80,10 +97,92 @@ static struct timeval pcap_start, pcap_end;
static time_t capture_for = 0;
static time_t capture_until = 0;
static u_int32_t num_flows;
+static struct ndpi_detection_module_struct *ndpi_info_mod = NULL;
+
+struct flow_info {
+ struct ndpi_flow_info *flow;
+ u_int16_t thread_id;
+};
+
+static struct flow_info *all_flows;
+
+
+struct info_pair {
+ u_int32_t addr;
+ u_int8_t version; /* IP version */
+ char proto[16]; /*app level protocol*/
+ int count;
+};
+
+typedef struct node_a{
+ u_int32_t addr;
+ u_int8_t version; /* IP version */
+ char proto[16]; /*app level protocol*/
+ int count;
+ struct node_a *left, *right;
+}addr_node;
+
+struct port_stats {
+ u_int32_t port; /* we'll use this field as the key */
+ u_int32_t num_pkts, num_bytes;
+ u_int32_t num_flows;
+ u_int32_t num_addr; /*number of distinct IP addresses */
+ u_int32_t cumulative_addr; /*cumulative some of IP addresses */
+ addr_node *addr_tree; /* tree of distinct IP addresses */
+ struct info_pair top_ip_addrs[MAX_NUM_IP_ADDRESS];
+ u_int8_t hasTopHost; /* as boolean flag*/
+ u_int32_t top_host; /*host that is contributed to > 95% of traffic*/
+ u_int8_t version; /* top host's ip version */
+ char proto[16]; /*application level protocol of top host */
+ UT_hash_handle hh; /* makes this structure hashable */
+};
+
+struct port_stats *srcStats = NULL, *dstStats = NULL;
+
+
+// struct to hold count of flows received by destination ports
+struct port_flow_info {
+ u_int32_t port; /* key */
+ u_int32_t num_flows;
+ UT_hash_handle hh;
+};
+
+// struct to hold single packet tcp flows sent by source ip address
+struct single_flow_info {
+ u_int32_t saddr; /* key */
+ u_int8_t version; /* IP version */
+ struct port_flow_info *ports;
+ u_int32_t tot_flows;
+ UT_hash_handle hh;
+};
+
+struct single_flow_info *scannerHosts = NULL;
+
+// struct to hold top receiver hosts
+struct receiver {
+ u_int32_t addr; /* key */
+ u_int8_t version; /* IP version */
+ u_int32_t num_pkts;
+ UT_hash_handle hh;
+};
+
+struct receiver *receivers = NULL, *topReceivers = NULL;
+
+
+struct ndpi_packet_trailer {
+ u_int32_t magic; /* 0x19682017 */
+ u_int16_t master_protocol /* e.g. HTTP */, app_protocol /* e.g. FaceBook */;
+ char name[16];
+};
+
+static pcap_dumper_t *extcap_dumper = NULL;
+static char extcap_buf[16384];
+static char *extcap_capture_fifo = NULL;
+static u_int16_t extcap_packet_filter = (u_int16_t)-1;
// struct associated to a workflow for a thread
struct reader_thread {
- struct ndpi_workflow * workflow;
+ struct ndpi_workflow *workflow;
pthread_t pthread;
u_int64_t last_idle_scan_time;
u_int32_t idle_scan_idx;
@@ -104,31 +203,40 @@ typedef struct ndpi_id {
u_int32_t current_ndpi_memory = 0, max_ndpi_memory = 0;
-/********************** FUNCTIONS ********************* */
+void test_lib(); /* Forward */
+
+/* ********************************** */
+#ifdef DEBUG_TRACE
+FILE *trace = NULL;
+#endif
+
+/********************** FUNCTIONS ********************* */
/**
* @brief Set main components necessary to the detection
*/
static void setupDetection(u_int16_t thread_id, pcap_t * pcap_handle);
-
/**
* @brief Print help instructions
*/
static void help(u_int long_help) {
printf("Welcome to nDPI %s\n\n", ndpi_revision());
- printf("ndpiReader -i <file|device> [-f <filter>][-s <duration>]\n"
+ printf("ndpiReader -i <file|device> [-f <filter>][-s <duration>][-m <duration>]\n"
" [-p <protos>][-l <loops> [-q][-d][-h][-t][-v <level>]\n"
- " [-n <threads>] [-w <file>] [-j <file>]\n\n"
+ " [-n <threads>] [-w <file>] [-j <file>] [-x <file>] \n\n"
"Usage:\n"
- " -i <file.pcap|device> | Specify a pcap file/playlist to read packets from or a device for live capture (comma-separated list)\n"
+ " -i <file.pcap|device> | Specify a pcap file/playlist to read packets from or a\n"
+ " | device for live capture (comma-separated list)\n"
" -f <BPF filter> | Specify a BPF filter for filtering selected traffic\n"
" -s <duration> | Maximum capture duration in seconds (live traffic capture only)\n"
+ " -m <duration> | Split analysis duration in <duration> max seconds\n"
" -p <file>.protos | Specify a protocol file (eg. protos.txt)\n"
" -l <num loops> | Number of detection loops (test only)\n"
- " -n <num threads> | Number of threads. Default: number of interfaces in -i. Ignored with pcap files.\n"
+ " -n <num threads> | Number of threads. Default: number of interfaces in -i.\n"
+ " | Ignored with pcap files.\n"
" -j <file.json> | Specify a file to write the content of packets in .json format\n"
#ifdef linux
" -g <id:id...> | Thread affinity mask (one core id per thread)\n"
@@ -140,41 +248,243 @@ static void help(u_int long_help) {
" -w <path> | Write test output on the specified file. This is useful for\n"
" | testing purposes in order to compare results across runs\n"
" -h | This help\n"
- " -v <1|2> | Verbose 'unknown protocol' packet print. 1=verbose, 2=very verbose\n");
+ " -v <1|2|3> | Verbose 'unknown protocol' packet print.\n"
+ " | 1 = verbose\n"
+ " | 2 = very verbose\n"
+ " | 3 = port stats\n"
+ " -V <1-4> | nDPI logging level\n"
+ " | 1 - trace, 2 - debug, 3 - full debug\n"
+ " | >3 - full debug + dbg_proto = all\n"
+ " -b <file.json> | Specify a file to write port based diagnose statistics\n"
+ " -x <file.json> | Produce bpf filters for specified diagnose file. Use\n"
+ " | this option only for .json files generated with -b flag.\n");
+
+
+#ifndef WIN32
+ printf("\nExcap (wireshark) options:\n"
+ " --extcap-interfaces\n"
+ " --extcap-version\n"
+ " --extcap-dlts\n"
+ " --extcap-interface <name>\n"
+ " --extcap-config\n"
+ " --capture\n"
+ " --extcap-capture-filter\n"
+ " --fifo <path to file or pipe>\n"
+ " --debug\n"
+ " --dbg-proto proto|num[,...]\n"
+ );
+#endif
if(long_help) {
printf("\n\nSupported protocols:\n");
num_threads = 1;
- setupDetection(0, NULL);
- ndpi_dump_protocols(ndpi_thread_info[0].workflow->ndpi_struct);
+ ndpi_dump_protocols(ndpi_info_mod);
}
exit(!long_help);
}
+static struct option longopts[] = {
+ /* mandatory extcap options */
+ { "extcap-interfaces", no_argument, NULL, '0'},
+ { "extcap-version", optional_argument, NULL, '1'},
+ { "extcap-dlts", no_argument, NULL, '2'},
+ { "extcap-interface", required_argument, NULL, '3'},
+ { "extcap-config", no_argument, NULL, '4'},
+ { "capture", no_argument, NULL, '5'},
+ { "extcap-capture-filter", required_argument, NULL, '6'},
+ { "fifo", required_argument, NULL, '7'},
+ { "debug", no_argument, NULL, '8'},
+ { "dbg-proto", required_argument, NULL, 257},
+ { "ndpi-proto-filter", required_argument, NULL, '9'},
+
+ /* ndpiReader options */
+ { "enable-protocol-guess", no_argument, NULL, 'd'},
+ { "interface", required_argument, NULL, 'i'},
+ { "filter", required_argument, NULL, 'f'},
+ { "cpu-bind", required_argument, NULL, 'g'},
+ { "loops", required_argument, NULL, 'l'},
+ { "num-threads", required_argument, NULL, 'n'},
+
+ { "protos", required_argument, NULL, 'p'},
+ { "capture-duration", required_argument, NULL, 's'},
+ { "decode-tunnels", no_argument, NULL, 't'},
+ { "revision", no_argument, NULL, 'r'},
+ { "verbose", no_argument, NULL, 'v'},
+ { "version", no_argument, NULL, 'V'},
+ { "help", no_argument, NULL, 'h'},
+ { "json", required_argument, NULL, 'j'},
+ { "result-path", required_argument, NULL, 'w'},
+ { "quiet", no_argument, NULL, 'q'},
+
+ {0, 0, 0, 0}
+};
+
+/* ********************************** */
+
+void extcap_interfaces() {
+ printf("extcap {version=%s}\n", ndpi_revision());
+ printf("interface {value=ndpi}{display=nDPI interface}\n");
+ exit(0);
+}
+
+/* ********************************** */
+
+void extcap_dlts() {
+ u_int dlts_number = DLT_EN10MB;
+ printf("dlt {number=%u}{name=%s}{display=%s}\n", dlts_number, "ndpi", "nDPI Interface");
+ exit(0);
+}
+
+/* ********************************** */
+
+struct ndpi_proto_sorter {
+ int id;
+ char name[16];
+};
+
+int cmpProto(const void *_a, const void *_b) {
+ struct ndpi_proto_sorter *a = (struct ndpi_proto_sorter*)_a;
+ struct ndpi_proto_sorter *b = (struct ndpi_proto_sorter*)_b;
+
+ return(strcmp(a->name, b->name));
+}
+
+int cmpFlows(const void *_a, const void *_b) {
+ struct ndpi_flow_info *fa = ((struct flow_info*)_a)->flow;
+ struct ndpi_flow_info *fb = ((struct flow_info*)_b)->flow;
+ uint64_t a_size = fa->src2dst_bytes + fa->dst2src_bytes;
+ uint64_t b_size = fb->src2dst_bytes + fb->dst2src_bytes;
+ if(a_size != b_size)
+ return a_size < b_size ? 1 : -1;
+
+// copy from ndpi_workflow_node_cmp();
+
+ if(fa->ip_version < fb->ip_version ) return(-1); else { if(fa->ip_version > fb->ip_version ) return(1); }
+ if(fa->protocol < fb->protocol ) return(-1); else { if(fa->protocol > fb->protocol ) return(1); }
+ if(htonl(fa->src_ip) < htonl(fb->src_ip) ) return(-1); else { if(htonl(fa->src_ip) > htonl(fb->src_ip) ) return(1); }
+ if(htons(fa->src_port) < htons(fb->src_port)) return(-1); else { if(htons(fa->src_port) > htons(fb->src_port)) return(1); }
+ if(htonl(fa->dst_ip) < htonl(fb->dst_ip) ) return(-1); else { if(htonl(fa->dst_ip) > htonl(fb->dst_ip) ) return(1); }
+ if(htons(fa->dst_port) < htons(fb->dst_port)) return(-1); else { if(htons(fa->dst_port) > htons(fb->dst_port)) return(1); }
+ return(0);
+}
+
+void extcap_config() {
+ int i, argidx = 0;
+ struct ndpi_proto_sorter *protos;
+
+ /* -i <interface> */
+ printf("arg {number=%d}{call=-i}{display=Capture Interface}{type=string}"
+ "{tooltip=The interface name}\n", argidx++);
+ printf("arg {number=%d}{call=-i}{display=Pcap File to Analyze}{type=fileselect}"
+ "{tooltip=The pcap file to analyze (if the interface is unspecified)}\n", argidx++);
+
+ protos = (struct ndpi_proto_sorter*)malloc(sizeof(struct ndpi_proto_sorter) * ndpi_info_mod->ndpi_num_supported_protocols);
+ if(!protos) exit(0);
+
+ for(i=0; i<(int) ndpi_info_mod->ndpi_num_supported_protocols; i++) {
+ protos[i].id = i;
+ snprintf(protos[i].name, sizeof(protos[i].name), "%s", ndpi_info_mod->proto_defaults[i].protoName);
+ }
+
+ qsort(protos, ndpi_info_mod->ndpi_num_supported_protocols, sizeof(struct ndpi_proto_sorter), cmpProto);
+
+ printf("arg {number=%d}{call=-9}{display=nDPI Protocol Filter}{type=selector}"
+ "{tooltip=nDPI Protocol to be filtered}\n", argidx);
+
+ printf("value {arg=%d}{value=%d}{display=%s}\n", argidx, -1, "All Protocols (no nDPI filtering)");
+
+ for(i=0; i<(int)ndpi_info_mod->ndpi_num_supported_protocols; i++)
+ printf("value {arg=%d}{value=%d}{display=%s (%d)}\n", argidx, protos[i].id,
+ protos[i].name, protos[i].id);
+
+ free(protos);
+
+ exit(0);
+}
+
+/* ********************************** */
+
+void extcap_capture() {
+#ifdef DEBUG_TRACE
+ if(trace) fprintf(trace, " #### %s #### \n", __FUNCTION__);
+#endif
+
+ if((extcap_dumper = pcap_dump_open(pcap_open_dead(DLT_EN10MB, 16384 /* MTU */),
+ extcap_capture_fifo)) == NULL) {
+ fprintf(stderr, "Unable to open the pcap dumper on %s", extcap_capture_fifo);
+
+#ifdef DEBUG_TRACE
+ if(trace) fprintf(trace, "Unable to open the pcap dumper on %s\n",
+ extcap_capture_fifo);
+#endif
+ return;
+ }
+
+#ifdef DEBUG_TRACE
+ if(trace) fprintf(trace, "Starting packet capture [%p]\n", extcap_dumper);
+#endif
+}
+
+/* ********************************** */
+
/**
* @brief Option parser
*/
static void parseOptions(int argc, char **argv) {
-
+ int option_idx = 0, do_capture = 0;
char *__pcap_file = NULL, *bind_mask = NULL;
int thread_id, opt;
#ifdef linux
u_int num_cores = sysconf(_SC_NPROCESSORS_ONLN);
#endif
- while ((opt = getopt(argc, argv, "df:g:i:hp:l:s:tv:V:n:j:rp:w:q")) != EOF) {
+#ifdef DEBUG_TRACE
+ trace = fopen("/tmp/ndpiReader.log", "a");
+
+ if(trace) fprintf(trace, " #### %s #### \n", __FUNCTION__);
+#endif
+
+ while ((opt = getopt_long(argc, argv, "df:g:i:hp:l:s:tv:V:n:j:rp:w:q0123:456:7:89:m:b:x:", longopts, &option_idx)) != EOF) {
+#ifdef DEBUG_TRACE
+ if(trace) fprintf(trace, " #### -%c [%s] #### \n", opt, optarg ? optarg : "");
+#endif
+
switch (opt) {
case 'd':
enable_protocol_guess = 0;
break;
case 'i':
+ case '3':
_pcap_file[0] = optarg;
break;
+ case 'b':
+#ifndef HAVE_JSON_C
+ printf("WARNING: this copy of ndpiReader has been compiled without JSON-C: json export disabled\n");
+#else
+ _statsFilePath = optarg;
+ stats_flag = 1;
+#endif
+ break;
+
+ case 'm':
+ pcap_analysis_duration = atol(optarg);
+ break;
+
+ case 'x':
+#ifndef HAVE_JSON_C
+ printf("WARNING: this copy of ndpiReader has been compiled without JSON-C: json export disabled\n");
+#else
+ _diagnoseFilePath = optarg;
+ bpf_filter_flag = 1;
+#endif
+ break;
+
case 'f':
- _bpf_filter = optarg;
+ case '6':
+ bpfFilter = optarg;
break;
case 'g':
@@ -211,8 +521,12 @@ static void parseOptions(int argc, char **argv) {
break;
case 'V':
- printf("%d\n",atoi(optarg) );
- nDPI_traceLevel = atoi(optarg);
+ nDPI_LogLevel = atoi(optarg);
+ if(nDPI_LogLevel < 0) nDPI_LogLevel = 0;
+ if(nDPI_LogLevel > 3) {
+ nDPI_LogLevel = 3;
+ _debug_protocols = strdup("all");
+ }
break;
case 'h':
@@ -238,6 +552,46 @@ static void parseOptions(int argc, char **argv) {
case 'q':
quiet_mode = 1;
+ nDPI_LogLevel = 0;
+ break;
+
+ /* Extcap */
+ case '0':
+ extcap_interfaces();
+ break;
+
+ case '1':
+ printf("extcap {version=%s}\n", ndpi_revision());
+ break;
+
+ case '2':
+ extcap_dlts();
+ break;
+
+ case '4':
+ extcap_config();
+ break;
+
+ case '5':
+ do_capture = 1;
+ break;
+
+ case '7':
+ extcap_capture_fifo = strdup(optarg);
+ break;
+
+ case '8':
+ nDPI_LogLevel = NDPI_LOG_DEBUG_EXTRA;
+ _debug_protocols = strdup("all");
+ break;
+
+ case '9':
+ extcap_packet_filter = ndpi_get_proto_by_name(ndpi_info_mod, optarg);
+ if (extcap_packet_filter == NDPI_PROTOCOL_UNKNOWN) extcap_packet_filter = atoi(optarg);
+ break;
+
+ case 257:
+ _debug_protocols = strdup(optarg);
break;
default:
@@ -246,36 +600,47 @@ static void parseOptions(int argc, char **argv) {
}
}
- // check parameters
- if(_pcap_file[0] == NULL || strcmp(_pcap_file[0], "") == 0) {
- help(0);
- }
+ if(!bpf_filter_flag) {
+ if(do_capture) {
+ quiet_mode = 1;
+ extcap_capture();
+ }
- if(strchr(_pcap_file[0], ',')) { /* multiple ingress interfaces */
- num_threads = 0; /* setting number of threads = number of interfaces */
- __pcap_file = strtok(_pcap_file[0], ",");
- while (__pcap_file != NULL && num_threads < MAX_NUM_READER_THREADS) {
- _pcap_file[num_threads++] = __pcap_file;
- __pcap_file = strtok(NULL, ",");
+ // check parameters
+ if(!bpf_filter_flag && (_pcap_file[0] == NULL || strcmp(_pcap_file[0], "") == 0)) {
+ help(0);
}
- } else {
- if(num_threads > MAX_NUM_READER_THREADS) num_threads = MAX_NUM_READER_THREADS;
- for(thread_id = 1; thread_id < num_threads; thread_id++)
- _pcap_file[thread_id] = _pcap_file[0];
- }
-#ifdef linux
- for(thread_id = 0; thread_id < num_threads; thread_id++)
- core_affinity[thread_id] = -1;
+ if(strchr(_pcap_file[0], ',')) { /* multiple ingress interfaces */
+ num_threads = 0; /* setting number of threads = number of interfaces */
+ __pcap_file = strtok(_pcap_file[0], ",");
+ while (__pcap_file != NULL && num_threads < MAX_NUM_READER_THREADS) {
+ _pcap_file[num_threads++] = __pcap_file;
+ __pcap_file = strtok(NULL, ",");
+ }
+ } else {
+ if(num_threads > MAX_NUM_READER_THREADS) num_threads = MAX_NUM_READER_THREADS;
+ for(thread_id = 1; thread_id < num_threads; thread_id++)
+ _pcap_file[thread_id] = _pcap_file[0];
+ }
- if(num_cores > 1 && bind_mask != NULL) {
- char *core_id = strtok(bind_mask, ":");
- thread_id = 0;
- while (core_id != NULL && thread_id < num_threads) {
- core_affinity[thread_id++] = atoi(core_id) % num_cores;
- core_id = strtok(NULL, ":");
+#ifdef linux
+ for(thread_id = 0; thread_id < num_threads; thread_id++)
+ core_affinity[thread_id] = -1;
+
+ if(num_cores > 1 && bind_mask != NULL) {
+ char *core_id = strtok(bind_mask, ":");
+ thread_id = 0;
+ while (core_id != NULL && thread_id < num_threads) {
+ core_affinity[thread_id++] = atoi(core_id) % num_cores;
+ core_id = strtok(NULL, ":");
+ }
}
+#endif
}
+
+#ifdef DEBUG_TRACE
+ if(trace) fclose(trace);
#endif
}
@@ -349,25 +714,27 @@ char* intoaV4(u_int32_t addr, char* buf, u_int16_t bufLen) {
/**
* @brief Print the flow
*/
-static void printFlow(u_int16_t thread_id, struct ndpi_flow_info *flow) {
+static void printFlow(u_int16_t id, struct ndpi_flow_info *flow, u_int16_t thread_id) {
#ifdef HAVE_JSON_C
json_object *jObj;
#endif
FILE *out = results_file ? results_file : stdout;
+ if((verbose != 1) && (verbose != 2))
+ return;
+
if(!json_flag) {
- fprintf(out, "\t%u", ++num_flows);
+ fprintf(out, "\t%u", id);
+
+ fprintf(out, "\t%s ", ipProto2Name(flow->protocol));
- fprintf(out, "\t%s %s%s%s:%u <-> %s%s%s:%u ",
- ipProto2Name(flow->protocol),
+ fprintf(out, "%s%s%s:%u %s %s%s%s:%u ",
(flow->ip_version == 6) ? "[" : "",
- flow->lower_name,
- (flow->ip_version == 6) ? "]" : "",
- ntohs(flow->lower_port),
+ flow->src_name, (flow->ip_version == 6) ? "]" : "", ntohs(flow->src_port),
+ flow->bidirectional ? "<->" : "->",
(flow->ip_version == 6) ? "[" : "",
- flow->upper_name,
- (flow->ip_version == 6) ? "]" : "",
- ntohs(flow->upper_port));
+ flow->dst_name, (flow->ip_version == 6) ? "]" : "", ntohs(flow->dst_port)
+ );
if(flow->vlan_id > 0) fprintf(out, "[VLAN: %u]", flow->vlan_id);
@@ -375,20 +742,24 @@ static void printFlow(u_int16_t thread_id, struct ndpi_flow_info *flow) {
char buf[64];
fprintf(out, "[proto: %u.%u/%s]",
- flow->detected_protocol.master_protocol, flow->detected_protocol.protocol,
+ flow->detected_protocol.master_protocol, flow->detected_protocol.app_protocol,
ndpi_protocol2name(ndpi_thread_info[thread_id].workflow->ndpi_struct,
flow->detected_protocol, buf, sizeof(buf)));
} else
fprintf(out, "[proto: %u/%s]",
- flow->detected_protocol.protocol,
- ndpi_get_proto_name(ndpi_thread_info[thread_id].workflow->ndpi_struct, flow->detected_protocol.protocol));
+ flow->detected_protocol.app_protocol,
+ ndpi_get_proto_name(ndpi_thread_info[thread_id].workflow->ndpi_struct, flow->detected_protocol.app_protocol));
- fprintf(out, "[%u pkts/%llu bytes]",
- flow->packets, (long long unsigned int) flow->bytes);
+ fprintf(out, "[%u pkts/%llu bytes ", flow->src2dst_packets, (long long unsigned int) flow->src2dst_bytes);
+ fprintf(out, "%s %u pkts/%llu bytes]",
+ (flow->dst2src_packets > 0) ? "<->" : "->",
+ flow->dst2src_packets, (long long unsigned int) flow->dst2src_bytes);
if(flow->host_server_name[0] != '\0') fprintf(out, "[Host: %s]", flow->host_server_name);
- if(flow->ssl.client_certificate[0] != '\0') fprintf(out, "[SSL client: %s]", flow->ssl.client_certificate);
- if(flow->ssl.server_certificate[0] != '\0') fprintf(out, "[SSL server: %s]", flow->ssl.server_certificate);
+ if(flow->info[0] != '\0') fprintf(out, "[%s]", flow->info);
+
+ if(flow->ssh_ssl.client_info[0] != '\0') fprintf(out, "[client: %s]", flow->ssh_ssl.client_info);
+ if(flow->ssh_ssl.server_info[0] != '\0') fprintf(out, "[server: %s]", flow->ssh_ssl.server_info);
if(flow->bittorent_hash[0] != '\0') fprintf(out, "[BT Hash: %s]", flow->bittorent_hash);
fprintf(out, "\n");
@@ -397,46 +768,46 @@ static void printFlow(u_int16_t thread_id, struct ndpi_flow_info *flow) {
jObj = json_object_new_object();
json_object_object_add(jObj,"protocol",json_object_new_string(ipProto2Name(flow->protocol)));
- json_object_object_add(jObj,"host_a.name",json_object_new_string(flow->lower_name));
- json_object_object_add(jObj,"host_a.port",json_object_new_int(ntohs(flow->lower_port)));
- json_object_object_add(jObj,"host_b.name",json_object_new_string(flow->upper_name));
- json_object_object_add(jObj,"host_b.port",json_object_new_int(ntohs(flow->upper_port)));
+ json_object_object_add(jObj,"host_a.name",json_object_new_string(flow->src_name));
+ json_object_object_add(jObj,"host_a.port",json_object_new_int(ntohs(flow->src_port)));
+ json_object_object_add(jObj,"host_b.name",json_object_new_string(flow->dst_name));
+ json_object_object_add(jObj,"host_b.port",json_object_new_int(ntohs(flow->dst_port)));
if(flow->detected_protocol.master_protocol)
- json_object_object_add(jObj,"detected.masterprotocol",json_object_new_int(flow->detected_protocol.master_protocol));
+ json_object_object_add(jObj,"detected.master_protocol",json_object_new_int(flow->detected_protocol.master_protocol));
- json_object_object_add(jObj,"detected.protocol",json_object_new_int(flow->detected_protocol.protocol));
+ json_object_object_add(jObj,"detected.app_protocol",json_object_new_int(flow->detected_protocol.app_protocol));
if(flow->detected_protocol.master_protocol) {
char tmp[256];
snprintf(tmp, sizeof(tmp), "%s.%s",
ndpi_get_proto_name(ndpi_thread_info[thread_id].workflow->ndpi_struct, flow->detected_protocol.master_protocol),
- ndpi_get_proto_name(ndpi_thread_info[thread_id].workflow->ndpi_struct, flow->detected_protocol.protocol));
+ ndpi_get_proto_name(ndpi_thread_info[thread_id].workflow->ndpi_struct, flow->detected_protocol.app_protocol));
json_object_object_add(jObj,"detected.protocol.name",
json_object_new_string(tmp));
} else
json_object_object_add(jObj,"detected.protocol.name",
json_object_new_string(ndpi_get_proto_name(ndpi_thread_info[thread_id].workflow->ndpi_struct,
- flow->detected_protocol.protocol)));
+ flow->detected_protocol.app_protocol)));
- json_object_object_add(jObj,"packets",json_object_new_int(flow->packets));
- json_object_object_add(jObj,"bytes",json_object_new_int(flow->bytes));
+ json_object_object_add(jObj,"packets",json_object_new_int(flow->src2dst_packets + flow->dst2src_packets));
+ json_object_object_add(jObj,"bytes",json_object_new_int(flow->src2dst_bytes + flow->dst2src_bytes));
if(flow->host_server_name[0] != '\0')
json_object_object_add(jObj,"host.server.name",json_object_new_string(flow->host_server_name));
- if((flow->ssl.client_certificate[0] != '\0') || (flow->ssl.server_certificate[0] != '\0')) {
+ if((flow->ssh_ssl.client_info[0] != '\0') || (flow->ssh_ssl.server_info[0] != '\0')) {
json_object *sjObj = json_object_new_object();
- if(flow->ssl.client_certificate[0] != '\0')
- json_object_object_add(sjObj, "client", json_object_new_string(flow->ssl.client_certificate));
+ if(flow->ssh_ssl.client_info[0] != '\0')
+ json_object_object_add(sjObj, "client", json_object_new_string(flow->ssh_ssl.client_info));
- if(flow->ssl.server_certificate[0] != '\0')
- json_object_object_add(sjObj, "server", json_object_new_string(flow->ssl.server_certificate));
+ if(flow->ssh_ssl.server_info[0] != '\0')
+ json_object_object_add(sjObj, "server", json_object_new_string(flow->ssh_ssl.server_info));
- json_object_object_add(jObj, "ssl", sjObj);
+ json_object_object_add(jObj, "ssh_ssl", sjObj);
}
if(json_flag == 1)
@@ -456,10 +827,13 @@ static void node_print_unknown_proto_walker(const void *node, ndpi_VISIT which,
struct ndpi_flow_info *flow = *(struct ndpi_flow_info**)node;
u_int16_t thread_id = *((u_int16_t*)user_data);
- if(flow->detected_protocol.protocol != NDPI_PROTOCOL_UNKNOWN) return;
+ if(flow->detected_protocol.app_protocol != NDPI_PROTOCOL_UNKNOWN) return;
- if((which == ndpi_preorder) || (which == ndpi_leaf)) /* Avoid walking the same node multiple times */
- printFlow(thread_id, flow);
+ if((which == ndpi_preorder) || (which == ndpi_leaf)) {
+ /* Avoid walking the same node multiple times */
+ all_flows[num_flows].thread_id = thread_id, all_flows[num_flows].flow = flow;
+ num_flows++;
+ }
}
/**
@@ -470,10 +844,13 @@ static void node_print_known_proto_walker(const void *node, ndpi_VISIT which, in
struct ndpi_flow_info *flow = *(struct ndpi_flow_info**)node;
u_int16_t thread_id = *((u_int16_t*)user_data);
- if(flow->detected_protocol.protocol == NDPI_PROTOCOL_UNKNOWN) return;
+ if(flow->detected_protocol.app_protocol == NDPI_PROTOCOL_UNKNOWN) return;
- if((which == ndpi_preorder) || (which == ndpi_leaf)) /* Avoid walking the same node multiple times */
- printFlow(thread_id, flow);
+ if((which == ndpi_preorder) || (which == ndpi_leaf)) {
+ /* Avoid walking the same node multiple times */
+ all_flows[num_flows].thread_id = thread_id, all_flows[num_flows].flow = flow;
+ num_flows++;
+ }
}
@@ -484,15 +861,15 @@ static u_int16_t node_guess_undetected_protocol(u_int16_t thread_id, struct ndpi
flow->detected_protocol = ndpi_guess_undetected_protocol(ndpi_thread_info[thread_id].workflow->ndpi_struct,
flow->protocol,
- ntohl(flow->lower_ip),
- ntohs(flow->lower_port),
- ntohl(flow->upper_ip),
- ntohs(flow->upper_port));
+ ntohl(flow->src_ip),
+ ntohs(flow->src_port),
+ ntohl(flow->dst_ip),
+ ntohs(flow->dst_port));
// printf("Guess state: %u\n", flow->detected_protocol);
- if(flow->detected_protocol.protocol != NDPI_PROTOCOL_UNKNOWN)
+ if(flow->detected_protocol.app_protocol != NDPI_PROTOCOL_UNKNOWN)
ndpi_thread_info[thread_id].workflow->stats.guessed_flow_protocols++;
- return(flow->detected_protocol.protocol);
+ return(flow->detected_protocol.app_protocol);
}
@@ -500,7 +877,6 @@ static u_int16_t node_guess_undetected_protocol(u_int16_t thread_id, struct ndpi
* @brief Proto Guess Walker
*/
static void node_proto_guess_walker(const void *node, ndpi_VISIT which, int depth, void *user_data) {
-
struct ndpi_flow_info *flow = *(struct ndpi_flow_info **) node;
u_int16_t thread_id = *((u_int16_t *) user_data);
@@ -509,18 +885,452 @@ static void node_proto_guess_walker(const void *node, ndpi_VISIT which, int dept
flow->detected_protocol = ndpi_detection_giveup(ndpi_thread_info[0].workflow->ndpi_struct, flow->ndpi_flow);
if(enable_protocol_guess) {
- if(flow->detected_protocol.protocol == NDPI_PROTOCOL_UNKNOWN) {
+ if(flow->detected_protocol.app_protocol == NDPI_PROTOCOL_UNKNOWN) {
node_guess_undetected_protocol(thread_id, flow);
- // printFlow(thread_id, flow);
}
}
- ndpi_thread_info[thread_id].workflow->stats.protocol_counter[flow->detected_protocol.protocol] += flow->packets;
- ndpi_thread_info[thread_id].workflow->stats.protocol_counter_bytes[flow->detected_protocol.protocol] += flow->bytes;
- ndpi_thread_info[thread_id].workflow->stats.protocol_flows[flow->detected_protocol.protocol]++;
+ process_ndpi_collected_info(ndpi_thread_info[thread_id].workflow, flow);
+ ndpi_thread_info[thread_id].workflow->stats.protocol_counter[flow->detected_protocol.app_protocol] += flow->src2dst_packets + flow->dst2src_packets;
+ ndpi_thread_info[thread_id].workflow->stats.protocol_counter_bytes[flow->detected_protocol.app_protocol] += flow->src2dst_bytes + flow->dst2src_bytes;
+ ndpi_thread_info[thread_id].workflow->stats.protocol_flows[flow->detected_protocol.app_protocol]++;
+ }
+}
+
+/* *********************************************** */
+
+void updateScanners(struct single_flow_info **scanners, u_int32_t saddr,
+ u_int8_t version, u_int32_t dport) {
+ struct single_flow_info *f;
+ struct port_flow_info *p;
+
+ HASH_FIND_INT(*scanners, (int *)&saddr, f);
+
+ if(f == NULL) {
+ f = (struct single_flow_info*)malloc(sizeof(struct single_flow_info));
+ if(!f) return;
+ f->saddr = saddr;
+ f->version = version;
+ f->tot_flows = 1;
+ f->ports = NULL;
+
+ p = (struct port_flow_info*)malloc(sizeof(struct port_flow_info));
+
+ if(!p) {
+ free(f);
+ return;
+ } else
+ p->port = dport, p->num_flows = 1;
+
+ HASH_ADD_INT(f->ports, port, p);
+ HASH_ADD_INT(*scanners, saddr, f);
+ } else{
+ struct port_flow_info *pp;
+ f->tot_flows++;
+
+ HASH_FIND_INT(f->ports, (int *)&dport, pp);
+
+ if(pp == NULL) {
+ pp = (struct port_flow_info*)malloc(sizeof(struct port_flow_info));
+ if(!pp) return;
+ pp->port = dport, pp->num_flows = 1;
+
+ HASH_ADD_INT(f->ports, port, pp);
+ } else
+ pp->num_flows++;
+ }
+}
+
+/* *********************************************** */
+
+int updateIpTree(u_int32_t key, u_int8_t version,
+ addr_node **vrootp, const char *proto) {
+ addr_node *q;
+ addr_node **rootp = vrootp;
+
+ if(rootp == (addr_node **)0)
+ return 0;
+
+ while (*rootp != (addr_node *)0) {
+ /* Knuth's T1: */
+ if((version == (*rootp)->version) && (key == (*rootp)->addr)) {
+ /* T2: */
+ return ++((*rootp)->count);
+ }
+
+ rootp = (key < (*rootp)->addr) ?
+ &(*rootp)->left : /* T3: follow left branch */
+ &(*rootp)->right; /* T4: follow right branch */
+ }
+
+ q = (addr_node *) malloc(sizeof(addr_node)); /* T5: key not found */
+ if(q != (addr_node *)0) { /* make new node */
+ *rootp = q; /* link new node to old */
+
+ q->addr = key;
+ q->version = version;
+ strncpy(q->proto, proto, sizeof(q->proto));
+ q->count = UPDATED_TREE;
+ q->left = q->right = (addr_node *)0;
+
+ return q->count;
}
+
+ return(0);
+}
+/* *********************************************** */
+
+void freeIpTree(addr_node *root) {
+ if (root == NULL)
+ return;
+
+ freeIpTree(root->left);
+ freeIpTree(root->right);
+ free(root);
+ root = NULL;
}
+/* *********************************************** */
+
+void updateTopIpAddress(u_int32_t addr, u_int8_t version, const char *proto,
+ int count, struct info_pair top[], int size) {
+ struct info_pair pair;
+ int min = count;
+ int update = 0;
+ int min_i = 0;
+ int i;
+
+ if(count == 0) return;
+
+ pair.addr = addr;
+ pair.version = version;
+ pair.count = count;
+ strncpy(pair.proto, proto, sizeof(pair.proto));
+
+ for(i=0; i<size; i++) {
+ /* if the same ip with a bigger
+ count just update it */
+ if(top[i].addr == addr) {
+ top[i].count = count;
+ return;
+ }
+ /* if array is not full yet
+ add it to the first empty place */
+ if(top[i].count == 0) {
+ top[i] = pair;
+ return;
+ }
+ }
+
+ /* if bigger than the smallest one, replace it */
+ for(i=0; i<size; i++) {
+ if(top[i].count < count && top[i].count < min) {
+ min = top[i].count;
+ min_i = i;
+ update = 1;
+ }
+ }
+
+ if(update)
+ top[min_i] = pair;
+}
+
+/* *********************************************** */
+
+static void updatePortStats(struct port_stats **stats, u_int32_t port,
+ u_int32_t addr, u_int8_t version,
+ u_int32_t num_pkts, u_int32_t num_bytes,
+ const char *proto) {
+
+ struct port_stats *s = NULL;
+ int count = 0;
+
+ HASH_FIND_INT(*stats, &port, s);
+ if(s == NULL) {
+ s = (struct port_stats*)calloc(1, sizeof(struct port_stats));
+ if(!s) return;
+
+ s->port = port, s->num_pkts = num_pkts, s->num_bytes = num_bytes;
+ s->num_addr = 1, s->cumulative_addr = 1; s->num_flows = 1;
+
+ updateTopIpAddress(addr, version, proto, 1, s->top_ip_addrs, MAX_NUM_IP_ADDRESS);
+
+ s->addr_tree = (addr_node *) malloc(sizeof(addr_node));
+ if(!s->addr_tree) {
+ free(s);
+ return;
+ }
+
+ s->addr_tree->addr = addr;
+ s->addr_tree->version = version;
+ strncpy(s->addr_tree->proto, proto, sizeof(s->addr_tree->proto));
+ s->addr_tree->count = 1;
+ s->addr_tree->left = NULL;
+ s->addr_tree->right = NULL;
+
+ HASH_ADD_INT(*stats, port, s);
+ }
+ else{
+ count = updateIpTree(addr, version, &(*s).addr_tree, proto);
+
+ if(count == UPDATED_TREE) s->num_addr++;
+
+ if(count) {
+ s->cumulative_addr++;
+ updateTopIpAddress(addr, version, proto, count, s->top_ip_addrs, MAX_NUM_IP_ADDRESS);
+ }
+
+ s->num_pkts += num_pkts, s->num_bytes += num_bytes, s->num_flows++;
+ }
+}
+
+/* *********************************************** */
+
+/* @brief heuristic choice for receiver stats */
+static int acceptable(u_int32_t num_pkts){
+ return num_pkts > 5;
+}
+
+/* *********************************************** */
+
+static int receivers_sort(void *_a, void *_b) {
+ struct receiver *a = (struct receiver *)_a;
+ struct receiver *b = (struct receiver *)_b;
+
+ return(b->num_pkts - a->num_pkts);
+}
+
+/* *********************************************** */
+
+static int receivers_sort_asc(void *_a, void *_b) {
+ struct receiver *a = (struct receiver *)_a;
+ struct receiver *b = (struct receiver *)_b;
+
+ return(a->num_pkts - b->num_pkts);
+}
+
+/* ***************************************************** */
+/*@brief removes first (size - max) elements from hash table.
+ * hash table is ordered in ascending order.
+*/
+static struct receiver *cutBackTo(struct receiver **receivers, u_int32_t size, u_int32_t max) {
+ struct receiver *r, *tmp;
+ int i=0;
+ int count;
+
+ if(size < max) //return the original table
+ return *receivers;
+
+ count = size - max;
+
+ HASH_ITER(hh, *receivers, r, tmp) {
+ if(i++ == count)
+ return r;
+ HASH_DEL(*receivers, r);
+ free(r);
+ }
+
+ return(NULL);
+
+}
+
+/* *********************************************** */
+/*@brief merge first table to the second table.
+ * if element already in the second table
+ * then updates its value
+ * else adds it to the second table
+*/
+static void mergeTables(struct receiver **primary, struct receiver **secondary) {
+ struct receiver *r, *s, *tmp;
+
+ HASH_ITER(hh, *primary, r, tmp) {
+ HASH_FIND_INT(*secondary, (int *)&(r->addr), s);
+ if(s == NULL){
+ s = (struct receiver *)malloc(sizeof(struct receiver));
+ if(!s) return;
+
+ s->addr = r->addr;
+ s->version = r->version;
+ s->num_pkts = r->num_pkts;
+
+ HASH_ADD_INT(*secondary, addr, s);
+ }
+ else
+ s->num_pkts += r->num_pkts;
+
+ HASH_DEL(*primary, r);
+ free(r);
+ }
+}
+/* *********************************************** */
+
+static void deleteReceivers(struct receiver *receivers) {
+ struct receiver *current, *tmp;
+
+ HASH_ITER(hh, receivers, current, tmp) {
+ HASH_DEL(receivers, current);
+ free(current);
+ }
+}
+
+/* *********************************************** */
+/* implementation of: https://jeroen.massar.ch/presentations/files/FloCon2010-TopK.pdf
+ *
+ * if (table1.size < max1 || acceptable){
+ * create new element and add to the table1
+ * if (table1.size > max2) {
+ * cut table1 back to max1
+ * merge table 1 to table2
+ * if(table2.size > max1)
+ * cut table2 back to max1
+ * }
+ * }
+ * else
+ * update table1
+*/
+static void updateReceivers(struct receiver **receivers, u_int32_t dst_addr,
+ u_int8_t version, u_int32_t num_pkts,
+ struct receiver **topReceivers) {
+ struct receiver *r;
+ u_int32_t size;
+ int a;
+
+ HASH_FIND_INT(*receivers, (int *)&dst_addr, r);
+ if(r == NULL) {
+ if(((size = HASH_COUNT(*receivers)) < MAX_TABLE_SIZE_1)
+ || ((a = acceptable(num_pkts)) != 0)){
+ r = (struct receiver *)malloc(sizeof(struct receiver));
+ if(!r) return;
+
+ r->addr = dst_addr;
+ r->version = version;
+ r->num_pkts = num_pkts;
+
+ HASH_ADD_INT(*receivers, addr, r);
+
+ if((size = HASH_COUNT(*receivers)) > MAX_TABLE_SIZE_2){
+
+ HASH_SORT(*receivers, receivers_sort_asc);
+ *receivers = cutBackTo(receivers, size, MAX_TABLE_SIZE_1);
+ mergeTables(receivers, topReceivers);
+
+ if((size = HASH_COUNT(*topReceivers)) > MAX_TABLE_SIZE_1){
+ HASH_SORT(*topReceivers, receivers_sort_asc);
+ *topReceivers = cutBackTo(topReceivers, size, MAX_TABLE_SIZE_1);
+ }
+
+ *receivers = NULL;
+ }
+ }
+ }
+ else
+ r->num_pkts += num_pkts;
+}
+
+/* *********************************************** */
+
+#ifdef HAVE_JSON_C
+static void saveReceiverStats(json_object **jObj_group,
+ struct receiver **receivers,
+ u_int64_t total_pkt_count) {
+
+ json_object *jArray_stats = json_object_new_array();
+ struct receiver *r, *tmp;
+ int i = 0;
+
+ HASH_ITER(hh, *receivers, r, tmp) {
+ json_object *jObj_stat = json_object_new_object();
+ char addr_name[48];
+
+ if(r->version == IPVERSION)
+ inet_ntop(AF_INET, &(r->addr), addr_name, sizeof(addr_name));
+ else
+ inet_ntop(AF_INET6, &(r->addr), addr_name, sizeof(addr_name));
+
+
+ json_object_object_add(jObj_stat,"ip.address",json_object_new_string(addr_name));
+ json_object_object_add(jObj_stat,"packets.number", json_object_new_int(r->num_pkts));
+ json_object_object_add(jObj_stat,"packets.percent",json_object_new_double(((double)r->num_pkts) / total_pkt_count));
+
+ json_object_array_add(jArray_stats, jObj_stat);
+
+ i++;
+ if(i >= 10) break;
+ }
+
+ json_object_object_add(*jObj_group, "top.receiver.stats", jArray_stats);
+}
+#endif
+
+
+/* *********************************************** */
+
+static void deleteScanners(struct single_flow_info *scanners) {
+ struct single_flow_info *s, *tmp;
+ struct port_flow_info *p, *tmp2;
+
+ HASH_ITER(hh, scanners, s, tmp) {
+ HASH_ITER(hh, s->ports, p, tmp2) {
+ HASH_DEL(s->ports, p);
+ free(p);
+ }
+ HASH_DEL(scanners, s);
+ free(s);
+ }
+}
+
+/* *********************************************** */
+
+static void deletePortsStats(struct port_stats *stats) {
+ struct port_stats *current_port, *tmp;
+
+ HASH_ITER(hh, stats, current_port, tmp) {
+ HASH_DEL(stats, current_port);
+ freeIpTree(current_port->addr_tree);
+ free(current_port);
+ }
+}
+
+/* *********************************************** */
+
+/**
+ * @brief Ports stats
+ */
+static void port_stats_walker(const void *node, ndpi_VISIT which, int depth, void *user_data) {
+ if((which == ndpi_preorder) || (which == ndpi_leaf)) { /* Avoid walking the same node multiple times */
+ struct ndpi_flow_info *flow = *(struct ndpi_flow_info **) node;
+ u_int16_t thread_id = *(int *)user_data;
+ u_int16_t sport, dport;
+ char proto[16];
+ int r;
+
+ sport = ntohs(flow->src_port), dport = ntohs(flow->dst_port);
+
+ /* get app level protocol */
+ if(flow->detected_protocol.master_protocol)
+ ndpi_protocol2name(ndpi_thread_info[thread_id].workflow->ndpi_struct,
+ flow->detected_protocol, proto, sizeof(proto));
+ else
+ strncpy(proto, ndpi_get_proto_name(ndpi_thread_info[thread_id].workflow->ndpi_struct,
+ flow->detected_protocol.app_protocol),sizeof(proto));
+
+ if(((r = strcmp(ipProto2Name(flow->protocol), "TCP")) == 0)
+ && (flow->src2dst_packets == 1) && (flow->dst2src_packets == 0)) {
+ updateScanners(&scannerHosts, flow->src_ip, flow->ip_version, dport);
+ }
+
+ updateReceivers(&receivers, flow->dst_ip, flow->ip_version,
+ flow->src2dst_packets, &topReceivers);
+
+ updatePortStats(&srcStats, sport, flow->src_ip, flow->ip_version,
+ flow->src2dst_packets, flow->src2dst_bytes, proto);
+
+ updatePortStats(&dstStats, dport, flow->dst_ip, flow->ip_version,
+ flow->dst2src_packets, flow->dst2src_bytes, proto);
+ }
+}
+
+/* *********************************************** */
/**
* @brief Idle Scan Walker
@@ -539,7 +1349,7 @@ static void node_idle_scan_walker(const void *node, ndpi_VISIT which, int depth,
/* update stats */
node_proto_guess_walker(node, which, depth, user_data);
- if((flow->detected_protocol.protocol == NDPI_PROTOCOL_UNKNOWN) && !undetected_flows_deleted)
+ if((flow->detected_protocol.app_protocol == NDPI_PROTOCOL_UNKNOWN) && !undetected_flows_deleted)
undetected_flows_deleted = 1;
ndpi_free_flow_info_half(flow);
@@ -561,15 +1371,15 @@ static void on_protocol_discovered(struct ndpi_workflow * workflow,
const u_int16_t thread_id = (uintptr_t) udata;
- if(verbose > 1){
+ if(verbose > 1) {
if(enable_protocol_guess) {
- if(flow->detected_protocol.protocol == NDPI_PROTOCOL_UNKNOWN) {
- flow->detected_protocol.protocol = node_guess_undetected_protocol(thread_id, flow),
+ if(flow->detected_protocol.app_protocol == NDPI_PROTOCOL_UNKNOWN) {
+ flow->detected_protocol.app_protocol = node_guess_undetected_protocol(thread_id, flow),
flow->detected_protocol.master_protocol = NDPI_PROTOCOL_UNKNOWN;
}
}
- printFlow(thread_id, flow);
+ // printFlow(thread_id, flow);
}
}
@@ -586,7 +1396,7 @@ static void debug_printf(u_int32_t protocol, void *id_struct,
struct tm result;
#endif
- if(log_level <= nDPI_traceLevel) {
+ if(log_level <= nDPI_LogLevel) {
char buf[8192], out_buf[8192];
char theDate[32];
const char *extra_msg = "";
@@ -656,7 +1466,6 @@ static void setupDetection(u_int16_t thread_id, pcap_t * pcap_handle) {
* @brief End of detection and free flow
*/
static void terminateDetection(u_int16_t thread_id) {
-
ndpi_workflow_free(ndpi_thread_info[thread_id].workflow);
}
@@ -722,15 +1531,35 @@ char* formatPackets(float numPkts, char *buf) {
static void json_init() {
jArray_known_flows = json_object_new_array();
jArray_unknown_flows = json_object_new_array();
+ jArray_topStats = json_object_new_array();
+}
+
+static void json_open_stats_file() {
+ if((file_first_time && ((stats_fp = fopen(_statsFilePath,"w")) == NULL))
+ ||
+ (!file_first_time && (stats_fp = fopen(_statsFilePath,"a")) == NULL)) {
+ printf("Error creating/opening file %s\n", _statsFilePath);
+ stats_flag = 0;
+ }
+ else file_first_time = 0;
+}
+
+static void json_close_stats_file() {
+ json_object *jObjFinal = json_object_new_object();
+ json_object_object_add(jObjFinal,"duration.in.seconds",json_object_new_int(pcap_analysis_duration));
+ json_object_object_add(jObjFinal,"statistics", jArray_topStats);
+ fprintf(stats_fp,"%s\n",json_object_to_json_string(jObjFinal));
+ fclose(stats_fp);
+ json_object_put(jObjFinal);
}
#endif
+/* *********************************************** */
/**
* @brief Bytes stats format
*/
char* formatBytes(u_int32_t howMuch, char *buf, u_int buf_len) {
-
char unit = 'B';
if(howMuch < 1024) {
@@ -752,12 +1581,252 @@ char* formatBytes(u_int32_t howMuch, char *buf, u_int buf_len) {
return(buf);
}
+/* *********************************************** */
+
+static int port_stats_sort(void *_a, void *_b) {
+ struct port_stats *a = (struct port_stats*)_a;
+ struct port_stats *b = (struct port_stats*)_b;
+
+ if(b->num_pkts == 0 && a->num_pkts == 0)
+ return(b->num_flows - a->num_flows);
+
+ return(b->num_pkts - a->num_pkts);
+}
+
+/* *********************************************** */
+
+#ifdef HAVE_JSON_C
+static int scanners_sort(void *_a, void *_b) {
+ struct single_flow_info *a = (struct single_flow_info *)_a;
+ struct single_flow_info *b = (struct single_flow_info *)_b;
+
+ return(b->tot_flows - a->tot_flows);
+}
+
+/* *********************************************** */
+
+static int scanners_port_sort(void *_a, void *_b) {
+ struct port_flow_info *a = (struct port_flow_info *)_a;
+ struct port_flow_info *b = (struct port_flow_info *)_b;
+
+ return(b->num_flows - a->num_flows);
+}
+
+#endif
+/* *********************************************** */
+
+static int info_pair_cmp (const void *_a, const void *_b)
+{
+ struct info_pair *a = (struct info_pair *)_a;
+ struct info_pair *b = (struct info_pair *)_b;
+
+ return b->count - a->count;
+}
+
+/* *********************************************** */
+
+#ifdef HAVE_JSON_C
+static int top_stats_sort(void *_a, void *_b) {
+ struct port_stats *a = (struct port_stats*)_a;
+ struct port_stats *b = (struct port_stats*)_b;
+
+ return(b->num_addr - a->num_addr);
+}
+
+/* *********************************************** */
+
+/**
+ * @brief Get port based top statistics
+ */
+static int getTopStats(struct port_stats *stats) {
+ struct port_stats *sp, *tmp;
+ struct info_pair inf;
+ u_int64_t total_ip_addrs = 0;
+
+ HASH_ITER(hh, stats, sp, tmp) {
+ qsort(sp->top_ip_addrs, MAX_NUM_IP_ADDRESS, sizeof(struct info_pair), info_pair_cmp);
+ inf = sp->top_ip_addrs[0];
+
+ if(((inf.count * 100.0)/sp->cumulative_addr) > AGGRESSIVE_PERCENT) {
+ sp->hasTopHost = 1;
+ sp->top_host = inf.addr;
+ sp->version = inf.version;
+ strncpy(sp->proto, inf.proto, sizeof(sp->proto));
+ } else
+ sp->hasTopHost = 0;
+
+ total_ip_addrs += sp->num_addr;
+ }
+
+ return total_ip_addrs;
+}
+
+/* *********************************************** */
+
+static void saveScannerStats(json_object **jObj_group, struct single_flow_info **scanners) {
+ struct single_flow_info *s, *tmp;
+ struct port_flow_info *p, *tmp2;
+ char addr_name[48];
+ int i = 0, j = 0;
+
+ json_object *jArray_stats = json_object_new_array();
+
+ HASH_SORT(*scanners, scanners_sort); // FIX
+
+ HASH_ITER(hh, *scanners, s, tmp) {
+ json_object *jObj_stat = json_object_new_object();
+ json_object *jArray_ports = json_object_new_array();
+
+ if(s->version == IPVERSION)
+ inet_ntop(AF_INET, &(s->saddr), addr_name, sizeof(addr_name));
+ else
+ inet_ntop(AF_INET6, &(s->saddr), addr_name, sizeof(addr_name));
+
+ json_object_object_add(jObj_stat,"ip.address",json_object_new_string(addr_name));
+ json_object_object_add(jObj_stat,"total.flows.number",json_object_new_int(s->tot_flows));
+
+ HASH_SORT(s->ports, scanners_port_sort);
+
+ HASH_ITER(hh, s->ports, p, tmp2) {
+ json_object *jObj_port = json_object_new_object();
+
+ json_object_object_add(jObj_port,"port",json_object_new_int(p->port));
+ json_object_object_add(jObj_port,"flows.number",json_object_new_int(p->num_flows));
+
+ json_object_array_add(jArray_ports, jObj_port);
+
+ j++;
+ if(j >= 10) break;
+ }
+
+ json_object_object_add(jObj_stat,"top.dst.ports",jArray_ports);
+ json_object_array_add(jArray_stats, jObj_stat);
+
+ j = 0;
+ i++;
+ if(i >= 10) break;
+ }
+
+ json_object_object_add(*jObj_group, "top.scanner.stats", jArray_stats);
+}
+
+#endif
+
+/* *********************************************** */
+
+#ifdef HAVE_JSON_C
+/*
+ * @brief Save Top Stats in json format
+ */
+static void saveTopStats(json_object **jObj_group,
+ struct port_stats **stats,
+ u_int8_t direction,
+ u_int64_t total_flow_count,
+ u_int64_t total_ip_addr) {
+ struct port_stats *s, *tmp;
+ char addr_name[48];
+ int i = 0;
+
+ json_object *jArray_stats = json_object_new_array();
+
+
+ HASH_ITER(hh, *stats, s, tmp) {
+
+ if((s->hasTopHost)) {
+ json_object *jObj_stat = json_object_new_object();
+
+ json_object_object_add(jObj_stat,"port",json_object_new_int(s->port));
+ json_object_object_add(jObj_stat,"packets.number",json_object_new_int(s->num_pkts));
+ json_object_object_add(jObj_stat,"flows.number",json_object_new_int(s->num_flows));
+ json_object_object_add(jObj_stat,"flows.percent",json_object_new_double((s->num_flows*100.0)/total_flow_count));
+ if(s->num_pkts) json_object_object_add(jObj_stat,"flows/packets",
+ json_object_new_double(((double)s->num_flows)/s->num_pkts));
+ else json_object_object_add(jObj_stat,"flows.num_packets",json_object_new_double(0.0));
+
+ if(s->version == IPVERSION) {
+ inet_ntop(AF_INET, &(s->top_host), addr_name, sizeof(addr_name));
+ } else {
+ inet_ntop(AF_INET6, &(s->top_host), addr_name, sizeof(addr_name));
+ }
+
+ json_object_object_add(jObj_stat,"aggressive.host",json_object_new_string(addr_name));
+ json_object_object_add(jObj_stat,"host.app.protocol",json_object_new_string(s->proto));
+
+ json_object_array_add(jArray_stats, jObj_stat);
+ i++;
+
+ if(i >= 10) break;
+ }
+ }
+
+ json_object_object_add(*jObj_group, (direction == DIR_SRC) ?
+ "top.src.pkts.stats" : "top.dst.pkts.stats", jArray_stats);
+
+ jArray_stats = json_object_new_array();
+ i=0;
+
+ /*sort top stats by ip addr count*/
+ HASH_SORT(*stats, top_stats_sort);
+
+
+ HASH_ITER(hh, *stats, s, tmp) {
+ json_object *jObj_stat = json_object_new_object();
+
+ json_object_object_add(jObj_stat,"port",json_object_new_int(s->port));
+ json_object_object_add(jObj_stat,"host.number",json_object_new_int64(s->num_addr));
+ json_object_object_add(jObj_stat,"host.percent",json_object_new_double((s->num_addr*100.0)/total_ip_addr));
+ json_object_object_add(jObj_stat,"flows.number",json_object_new_int(s->num_flows));
+
+ json_object_array_add(jArray_stats,jObj_stat);
+ i++;
+
+ if(i >= 10) break;
+ }
+
+ json_object_object_add(*jObj_group, (direction == DIR_SRC) ?
+ "top.src.host.stats" : "top.dst.host.stats", jArray_stats);
+}
+#endif
+
+/* *********************************************** */
+
+void printPortStats(struct port_stats *stats) {
+ struct port_stats *s, *tmp;
+ char addr_name[48];
+ int i = 0, j = 0;
+
+ HASH_ITER(hh, stats, s, tmp) {
+ i++;
+ printf("\t%2d\tPort %5u\t[%u IP address(es)/%u flows/%u pkts/%u bytes]\n\t\tTop IP Stats:\n",
+ i, s->port, s->num_addr, s->num_flows, s->num_pkts, s->num_bytes);
+
+ qsort(&s->top_ip_addrs[0], MAX_NUM_IP_ADDRESS, sizeof(struct info_pair), info_pair_cmp);
+
+ for(j=0; j<MAX_NUM_IP_ADDRESS; j++) {
+ if(s->top_ip_addrs[j].count != 0) {
+ if(s->top_ip_addrs[j].version == IPVERSION) {
+ inet_ntop(AF_INET, &(s->top_ip_addrs[j].addr), addr_name, sizeof(addr_name));
+ } else {
+ inet_ntop(AF_INET6, &(s->top_ip_addrs[j].addr), addr_name, sizeof(addr_name));
+ }
+
+ printf("\t\t%-36s ~ %.2f%%\n", addr_name,
+ ((s->top_ip_addrs[j].count) * 100.0) / s->cumulative_addr);
+ }
+ }
+
+ printf("\n");
+ if(i >= 10) break;
+ }
+}
+
+
+/* *********************************************** */
/**
* @brief Print result
*/
static void printResults(u_int64_t tot_usec) {
-
u_int32_t i;
u_int64_t total_flow_bytes = 0;
u_int32_t avg_pkt_size = 0;
@@ -777,8 +1846,10 @@ static void printResults(u_int64_t tot_usec) {
&& (ndpi_thread_info[thread_id].workflow->stats.raw_packet_count == 0))
continue;
- for(i=0; i<NUM_ROOTS; i++)
+ for(i=0; i<NUM_ROOTS; i++) {
ndpi_twalk(ndpi_thread_info[thread_id].workflow->ndpi_flows_root[i], node_proto_guess_walker, &thread_id);
+ if(verbose == 3 || stats_flag) ndpi_twalk(ndpi_thread_info[thread_id].workflow->ndpi_flows_root[i], port_stats_walker, &thread_id);
+ }
/* Stats aggregation */
cumulative_stats.guessed_flow_protocols += ndpi_thread_info[thread_id].workflow->stats.guessed_flow_protocols;
@@ -806,6 +1877,9 @@ static void printResults(u_int64_t tot_usec) {
cumulative_stats.max_packet_len += ndpi_thread_info[thread_id].workflow->stats.max_packet_len;
}
+ if(cumulative_stats.total_wire_bytes == 0)
+ goto free_stats;
+
if(!quiet_mode) {
printf("\nnDPI Memory statistics:\n");
printf("\tnDPI Memory (once): %-13s\n", formatBytes(sizeof(struct ndpi_detection_module_struct), buf, sizeof(buf)));
@@ -844,15 +1918,20 @@ static void printResults(u_int64_t tot_usec) {
printf("\tPacket Len > 1500: %-13lu\n", (unsigned long)cumulative_stats.packet_len[5]);
if(tot_usec > 0) {
- char buf[32], buf1[32];
+ char buf[32], buf1[32], when[64];
float t = (float)(cumulative_stats.ip_packet_count*1000000)/(float)tot_usec;
float b = (float)(cumulative_stats.total_wire_bytes * 8 *1000000)/(float)tot_usec;
float traffic_duration;
- if (live_capture) traffic_duration = tot_usec;
+ if(live_capture) traffic_duration = tot_usec;
else traffic_duration = (pcap_end.tv_sec*1000000 + pcap_end.tv_usec) - (pcap_start.tv_sec*1000000 + pcap_start.tv_usec);
printf("\tnDPI throughput: %s pps / %s/sec\n", formatPackets(t, buf), formatTraffic(b, 1, buf1));
t = (float)(cumulative_stats.ip_packet_count*1000000)/(float)traffic_duration;
b = (float)(cumulative_stats.total_wire_bytes * 8 *1000000)/(float)traffic_duration;
+
+ strftime(when, sizeof(when), "%d/%b/%Y %H:%M:%S", localtime(&pcap_start.tv_sec));
+ printf("\tAnalysis begin: %s\n", when);
+ strftime(when, sizeof(when), "%d/%b/%Y %H:%M:%S", localtime(&pcap_end.tv_sec));
+ printf("\tAnalysis end: %s\n", when);
printf("\tTraffic throughput: %s pps / %s/sec\n", formatPackets(t, buf), formatTraffic(b, 1, buf1));
printf("\tTraffic duration: %.3f sec\n", traffic_duration/1000000);
}
@@ -865,7 +1944,7 @@ static void printResults(u_int64_t tot_usec) {
if(json_flag) {
#ifdef HAVE_JSON_C
if((json_fp = fopen(_jsonFilePath,"w")) == NULL) {
- printf("Error createing .json file %s\n", _jsonFilePath);
+ printf("Error creating .json file %s\n", _jsonFilePath);
json_flag = 0;
} else {
jObj_main = json_object_new_object();
@@ -954,8 +2033,17 @@ static void printResults(u_int64_t tot_usec) {
// printf("\n\nTotal Flow Traffic: %llu (diff: %llu)\n", total_flow_bytes, cumulative_stats.total_ip_bytes-total_flow_bytes);
- if(verbose) {
+ if((verbose == 1) || (verbose == 2)) {
FILE *out = results_file ? results_file : stdout;
+ u_int32_t total_flows = 0;
+
+ for(thread_id = 0; thread_id < num_threads; thread_id++)
+ total_flows += ndpi_thread_info[thread_id].workflow->num_allocated_flows;
+
+ if((all_flows = (struct flow_info*)malloc(sizeof(struct flow_info)*total_flows)) == NULL) {
+ printf("Fatal error: not enough memory\n");
+ exit(-1);
+ }
if(!json_flag) fprintf(out, "\n");
@@ -965,6 +2053,11 @@ static void printResults(u_int64_t tot_usec) {
ndpi_twalk(ndpi_thread_info[thread_id].workflow->ndpi_flows_root[i], node_print_known_proto_walker, &thread_id);
}
+ qsort(all_flows, num_flows, sizeof(struct flow_info), cmpFlows);
+
+ for(i=0; i<num_flows; i++)
+ printFlow(i+1, all_flows[i].flow, all_flows[i].thread_id);
+
for(thread_id = 0; thread_id < num_threads; thread_id++) {
if(ndpi_thread_info[thread_id].workflow->stats.protocol_counter[0 /* 0 = Unknown */] > 0) {
if(!json_flag) {
@@ -986,6 +2079,13 @@ static void printResults(u_int64_t tot_usec) {
ndpi_twalk(ndpi_thread_info[thread_id].workflow->ndpi_flows_root[i], node_print_unknown_proto_walker, &thread_id);
}
}
+
+ qsort(all_flows, num_flows, sizeof(struct flow_info), cmpFlows);
+
+ for(i=0; i<num_flows; i++)
+ printFlow(i+1, all_flows[i].flow, all_flows[i].thread_id);
+
+ free(all_flows);
}
if(json_flag != 0) {
@@ -1000,21 +2100,89 @@ static void printResults(u_int64_t tot_usec) {
fclose(json_fp);
#endif
}
-}
+ if(stats_flag || verbose == 3) {
+ HASH_SORT(srcStats, port_stats_sort);
+ HASH_SORT(dstStats, port_stats_sort);
+ }
+
+ if(verbose == 3) {
+ printf("\n\nSource Ports Stats:\n");
+ printPortStats(srcStats);
+
+ printf("\nDestination Ports Stats:\n");
+ printPortStats(dstStats);
+ }
+
+ if(stats_flag) {
+#ifdef HAVE_JSON_C
+ json_object *jObj_stats = json_object_new_object();
+ char timestamp[64];
+ int count;
+
+ strftime(timestamp, sizeof(timestamp), "%FT%TZ", localtime(&pcap_start.tv_sec));
+ json_object_object_add(jObj_stats, "time", json_object_new_string(timestamp));
+
+ saveScannerStats(&jObj_stats, &scannerHosts);
+
+ if((count = HASH_COUNT(topReceivers)) == 0){
+ HASH_SORT(receivers, receivers_sort);
+ saveReceiverStats(&jObj_stats, &receivers, cumulative_stats.ip_packet_count);
+ }
+ else{
+ HASH_SORT(topReceivers, receivers_sort);
+ saveReceiverStats(&jObj_stats, &topReceivers, cumulative_stats.ip_packet_count);
+ }
+
+ u_int64_t total_src_addr = getTopStats(srcStats);
+ u_int64_t total_dst_addr = getTopStats(dstStats);
+
+ saveTopStats(&jObj_stats, &srcStats, DIR_SRC,
+ cumulative_stats.ndpi_flow_count, total_src_addr);
+
+ saveTopStats(&jObj_stats, &dstStats, DIR_DST,
+ cumulative_stats.ndpi_flow_count, total_dst_addr);
+
+ json_object_array_add(jArray_topStats, jObj_stats);
+#endif
+ }
+
+ free_stats:
+ if(scannerHosts) {
+ deleteScanners(scannerHosts);
+ scannerHosts = NULL;
+ }
+
+ if(receivers){
+ deleteReceivers(receivers);
+ receivers = NULL;
+ }
+
+ if(topReceivers){
+ deleteReceivers(topReceivers);
+ topReceivers = NULL;
+ }
+
+ if(srcStats) {
+ deletePortsStats(srcStats);
+ srcStats = NULL;
+ }
+
+ if(dstStats) {
+ deletePortsStats(dstStats);
+ dstStats = NULL;
+ }
+}
/**
* @brief Force a pcap_dispatch() or pcap_loop() call to return
*/
static void breakPcapLoop(u_int16_t thread_id) {
-
if(ndpi_thread_info[thread_id].workflow->pcap_handle != NULL) {
pcap_breakloop(ndpi_thread_info[thread_id].workflow->pcap_handle);
}
}
-
-
/**
* @brief Sigproc is executed for each packet in the pcap file
*/
@@ -1060,16 +2228,16 @@ static int getNextPcapFileFromPlaylist(u_int16_t thread_id, char filename[], u_i
*/
static void configurePcapHandle(pcap_t * pcap_handle) {
- if(_bpf_filter != NULL) {
+ if(bpfFilter != NULL) {
struct bpf_program fcode;
- if(pcap_compile(pcap_handle, &fcode, _bpf_filter, 1, 0xFFFFFF00) < 0) {
+ if(pcap_compile(pcap_handle, &fcode, bpfFilter, 1, 0xFFFFFF00) < 0) {
printf("pcap_compile error: '%s'\n", pcap_geterr(pcap_handle));
} else {
if(pcap_setfilter(pcap_handle, &fcode) < 0) {
printf("pcap_setfilter error: '%s'\n", pcap_geterr(pcap_handle));
} else
- printf("Successfully set BPF filter to '%s'\n", _bpf_filter);
+ printf("Successfully set BPF filter to '%s'\n", bpfFilter);
}
}
}
@@ -1086,7 +2254,8 @@ static pcap_t * openPcapFileOrDevice(u_int16_t thread_id, const u_char * pcap_fi
pcap_t * pcap_handle = NULL;
/* trying to open a live interface */
- if((pcap_handle = pcap_open_live((char*)pcap_file, snaplen, promisc, 500, pcap_error_buffer)) == NULL) {
+ if((pcap_handle = pcap_open_live((char*)pcap_file, snaplen, promisc,
+ 500, pcap_error_buffer)) == NULL) {
capture_for = capture_until = 0;
live_capture = 0;
@@ -1094,30 +2263,34 @@ static pcap_t * openPcapFileOrDevice(u_int16_t thread_id, const u_char * pcap_fi
/* trying to open a pcap file */
if((pcap_handle = pcap_open_offline((char*)pcap_file, pcap_error_buffer)) == NULL) {
- char filename[256];
-
- /* trying to open a pcap playlist */
- if(getNextPcapFileFromPlaylist(thread_id, filename, sizeof(filename)) != 0 ||
- (pcap_handle = pcap_open_offline(filename, pcap_error_buffer)) == NULL) {
+ char filename[256] = { 0 };
- printf("ERROR: could not open pcap file or playlist: %s\n", pcap_error_buffer);
+ if(strstr((char*)pcap_file, (char*)".pcap"))
+ printf("ERROR: could not open pcap file %s: %s\n", pcap_file, pcap_error_buffer);
+ else if((getNextPcapFileFromPlaylist(thread_id, filename, sizeof(filename)) != 0)
+ || ((pcap_handle = pcap_open_offline(filename, pcap_error_buffer)) == NULL)) {
+ printf("ERROR: could not open playlist %s: %s\n", filename, pcap_error_buffer);
exit(-1);
} else {
- if((!json_flag) && (!quiet_mode)) printf("Reading packets from playlist %s...\n", pcap_file);
+ if((!json_flag) && (!quiet_mode))
+ printf("Reading packets from playlist %s...\n", pcap_file);
}
} else {
- if((!json_flag) && (!quiet_mode)) printf("Reading packets from pcap file %s...\n", pcap_file);
+ if((!json_flag) && (!quiet_mode))
+ printf("Reading packets from pcap file %s...\n", pcap_file);
}
} else {
live_capture = 1;
- if((!json_flag) && (!quiet_mode)) printf("Capturing live traffic from device %s...\n", pcap_file);
+ if((!json_flag) && (!quiet_mode))
+ printf("Capturing live traffic from device %s...\n", pcap_file);
}
configurePcapHandle(pcap_handle);
if(capture_for > 0) {
- if((!json_flag) && (!quiet_mode)) printf("Capturing traffic up to %u seconds\n", (unsigned int)capture_for);
+ if((!json_flag) && (!quiet_mode))
+ printf("Capturing traffic up to %u seconds\n", (unsigned int)capture_for);
#ifndef WIN32
alarm(capture_for);
@@ -1128,20 +2301,20 @@ static pcap_t * openPcapFileOrDevice(u_int16_t thread_id, const u_char * pcap_fi
return pcap_handle;
}
-
/**
* @brief Check pcap packet
*/
-static void pcap_packet_callback_checked(u_char *args,
- const struct pcap_pkthdr *header,
- const u_char *packet) {
-
+static void pcap_process_packet(u_char *args,
+ const struct pcap_pkthdr *header,
+ const u_char *packet) {
+ struct ndpi_proto p;
u_int16_t thread_id = *((u_int16_t*)args);
/* allocate an exact size buffer to check overflows */
uint8_t *packet_checked = malloc(header->caplen);
+
memcpy(packet_checked, packet, header->caplen);
- ndpi_workflow_process_packet(ndpi_thread_info[thread_id].workflow, header, packet_checked);
+ p = ndpi_workflow_process_packet(ndpi_thread_info[thread_id].workflow, header, packet_checked);
if((capture_until != 0) && (header->ts.tv_sec >= capture_until)) {
if(ndpi_thread_info[thread_id].workflow->pcap_handle != NULL)
@@ -1149,11 +2322,8 @@ static void pcap_packet_callback_checked(u_char *args,
return;
}
- /* Check if capture is live or not */
- if (!live_capture) {
- if (!pcap_start.tv_sec) pcap_start.tv_sec = header->ts.tv_sec, pcap_start.tv_usec = header->ts.tv_usec;
- pcap_end.tv_sec = header->ts.tv_sec, pcap_end.tv_usec = header->ts.tv_usec;
- }
+ if(!pcap_start.tv_sec) pcap_start.tv_sec = header->ts.tv_sec, pcap_start.tv_usec = header->ts.tv_usec;
+ pcap_end.tv_sec = header->ts.tv_sec, pcap_end.tv_usec = header->ts.tv_usec;
/* Idle flows cleanup */
if(live_capture) {
@@ -1179,11 +2349,73 @@ static void pcap_packet_callback_checked(u_char *args,
}
}
+#ifdef DEBUG_TRACE
+ if(trace) fprintf(trace, "Found %u bytes packet %u.%u\n", header->caplen, p.app_protocol, p.master_protocol);
+#endif
+
+ if(extcap_dumper
+ && ((extcap_packet_filter == (u_int16_t)-1)
+ || (p.app_protocol == extcap_packet_filter)
+ || (p.master_protocol == extcap_packet_filter)
+ )
+ ) {
+ struct pcap_pkthdr h;
+ uint32_t *crc, delta = sizeof(struct ndpi_packet_trailer) + 4 /* ethernet trailer */;
+ struct ndpi_packet_trailer *trailer;
+
+ memcpy(&h, header, sizeof(h));
+
+ if(h.caplen > (sizeof(extcap_buf)-sizeof(struct ndpi_packet_trailer) - 4)) {
+ printf("INTERNAL ERROR: caplen=%u\n", h.caplen);
+ h.caplen = sizeof(extcap_buf)-sizeof(struct ndpi_packet_trailer) - 4;
+ }
+
+ trailer = (struct ndpi_packet_trailer*)&extcap_buf[h.caplen];
+ memcpy(extcap_buf, packet, h.caplen);
+ memset(trailer, 0, sizeof(struct ndpi_packet_trailer));
+ trailer->magic = htonl(0x19680924);
+ trailer->master_protocol = htons(p.master_protocol), trailer->app_protocol = htons(p.app_protocol);
+ ndpi_protocol2name(ndpi_thread_info[thread_id].workflow->ndpi_struct, p, trailer->name, sizeof(trailer->name));
+ crc = (uint32_t*)&extcap_buf[h.caplen+sizeof(struct ndpi_packet_trailer)];
+ *crc = ethernet_crc32((const void*)extcap_buf, h.caplen+sizeof(struct ndpi_packet_trailer));
+ h.caplen += delta;
+ h.len += delta;
+
+#ifdef DEBUG_TRACE
+ if(trace) fprintf(trace, "Dumping %u bytes packet\n", h.caplen);
+#endif
+
+ pcap_dump((u_char*)extcap_dumper, &h, (const u_char *)extcap_buf);
+ pcap_dump_flush(extcap_dumper);
+ }
+
/* check for buffer changes */
if(memcmp(packet, packet_checked, header->caplen) != 0)
- printf("INTERNAL ERROR: ingress packet was nodified by nDPI: this should not happen [thread_id=%u, packetId=%lu]\n",
- thread_id, (unsigned long)ndpi_thread_info[thread_id].workflow->stats.raw_packet_count);
+ printf("INTERNAL ERROR: ingress packet was modified by nDPI: this should not happen [thread_id=%u, packetId=%lu, caplen=%u]\n",
+ thread_id, (unsigned long)ndpi_thread_info[thread_id].workflow->stats.raw_packet_count, header->caplen);
free(packet_checked);
+
+ if((pcap_end.tv_sec-pcap_start.tv_sec) > pcap_analysis_duration) {
+ int i;
+ u_int64_t tot_usec;
+
+ gettimeofday(&end, NULL);
+ tot_usec = end.tv_sec*1000000 + end.tv_usec - (begin.tv_sec*1000000 + begin.tv_usec);
+
+ printResults(tot_usec);
+
+ for(i=0; i<ndpi_thread_info[thread_id].workflow->prefs.num_roots; i++) {
+ ndpi_tdestroy(ndpi_thread_info[thread_id].workflow->ndpi_flows_root[i], ndpi_flow_info_freer);
+ ndpi_thread_info[thread_id].workflow->ndpi_flows_root[i] = NULL;
+
+ memset(&ndpi_thread_info[thread_id].workflow->stats, 0, sizeof(struct ndpi_stats));
+ }
+
+ printf("\n-------------------------------------------\n\n");
+
+ memcpy(&begin, &end, sizeof(begin));
+ memcpy(&pcap_start, &pcap_end, sizeof(pcap_start));
+ }
}
@@ -1191,12 +2423,10 @@ static void pcap_packet_callback_checked(u_char *args,
* @brief Call pcap_loop() to process packets from a live capture or savefile
*/
static void runPcapLoop(u_int16_t thread_id) {
-
if((!shutdown_app) && (ndpi_thread_info[thread_id].workflow->pcap_handle != NULL))
- pcap_loop(ndpi_thread_info[thread_id].workflow->pcap_handle, -1, &pcap_packet_callback_checked, (u_char*)&thread_id);
+ pcap_loop(ndpi_thread_info[thread_id].workflow->pcap_handle, -1, &pcap_process_packet, (u_char*)&thread_id);
}
-
/**
* @brief Process a running thread
*/
@@ -1241,29 +2471,57 @@ void * processing_thread(void *_thread_id) {
* @brief Begin, process, end detection process
*/
void test_lib() {
-
- struct timeval begin, end;
+ struct timeval end;
u_int64_t tot_usec;
long thread_id;
#ifdef HAVE_JSON_C
json_init();
+ if(stats_flag) json_open_stats_file();
+#endif
+
+#ifdef DEBUG_TRACE
+ if(trace) fprintf(trace, "Num threads: %d\n", num_threads);
#endif
for(thread_id = 0; thread_id < num_threads; thread_id++) {
- pcap_t * cap = openPcapFileOrDevice(thread_id, (const u_char*)_pcap_file[thread_id]);
+ pcap_t *cap;
+
+#ifdef DEBUG_TRACE
+ if(trace) fprintf(trace, "Opening %s\n", (const u_char*)_pcap_file[thread_id]);
+#endif
+
+ cap = openPcapFileOrDevice(thread_id, (const u_char*)_pcap_file[thread_id]);
setupDetection(thread_id, cap);
}
gettimeofday(&begin, NULL);
- /* Running processing threads */
- for(thread_id = 0; thread_id < num_threads; thread_id++)
- pthread_create(&ndpi_thread_info[thread_id].pthread, NULL, processing_thread, (void *) thread_id);
+ int status;
+ void * thd_res;
+ /* Running processing threads */
+ for(thread_id = 0; thread_id < num_threads; thread_id++) {
+ status = pthread_create(&ndpi_thread_info[thread_id].pthread, NULL, processing_thread, (void *) thread_id);
+ /* check pthreade_create return value */
+ if(status != 0) {
+ fprintf(stderr, "error on create %ld thread\n", thread_id);
+ exit(-1);
+ }
+ }
/* Waiting for completion */
- for(thread_id = 0; thread_id < num_threads; thread_id++)
- pthread_join(ndpi_thread_info[thread_id].pthread, NULL);
+ for(thread_id = 0; thread_id < num_threads; thread_id++) {
+ status = pthread_join(ndpi_thread_info[thread_id].pthread, &thd_res);
+ /* check pthreade_join return value */
+ if(status != 0) {
+ fprintf(stderr, "error on join %ld thread\n", thread_id);
+ exit(-1);
+ }
+ if(thd_res != NULL) {
+ fprintf(stderr, "error on returned value of %ld joined thread\n", thread_id);
+ exit(-1);
+ }
+ }
gettimeofday(&end, NULL);
tot_usec = end.tv_sec*1000000 + end.tv_usec - (begin.tv_sec*1000000 + begin.tv_usec);
@@ -1271,10 +2529,16 @@ void test_lib() {
/* Printing cumulative results */
printResults(tot_usec);
+ if(stats_flag) {
+#ifdef HAVE_JSON_C
+ json_close_stats_file();
+#endif
+ }
+
for(thread_id = 0; thread_id < num_threads; thread_id++) {
- if(ndpi_thread_info[thread_id].workflow->pcap_handle != NULL) {
+ if(ndpi_thread_info[thread_id].workflow->pcap_handle != NULL)
pcap_close(ndpi_thread_info[thread_id].workflow->pcap_handle);
- }
+
terminateDetection(thread_id);
}
}
@@ -1291,21 +2555,609 @@ void automataUnitTest() {
ndpi_free_automa(automa);
}
+/* *********************************************** */
+/**
+ * @brief Produce bpf filter to filter ports and hosts
+ * in order to remove a peak in terms of number of packets
+ * sent by source hosts.
+ */
+#ifdef HAVE_JSON_C
+void bpf_filter_pkt_peak_filter(json_object **jObj_bpfFilter,
+ int port_array[], int p_size,
+ const char *src_host_array[16],
+ int sh_size,
+ const char *dst_host_array[16],
+ int dh_size) {
+ char filter[2048];
+ int produced = 0;
+ int i = 0;
+
+ if(port_array[0] != INIT_VAL) {
+ int l;
+
+ strcpy(filter, "not (src port ");
+
+ while(i < p_size && port_array[i] != INIT_VAL) {
+ l = strlen(filter);
+
+ if(i+1 == p_size || port_array[i+1] == INIT_VAL)
+ snprintf(&filter[l], sizeof(filter)-l, "%d", port_array[i]);
+ else
+ snprintf(&filter[l], sizeof(filter)-l, "%d or ", port_array[i]);
+ i++;
+ }
+
+ l = strlen(filter);
+ snprintf(&filter[l], sizeof(filter)-l, "%s", ")");
+ produced = 1;
+ }
+
+
+ if(src_host_array[0] != NULL) {
+ int l;
+
+ if(port_array[0] != INIT_VAL)
+ strncat(filter, " and not (src ", sizeof(" and not (src "));
+ else
+ strcpy(filter, "not (src ");
+
+
+ i=0;
+ while(i < sh_size && src_host_array[i] != NULL) {
+ l = strlen(filter);
+
+ if(i+1 == sh_size || src_host_array[i+1] == NULL)
+ snprintf(&filter[l], sizeof(filter)-l, "%s", src_host_array[i]);
+ else
+ snprintf(&filter[l], sizeof(filter)-l, "%s or ", src_host_array[i]);
+
+ i++;
+ }
+
+ l = strlen(filter);
+ snprintf(&filter[l], sizeof(filter)-l, "%s", ")");
+ produced = 1;
+ }
+
+
+ if(dst_host_array[0] != NULL) {
+ int l;
+
+ if(port_array[0] != INIT_VAL || src_host_array[0] != NULL)
+ strncat(filter, " and not (dst ", sizeof(" and not (dst "));
+ else
+ strcpy(filter, "not (dst ");
+
+ i=0;
+
+ while(i < dh_size && dst_host_array[i] != NULL) {
+ l = strlen(filter);
+
+ if(i+1 == dh_size || dst_host_array[i+1] == NULL)
+ snprintf(&filter[l], sizeof(filter)-l, "%s", dst_host_array[i]);
+ else
+ snprintf(&filter[l], sizeof(filter)-l, "%s or ", dst_host_array[i]);
+
+ i++;
+ }
+
+ l = strlen(filter);
+ snprintf(&filter[l], sizeof(filter)-l, "%s", ")");
+ produced = 1;
+ }
+
+
+
+ if(produced)
+ json_object_object_add(*jObj_bpfFilter, "pkt.peak.filter", json_object_new_string(filter));
+ else
+ json_object_object_add(*jObj_bpfFilter, "pkt.peak.filter", json_object_new_string(""));
+}
+#endif
+
+/* *********************************************** */
+/**
+ * @brief Produce bpf filter to filter ports and hosts
+ * in order to remove a peak in terms of number of source
+ * addresses.
+ */
+#ifdef HAVE_JSON_C
+void bpf_filter_host_peak_filter(json_object **jObj_bpfFilter,
+ const char *host_array[16],
+ int h_size) {
+ char filter[2048];
+ int produced = 0;
+ int i = 0;
+
+
+ if(host_array[0] != NULL) {
+ int l;
+
+ strcpy(filter, "not (dst ");
+
+ while(i < h_size && host_array[i] != NULL) {
+ l = strlen(filter);
+
+ if(i+1 == h_size || host_array[i+1] == NULL)
+ snprintf(&filter[l], sizeof(filter)-l, "%s", host_array[i]);
+ else
+ snprintf(&filter[l], sizeof(filter)-l, "%s or ", host_array[i]);
+
+ i++;
+ }
+
+ l = strlen(filter);
+ snprintf(&filter[l], sizeof(filter)-l, "%s", ")");
+ produced = 1;
+ }
+
+ if(produced)
+ json_object_object_add(*jObj_bpfFilter, "host.peak.filter", json_object_new_string(filter));
+ else
+ json_object_object_add(*jObj_bpfFilter, "host.peak.filter", json_object_new_string(""));
+}
+#endif
+
+/* *********************************************** */
+/**
+ * @brief Initialize port array
+ */
+
+void bpf_filter_port_array_init(int array[], int size) {
+ int i;
+ for(i=0; i<size; i++)
+ array[i] = INIT_VAL;
+}
+
+/* *********************************************** */
+/**
+ * @brief Initialize host array
+ */
+
+void bpf_filter_host_array_init(const char *array[48], int size) {
+ int i;
+ for(i=0; i<size; i++)
+ array[i] = NULL;
+}
+
+/* *********************************************** */
+
+/**
+ * @brief Add host to host filter array
+ */
+
+void bpf_filter_host_array_add(const char *filter_array[48], int size, const char *host) {
+ int i;
+ int r;
+ for(i=0; i<size; i++) {
+ if((filter_array[i] != NULL) && (r = strcmp(filter_array[i], host)) == 0)
+ return;
+ if(filter_array[i] == NULL) {
+ filter_array[i] = host;
+ return;
+ }
+ }
+ fprintf(stderr,"bpf_filter_host_array_add: max array size is reached!\n");
+ exit(-1);
+}
+
+
+/* *********************************************** */
+
+/**
+ * @brief Add port to port filter array
+ */
+
+void bpf_filter_port_array_add(int filter_array[], int size, int port) {
+ int i;
+ for(i=0; i<size; i++) {
+ if(filter_array[i] == port)
+ return;
+ if(filter_array[i] == INIT_VAL) {
+ filter_array[i] = port;
+ return;
+ }
+ }
+ fprintf(stderr,"bpf_filter_port_array_add: max array size is reached!\n");
+ exit(-1);
+}
+
+
+/* *********************************************** */
+#ifdef HAVE_JSON_C
+/*
+ * @brief returns average value for a given field
+ */
+float getAverage(struct json_object *jObj_stat, char *field){
+ json_object *field_stat;
+ json_bool res;
+ float average;
+ float sum = 0;
+ int r;
+ int j;
+
+ if((r = strcmp(field, "top.scanner.stats")) == 0) {
+ for(j=0; j<json_object_array_length(jObj_stat); j++) {
+ field_stat = json_object_array_get_idx(jObj_stat, j);
+ json_object *jObj_tot_flows_number;
+
+ if((res = json_object_object_get_ex(field_stat, "total.flows.number", &jObj_tot_flows_number)) == 0) {
+ fprintf(stderr, "ERROR: can't get \"total.flows.number\", use -x flag only with .json files generated by ndpiReader -b flag.\n");
+ exit(-1);
+ }
+ u_int32_t tot_flows_number = json_object_get_int(jObj_tot_flows_number);
+
+ sum += tot_flows_number;
+ }
+ } else if((r = strcmp(field, "top.src.pkts.stats")) == 0) {
+ for(j=0; j<json_object_array_length(jObj_stat); j++) {
+ field_stat = json_object_array_get_idx(jObj_stat, j);
+ json_object *jObj_packets_number;
+
+ if((res = json_object_object_get_ex(field_stat, "packets.number", &jObj_packets_number)) == 0) {
+ fprintf(stderr, "ERROR: can't get \"packets.number\", use -x flag only with .json files generated by ndpiReader -b flag.\n");
+ exit(-1);
+ }
+ u_int32_t packets_number = json_object_get_int(jObj_packets_number);
+
+ sum += packets_number;
+ }
+ }
+
+ if(j == 0) return 0.0;
+
+ return sum/j;
+}
+#endif
+/* *********************************************** */
+#ifdef HAVE_JSON_C
+/*
+ * @brief returns standard deviation for a given
+ * field and it's average value.
+ */
+float getStdDeviation(struct json_object *jObj_stat, float average, char *field){
+ json_object *field_stat;
+ json_bool res;
+ float sum = 0;
+ int j;
+ int r;
+
+ if((r = strcmp(field, "top.scanner.stats")) == 0){
+ for(j=0; j<json_object_array_length(jObj_stat); j++) {
+ field_stat = json_object_array_get_idx(jObj_stat, j);
+ json_object *jObj_tot_flows_number;
+
+ if((res = json_object_object_get_ex(field_stat, "total.flows.number", &jObj_tot_flows_number)) == 0) {
+ fprintf(stderr, "ERROR: can't get \"total.flows.number\", use -x flag only with .json files generated by ndpiReader -b flag.\n");
+ exit(-1);
+ }
+ u_int32_t tot_flows_number = json_object_get_int(jObj_tot_flows_number);
+
+ sum += pow((tot_flows_number - average), 2);
+ }
+ }
+
+ return sqrt(sum/(float)j);
+}
+
+#endif
+
+/* *********************************************** */
+
+#ifdef HAVE_JSON_C
+void getSourcePorts(struct json_object *jObj_stat, int srcPortArray[], int size, float threshold) {
+ int j;
+
+ for(j=0; j<json_object_array_length(jObj_stat); j++) {
+ json_object *src_pkts_stat = json_object_array_get_idx(jObj_stat, j);
+ json_object *jObj_packets_number;
+ json_object *jObj_flows_percent;
+ json_object *jObj_flows_packets;
+ json_object *jObj_port;
+ json_bool res;
+
+ if((res = json_object_object_get_ex(src_pkts_stat, "packets.number", &jObj_packets_number)) == 0) {
+ fprintf(stderr, "ERROR: can't get \"packets.number\", use -x flag only with .json files generated by ndpiReader -b flag.\n");
+ exit(-1);
+ }
+ u_int32_t packets_number = json_object_get_int(jObj_packets_number);
+
+ if((res = json_object_object_get_ex(src_pkts_stat, "flows.percent", &jObj_flows_percent)) == 0) {
+ fprintf(stderr, "ERROR: can't get \"flows.percent\", use -x flag only with .json files generated by ndpiReader -b flag.\n");
+ exit(-1);
+ }
+ double flows_percent = json_object_get_double(jObj_flows_percent);
+
+
+ if((res = json_object_object_get_ex(src_pkts_stat, "flows/packets", &jObj_flows_packets)) == 0) {
+ fprintf(stderr, "ERROR: can't get \"flows/packets\", use -x flag only with .json files generated by ndpiReader -b flag.\n");
+ exit(-1);
+ }
+ double flows_packets = json_object_get_double(jObj_flows_packets);
+
+
+ if((flows_packets > FLOWS_PACKETS_THRESHOLD)
+ && (flows_percent >= FLOWS_PERCENT_THRESHOLD)
+ && packets_number >= threshold) {
+ if((res = json_object_object_get_ex(src_pkts_stat, "port", &jObj_port)) == 0) {
+ fprintf(stderr, "ERROR: can't get \"port\", use -x flag only with .json files generated by ndpiReader -b flag.\n");
+ exit(-1);
+ }
+ int port = json_object_get_int(jObj_port);
+
+ bpf_filter_port_array_add(srcPortArray, size, port);
+ }
+ }
+}
+#endif
+
+/* *********************************************** */
+
+#ifdef HAVE_JSON_C
+void getReceiverHosts(struct json_object *jObj_stat, const char *dstHostArray[16], int size) {
+ int j;
+
+ for(j=0; j<json_object_array_length(jObj_stat); j++) {
+ json_object *scanner_stat = json_object_array_get_idx(jObj_stat, j);
+ json_object *jObj_host_address;
+ json_object *jObj_pkts_percent;
+ json_bool res;
+
+ if((res = json_object_object_get_ex(scanner_stat, "packets.percent", &jObj_pkts_percent)) == 0) {
+ fprintf(stderr, "ERROR: can't get \"packets.percent\", use -x flag only with .json files generated by ndpiReader -b flag.\n");
+ exit(-1);
+ }
+ double pkts_percent = json_object_get_double(jObj_pkts_percent);
+
+
+ if(pkts_percent > PKTS_PERCENT_THRESHOLD) {
+ if((res = json_object_object_get_ex(scanner_stat, "ip.address", &jObj_host_address)) == 0) {
+ fprintf(stderr, "ERROR: can't get \"ip.address, use -x flag only with .json files generated by ndpiReader -b flag.\n");
+ exit(-1);
+ }
+ const char *host_address = json_object_get_string(jObj_host_address);
+
+ bpf_filter_host_array_add(dstHostArray, size, host_address);
+ }
+ }
+}
+#endif
+
+/* *********************************************** */
+
+#ifdef HAVE_JSON_C
+void getScannerHosts(struct json_object *jObj_stat, int duration,
+ const char *srcHostArray[48], int size,
+ float threshold) {
+ int j;
+
+ for(j=0; j<json_object_array_length(jObj_stat); j++) {
+ json_object *scanner_stat = json_object_array_get_idx(jObj_stat, j);
+ json_object *jObj_host_address;
+ json_object *jObj_tot_flows_number;
+ json_bool res;
+
+
+ if((res = json_object_object_get_ex(scanner_stat, "total.flows.number", &jObj_tot_flows_number)) == 0) {
+ fprintf(stderr, "ERROR: can't get \"total.flows.number\", use -x flag only with .json files generated by ndpiReader -b flag.\n");
+ exit(-1);
+ }
+ u_int32_t tot_flows_number = json_object_get_int(jObj_tot_flows_number);
+
+
+ if(((tot_flows_number/(float)duration) > FLOWS_THRESHOLD) && tot_flows_number > threshold) {
+ if((res = json_object_object_get_ex(scanner_stat, "ip.address", &jObj_host_address)) == 0) {
+ fprintf(stderr, "ERROR: can't get \"ip.address\", use -x flag only with .json files generated by ndpiReader -b flag.\n");
+ exit(-1);
+ }
+ const char *host_address = json_object_get_string(jObj_host_address);
+
+ bpf_filter_host_array_add(srcHostArray, size, host_address);
+
+ }
+ }
+}
+#endif
+
+/* *********************************************** */
+
+#ifdef HAVE_JSON_C
+void getDestinationHosts(struct json_object *jObj_stat, int duration,
+ const char *dstHostArray[16], int size) {
+ int j;
+
+ for(j=0; j<json_object_array_length(jObj_stat); j++) {
+ json_object *scanner_stat = json_object_array_get_idx(jObj_stat, j);
+ json_object *jObj_host_address;
+ json_object *jObj_flows_percent;
+ json_bool res;
+
+
+ if((res = json_object_object_get_ex(scanner_stat, "flows.percent", &jObj_flows_percent)) == 0) {
+ fprintf(stderr, "ERROR: can't get \"flows.percent\", use -x flag only with .json files generated by ndpiReader -b flag.\n");
+ exit(-1);
+ }
+ double flows_percent = json_object_get_double(jObj_flows_percent);
+
+
+ if(flows_percent > FLOWS_PERCENT_THRESHOLD_2) {
+ if((res = json_object_object_get_ex(scanner_stat, "aggressive.host", &jObj_host_address)) == 0) {
+ fprintf(stderr, "ERROR: can't get \"aggressive.host\", use -x flag only with .json files generated by ndpiReader -b flag.\n");
+ exit(-1);
+ }
+ const char *host_address = json_object_get_string(jObj_host_address);
+
+ bpf_filter_host_array_add(dstHostArray, size, host_address);
+
+ }
+ }
+}
+#endif
+
+/* *********************************************** */
+
+#ifdef HAVE_JSON_C
+static void produceBpfFilter(char *filePath) {
+ json_object *jObj; /* entire json object from file */
+ json_object *jObj_duration;
+ json_object *jObj_statistics; /* json array */
+ json_bool res;
+ int filterSrcPorts[PORT_ARRAY_SIZE];
+ const char *filterSrcHosts[48];
+ const char *filterDstHosts[48];
+ const char *filterPktDstHosts[48];
+ struct stat statbuf;
+ FILE *fp = NULL;
+ char *fileName;
+ char _filterFilePath[1024];
+ json_object *jObj_bpfFilter;
+ void *fmap;
+ int fsock;
+ float average;
+ float deviation;
+ int duration;
+ int typeCheck;
+ int array_len;
+ int i;
+
+ if((fsock = open(filePath, O_RDONLY)) == -1) {
+ fprintf(stderr,"error opening file %s\n", filePath);
+ exit(-1);
+ }
+
+ if(fstat(fsock, &statbuf) == -1) {
+ fprintf(stderr,"error getting file stat\n");
+ exit(-1);
+ }
+
+ if((fmap = mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, fsock, 0)) == MAP_FAILED) {
+ fprintf(stderr,"error mmap is failed\n");
+ exit(-1);
+ }
+
+ if((jObj = json_tokener_parse(fmap)) == NULL) {
+ fprintf(stderr,"ERROR: invalid json file. Use -x flag only with .json files generated by ndpiReader -b flag.\n");
+ exit(-1);
+ }
+
+
+ if((res = json_object_object_get_ex(jObj, "duration.in.seconds", &jObj_duration)) == 0) {
+ fprintf(stderr,"ERROR: can't get \"duration.in.seconds\", use -x flag only with .json files generated by ndpiReader -b flag.\n");
+ exit(-1);
+ }
+ duration = json_object_get_int(jObj_duration);
+
+
+ if((res = json_object_object_get_ex(jObj, "statistics", &jObj_statistics)) == 0) {
+ fprintf(stderr,"ERROR: can't get \"statistics\", use -x flag only with .json files generated by ndpiReader -b flag.\n");
+ exit(-1);
+ }
+
+ if((typeCheck = json_object_is_type(jObj_statistics, json_type_array)) == 0) {
+ fprintf(stderr,"ERROR: invalid json file. Use -x flag only with .json files generated by ndpiReader -b flag.\n");
+ exit(-1);
+ }
+ array_len = json_object_array_length(jObj_statistics);
+
+
+ bpf_filter_port_array_init(filterSrcPorts, PORT_ARRAY_SIZE);
+ bpf_filter_host_array_init(filterSrcHosts, HOST_ARRAY_SIZE);
+ bpf_filter_host_array_init(filterDstHosts, HOST_ARRAY_SIZE);
+ bpf_filter_host_array_init(filterPktDstHosts, HOST_ARRAY_SIZE/2);
+
+ for(i=0; i<array_len; i++) {
+ json_object *stats = json_object_array_get_idx(jObj_statistics, i);
+ json_object *val;
+
+ if((res = json_object_object_get_ex(stats, "top.scanner.stats", &val)) == 0) {
+ fprintf(stderr,"ERROR: can't get \"top.scanner.stats\", use -x flag only with .json files generated by ndpiReader -b flag.\n");
+ exit(-1);
+ }
+
+ if((average = getAverage(val, "top.scanner.stats")) != 0){
+ deviation = getStdDeviation(val, average, "top.scanner.stats");
+ getScannerHosts(val, duration, filterSrcHosts, HOST_ARRAY_SIZE, average+deviation);
+ }
+
+
+ if((res = json_object_object_get_ex(stats, "top.receiver.stats", &val)) == 0) {
+ fprintf(stderr,"ERROR: can't get \"top.receiver.stats\", use -x flag only with .json files generated by ndpiReader -b flag.\n");
+ exit(-1);
+ }
+ getReceiverHosts(val, filterPktDstHosts, HOST_ARRAY_SIZE/2);
+
+
+ if((res = json_object_object_get_ex(stats, "top.src.pkts.stats", &val)) == 0) {
+ fprintf(stderr,"ERROR: can't get \"top.src.pkts.stats\", use -x flag only with .json files generated by ndpiReader -b flag.\n");
+ exit(-1);
+ }
+
+ if((average = getAverage(val, "top.src.pkts.stats")) != 0)
+ getSourcePorts(val, filterSrcPorts, PORT_ARRAY_SIZE, average);
+
+
+ if((res = json_object_object_get_ex(stats, "top.dst.pkts.stats", &val)) == 0) {
+ fprintf(stderr,"ERROR: can't get \"top.dst.pkts.stats\", use -x flag only with .json files generated by ndpiReader -b flag.\n");
+ exit(-1);
+ }
+ getDestinationHosts(val, duration, filterDstHosts, HOST_ARRAY_SIZE);
+ }
+
+
+ fileName = basename(filePath);
+ snprintf(_filterFilePath, sizeof(_filterFilePath), "%s.bpf", filePath);
+
+ if((fp = fopen(_filterFilePath,"w")) == NULL) {
+ printf("Error creating .json file %s\n", _filterFilePath);
+ exit(-1);
+ }
+
+ jObj_bpfFilter = json_object_new_object();
+
+ bpf_filter_pkt_peak_filter(&jObj_bpfFilter, filterSrcPorts, PORT_ARRAY_SIZE,
+ filterSrcHosts, HOST_ARRAY_SIZE, filterPktDstHosts, HOST_ARRAY_SIZE/2);
+
+ bpf_filter_host_peak_filter(&jObj_bpfFilter, filterDstHosts, HOST_ARRAY_SIZE);
+
+ fprintf(fp,"%s\n",json_object_to_json_string(jObj_bpfFilter));
+ fclose(fp);
+
+ printf("created: %s\n", _filterFilePath);
+
+ json_object_put(jObj); /* free memory */
+}
+#endif
+
+
+/* *********************************************** */
+
+
/**
@brief MAIN FUNCTION
**/
+#ifdef APP_HAS_OWN_MAIN
+int orginal_main(int argc, char **argv) {
+#else
int main(int argc, char **argv) {
-
+#endif
int i;
automataUnitTest();
+ ndpi_info_mod = ndpi_init_detection_module();
+ if (ndpi_info_mod == NULL) return -1;
+
memset(ndpi_thread_info, 0, sizeof(ndpi_thread_info));
- memset(&pcap_start, 0, sizeof(pcap_start));
- memset(&pcap_end, 0, sizeof(pcap_end));
parseOptions(argc, argv);
+ if(bpf_filter_flag) {
+#ifdef HAVE_JSON_C
+ produceBpfFilter(_diagnoseFilePath);
+ return 0;
+#endif
+ }
+
if((!json_flag) && (!quiet_mode)) {
printf("\n-----------------------------------------------------------\n"
"* NOTE: This is demo app to show *some* nDPI features.\n"
@@ -1322,13 +3174,14 @@ int main(int argc, char **argv) {
for(i=0; i<num_loops; i++)
test_lib();
- if(results_path) free(results_path);
- if(results_file) fclose(results_file);
+ if(results_path) free(results_path);
+ if(results_file) fclose(results_file);
+ if(extcap_dumper) pcap_dump_close(extcap_dumper);
+ if(ndpi_info_mod) ndpi_exit_detection_module(ndpi_info_mod);
return 0;
}
-
#ifdef WIN32
#ifndef __GNUC__
#define EPOCHFILETIME (116444736000000000i64)
@@ -1336,7 +3189,6 @@ int main(int argc, char **argv) {
#define EPOCHFILETIME (116444736000000000LL)
#endif
-
/**
@brief Timezone
**/
diff --git a/example/ndpi_util.c b/example/ndpi_util.c
index d121b5841..104aa4db5 100644
--- a/example/ndpi_util.c
+++ b/example/ndpi_util.c
@@ -1,7 +1,7 @@
/*
* ndpi_util.c
*
- * Copyright (C) 2011-16 - ntop.org
+ * Copyright (C) 2011-17 - ntop.org
*
* This file is part of nDPI, an open source deep packet inspection
* library based on the OpenDPI and PACE technology by ipoque GmbH
@@ -48,6 +48,7 @@
#define MPLS_MULTI 0x8848
#define PPPoE 0x8864
#define SNAP 0xaa
+#define BSTP 0x42 /* Bridge Spanning Tree Protocol */
/* mask for FCF */
#define WIFI_DATA 0x2 /* 0000 0010 */
@@ -62,8 +63,9 @@
#define GTP_U_V1_PORT 2152
#define TZSP_PORT 37008
-#define SIZEOF_ID_STRUCT (sizeof(struct ndpi_id_struct))
-#define SIZEOF_FLOW_STRUCT (sizeof(struct ndpi_flow_struct))
+#ifndef DLT_LINUX_SLL
+#define DLT_LINUX_SLL 113
+#endif
#include "ndpi_main.h"
#include "ndpi_util.h"
@@ -71,7 +73,7 @@
/* ***************************************************** */
void ndpi_free_flow_info_half(struct ndpi_flow_info *flow) {
- if(flow->ndpi_flow) { ndpi_free_flow(flow->ndpi_flow); flow->ndpi_flow = NULL; }
+ if(flow->ndpi_flow) { ndpi_flow_free(flow->ndpi_flow); flow->ndpi_flow = NULL; }
if(flow->src_id) { ndpi_free(flow->src_id); flow->src_id = NULL; }
if(flow->dst_id) { ndpi_free(flow->dst_id); flow->dst_id = NULL; }
}
@@ -103,9 +105,64 @@ static void free_wrapper(void *freeable) {
/* ***************************************************** */
-struct ndpi_workflow * ndpi_workflow_init(const struct ndpi_workflow_prefs * prefs, pcap_t * pcap_handle) {
+static uint16_t ndpi_get_proto_id(struct ndpi_detection_module_struct *ndpi_mod, const char *name) {
+ uint16_t proto_id;
+ char *e;
+ unsigned long p = strtol(name,&e,0);
+ if(e && !*e) {
+ if(p < NDPI_MAX_SUPPORTED_PROTOCOLS+NDPI_MAX_NUM_CUSTOM_PROTOCOLS &&
+ ndpi_mod->proto_defaults[p].protoName) return (uint16_t)p;
+ return NDPI_PROTOCOL_UNKNOWN;
+ }
+ for(proto_id=NDPI_PROTOCOL_UNKNOWN; proto_id < NDPI_MAX_SUPPORTED_PROTOCOLS+NDPI_MAX_NUM_CUSTOM_PROTOCOLS; proto_id++) {
+ if(ndpi_mod->proto_defaults[proto_id].protoName &&
+ !strcasecmp(ndpi_mod->proto_defaults[proto_id].protoName,name))
+ return proto_id;
+ }
+ return NDPI_PROTOCOL_UNKNOWN;
+}
+static NDPI_PROTOCOL_BITMASK debug_bitmask;
+static char _proto_delim[] = " \t,:;";
+static int parse_debug_proto(struct ndpi_detection_module_struct *ndpi_mod, char *str) {
+char *n;
+uint16_t proto;
+char op=1;
+for(n = strtok(str,_proto_delim); n && *n; n = strtok(NULL,_proto_delim)) {
+ if(*n == '-') {
+ op = 0;
+ n++;
+ } else if(*n == '+') {
+ op = 1;
+ n++;
+ }
+ if(!strcmp(n,"all")) {
+ if(op)
+ NDPI_BITMASK_SET_ALL(debug_bitmask);
+ else
+ NDPI_BITMASK_RESET(debug_bitmask);
+ continue;
+ }
+ proto = ndpi_get_proto_id(ndpi_mod, n);
+ if(proto == NDPI_PROTOCOL_UNKNOWN && strcmp(n,"unknown") && strcmp(n,"0")) {
+ fprintf(stderr,"Invalid protocol %s\n",n);
+ return 1;
+ }
+ if(op)
+ NDPI_BITMASK_ADD(debug_bitmask,proto);
+ else
+ NDPI_BITMASK_DEL(debug_bitmask,proto);
+}
+return 0;
+}
+
+/* ***************************************************** */
+extern char *_debug_protocols;
+static int _debug_protocols_ok = 0;
+
+struct ndpi_workflow * ndpi_workflow_init(const struct ndpi_workflow_prefs * prefs, pcap_t * pcap_handle) {
set_ndpi_malloc(malloc_wrapper), set_ndpi_free(free_wrapper);
+ set_ndpi_flow_malloc(NULL), set_ndpi_flow_free(NULL);
/* TODO: just needed here to init ndpi malloc wrapper */
struct ndpi_detection_module_struct * module = ndpi_init_detection_module();
@@ -119,14 +176,25 @@ struct ndpi_workflow * ndpi_workflow_init(const struct ndpi_workflow_prefs * pre
NDPI_LOG(0, NULL, NDPI_LOG_ERROR, "global structure initialization failed\n");
exit(-1);
}
+ module->ndpi_log_level = nDPI_LogLevel;
+ if(_debug_protocols != NULL && ! _debug_protocols_ok) {
+ if(parse_debug_proto(module,_debug_protocols))
+ exit(-1);
+ _debug_protocols_ok = 1;
+ }
+#ifdef NDPI_ENABLE_DEBUG_MESSAGES
+ NDPI_BITMASK_RESET(module->debug_bitmask);
+ if(_debug_protocols_ok)
+ module->debug_bitmask = debug_bitmask;
+#endif
workflow->ndpi_flows_root = ndpi_calloc(workflow->prefs.num_roots, sizeof(void *));
return workflow;
}
/* ***************************************************** */
-static void ndpi_flow_info_freer(void *node) {
+void ndpi_flow_info_freer(void *node) {
struct ndpi_flow_info *flow = (struct ndpi_flow_info*)node;
ndpi_free_flow_info_half(flow);
@@ -152,14 +220,36 @@ int ndpi_workflow_node_cmp(const void *a, const void *b) {
struct ndpi_flow_info *fa = (struct ndpi_flow_info*)a;
struct ndpi_flow_info *fb = (struct ndpi_flow_info*)b;
- if(fa->vlan_id < fb->vlan_id ) return(-1); else { if(fa->vlan_id > fb->vlan_id ) return(1); }
- if(fa->lower_ip < fb->lower_ip ) return(-1); else { if(fa->lower_ip > fb->lower_ip ) return(1); }
- if(fa->lower_port < fb->lower_port) return(-1); else { if(fa->lower_port > fb->lower_port) return(1); }
- if(fa->upper_ip < fb->upper_ip ) return(-1); else { if(fa->upper_ip > fb->upper_ip ) return(1); }
- if(fa->upper_port < fb->upper_port) return(-1); else { if(fa->upper_port > fb->upper_port) return(1); }
- if(fa->protocol < fb->protocol ) return(-1); else { if(fa->protocol > fb->protocol ) return(1); }
+ if(fa->hashval < fb->hashval) return(-1); else if(fa->hashval > fb->hashval) return(1);
+
+ /* Flows have the same hash */
+
+ if(fa->vlan_id < fb->vlan_id ) return(-1); else { if(fa->vlan_id > fb->vlan_id ) return(1); }
+ if(fa->protocol < fb->protocol ) return(-1); else { if(fa->protocol > fb->protocol ) return(1); }
+
+ if(
+ (
+ (fa->src_ip == fb->src_ip )
+ && (fa->src_port == fb->src_port)
+ && (fa->dst_ip == fb->dst_ip )
+ && (fa->dst_port == fb->dst_port)
+ )
+ ||
+ (
+ (fa->src_ip == fb->dst_ip )
+ && (fa->src_port == fb->dst_port)
+ && (fa->dst_ip == fb->src_ip )
+ && (fa->dst_port == fb->src_port)
+ )
+ )
+ return(0);
+
+ if(fa->src_ip < fb->src_ip ) return(-1); else { if(fa->src_ip > fb->src_ip ) return(1); }
+ if(fa->src_port < fb->src_port) return(-1); else { if(fa->src_port > fb->src_port) return(1); }
+ if(fa->dst_ip < fb->dst_ip ) return(-1); else { if(fa->dst_ip > fb->dst_ip ) return(1); }
+ if(fa->dst_port < fb->dst_port) return(-1); else { if(fa->dst_port > fb->dst_port) return(1); }
- return(0);
+ return(0); /* notreached */
}
/* ***************************************************** */
@@ -199,11 +289,7 @@ static struct ndpi_flow_info *get_ndpi_flow_info(struct ndpi_workflow * workflow
u_int8_t **payload,
u_int16_t *payload_len,
u_int8_t *src_to_dst_direction) {
- u_int32_t idx, l4_offset;
- u_int32_t lower_ip;
- u_int32_t upper_ip;
- u_int16_t lower_port;
- u_int16_t upper_port;
+ u_int32_t idx, l4_offset, hashval;
struct ndpi_flow_info flow;
void *ret;
u_int8_t *l3, *l4;
@@ -217,7 +303,7 @@ static struct ndpi_flow_info *get_ndpi_flow_info(struct ndpi_workflow * workflow
return NULL;
if((iph->ihl * 4) > ipsize || ipsize < ntohs(iph->tot_len)
- || (iph->frag_off & htons(0x1FFF)) != 0)
+ /* || (iph->frag_off & htons(0x1FFF)) != 0 */)
return NULL;
l4_offset = iph->ihl * 4;
@@ -243,95 +329,64 @@ static struct ndpi_flow_info *get_ndpi_flow_info(struct ndpi_workflow * workflow
if(l4_packet_len > workflow->stats.max_packet_len)
workflow->stats.max_packet_len = l4_packet_len;
- if(iph->saddr < iph->daddr) {
- lower_ip = iph->saddr;
- upper_ip = iph->daddr;
- } else {
- lower_ip = iph->daddr;
- upper_ip = iph->saddr;
- }
-
*proto = iph->protocol;
l4 = ((u_int8_t *) l3 + l4_offset);
if(iph->protocol == IPPROTO_TCP && l4_packet_len >= 20) {
u_int tcp_len;
- workflow->stats.tcp_count++;
-
// tcp
+ workflow->stats.tcp_count++;
*tcph = (struct ndpi_tcphdr *)l4;
*sport = ntohs((*tcph)->source), *dport = ntohs((*tcph)->dest);
-
- if(iph->saddr < iph->daddr) {
- lower_port = (*tcph)->source, upper_port = (*tcph)->dest;
- *src_to_dst_direction = 1;
- } else {
- lower_port = (*tcph)->dest;
- upper_port = (*tcph)->source;
-
- *src_to_dst_direction = 0;
- if(iph->saddr == iph->daddr) {
- if(lower_port > upper_port) {
- u_int16_t p = lower_port;
-
- lower_port = upper_port;
- upper_port = p;
- }
- }
- }
-
tcp_len = ndpi_min(4*(*tcph)->doff, l4_packet_len);
*payload = &l4[tcp_len];
*payload_len = ndpi_max(0, l4_packet_len-4*(*tcph)->doff);
} else if(iph->protocol == IPPROTO_UDP && l4_packet_len >= 8) {
// udp
- workflow->stats.udp_count++;
+ workflow->stats.udp_count++;
*udph = (struct ndpi_udphdr *)l4;
*sport = ntohs((*udph)->source), *dport = ntohs((*udph)->dest);
*payload = &l4[sizeof(struct ndpi_udphdr)];
*payload_len = ndpi_max(0, l4_packet_len-sizeof(struct ndpi_udphdr));
-
- if(iph->saddr < iph->daddr) {
- lower_port = (*udph)->source, upper_port = (*udph)->dest;
- *src_to_dst_direction = 1;
- } else {
- lower_port = (*udph)->dest, upper_port = (*udph)->source;
-
- *src_to_dst_direction = 0;
-
- if(iph->saddr == iph->daddr) {
- if(lower_port > upper_port) {
- u_int16_t p = lower_port;
-
- lower_port = upper_port;
- upper_port = p;
- }
- }
- }
-
- *sport = ntohs(lower_port), *dport = ntohs(upper_port);
} else {
// non tcp/udp protocols
- lower_port = 0;
- upper_port = 0;
+ *sport = *dport = 0;
}
flow.protocol = iph->protocol, flow.vlan_id = vlan_id;
- flow.lower_ip = lower_ip, flow.upper_ip = upper_ip;
- flow.lower_port = lower_port, flow.upper_port = upper_port;
+ flow.src_ip = iph->saddr, flow.dst_ip = iph->daddr;
+ flow.src_port = htons(*sport), flow.dst_port = htons(*dport);
+ flow.hashval = hashval = flow.protocol + flow.vlan_id + flow.src_ip + flow.dst_ip + flow.src_port + flow.dst_port;
+ idx = hashval % workflow->prefs.num_roots;
+ ret = ndpi_tfind(&flow, &workflow->ndpi_flows_root[idx], ndpi_workflow_node_cmp);
- if(0)
- NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_DEBUG, "[NDPI] [%u][%u:%u <-> %u:%u]\n",
- iph->protocol, lower_ip, ntohs(lower_port), upper_ip, ntohs(upper_port));
- idx = (vlan_id + lower_ip + upper_ip + iph->protocol + lower_port + upper_port) % workflow->prefs.num_roots;
- ret = ndpi_tfind(&flow, &workflow->ndpi_flows_root[idx], ndpi_workflow_node_cmp);
+ /* to avoid two nodes in one binary tree for a flow */
+ int is_changed = 0;
+ if(ret == NULL)
+ {
+ u_int32_t orig_src_ip = flow.src_ip;
+ u_int16_t orig_src_port = flow.src_port;
+ u_int32_t orig_dst_ip = flow.dst_ip;
+ u_int16_t orig_dst_port = flow.dst_port;
+
+ flow.src_ip = orig_dst_ip;
+ flow.src_port = orig_dst_port;
+ flow.dst_ip = orig_src_ip;
+ flow.dst_port = orig_src_port;
+
+ is_changed = 1;
+
+ ret = ndpi_tfind(&flow, &workflow->ndpi_flows_root[idx], ndpi_workflow_node_cmp);
+ }
if(ret == NULL) {
if(workflow->stats.ndpi_flow_count == workflow->prefs.max_ndpi_flows) {
- NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_ERROR, "maximum flow count (%u) has been exceeded\n", workflow->prefs.max_ndpi_flows);
+ NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_ERROR,
+ "maximum flow count (%u) has been exceeded\n",
+ workflow->prefs.max_ndpi_flows);
exit(-1);
} else {
struct ndpi_flow_info *newflow = (struct ndpi_flow_info*)malloc(sizeof(struct ndpi_flow_info));
@@ -339,25 +394,27 @@ static struct ndpi_flow_info *get_ndpi_flow_info(struct ndpi_workflow * workflow
if(newflow == NULL) {
NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_ERROR, "[NDPI] %s(1): not enough memory\n", __FUNCTION__);
return(NULL);
- }
+ } else
+ workflow->num_allocated_flows++;
memset(newflow, 0, sizeof(struct ndpi_flow_info));
+ newflow->hashval = hashval;
newflow->protocol = iph->protocol, newflow->vlan_id = vlan_id;
- newflow->lower_ip = lower_ip, newflow->upper_ip = upper_ip;
- newflow->lower_port = lower_port, newflow->upper_port = upper_port;
+ newflow->src_ip = iph->saddr, newflow->dst_ip = iph->daddr;
+ newflow->src_port = htons(*sport), newflow->dst_port = htons(*dport);
newflow->ip_version = version;
if(version == IPVERSION) {
- inet_ntop(AF_INET, &lower_ip, newflow->lower_name, sizeof(newflow->lower_name));
- inet_ntop(AF_INET, &upper_ip, newflow->upper_name, sizeof(newflow->upper_name));
+ inet_ntop(AF_INET, &newflow->src_ip, newflow->src_name, sizeof(newflow->src_name));
+ inet_ntop(AF_INET, &newflow->dst_ip, newflow->dst_name, sizeof(newflow->dst_name));
} else {
- inet_ntop(AF_INET6, &iph6->ip6_src, newflow->lower_name, sizeof(newflow->lower_name));
- inet_ntop(AF_INET6, &iph6->ip6_dst, newflow->upper_name, sizeof(newflow->upper_name));
- /* For consistency across platfoms replace :0: with :: */
- patchIPv6Address(newflow->lower_name), patchIPv6Address(newflow->upper_name);
+ inet_ntop(AF_INET6, &iph6->ip6_src, newflow->src_name, sizeof(newflow->src_name));
+ inet_ntop(AF_INET6, &iph6->ip6_dst, newflow->dst_name, sizeof(newflow->dst_name));
+ /* For consistency across platforms replace :0: with :: */
+ patchIPv6Address(newflow->src_name), patchIPv6Address(newflow->dst_name);
}
- if((newflow->ndpi_flow = ndpi_malloc(SIZEOF_FLOW_STRUCT)) == NULL) {
+ if((newflow->ndpi_flow = ndpi_flow_malloc(SIZEOF_FLOW_STRUCT)) == NULL) {
NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_ERROR, "[NDPI] %s(2): not enough memory\n", __FUNCTION__);
free(newflow);
return(NULL);
@@ -388,12 +445,26 @@ static struct ndpi_flow_info *get_ndpi_flow_info(struct ndpi_workflow * workflow
} else {
struct ndpi_flow_info *flow = *(struct ndpi_flow_info**)ret;
- if(flow->lower_ip == lower_ip && flow->upper_ip == upper_ip
- && flow->lower_port == lower_port && flow->upper_port == upper_port)
- *src = flow->src_id, *dst = flow->dst_id;
- else
- *src = flow->dst_id, *dst = flow->src_id;
-
+ if (is_changed) {
+ if(flow->src_ip == iph->saddr
+ && flow->dst_ip == iph->daddr
+ && flow->src_port == htons(*sport)
+ && flow->dst_port == htons(*dport)
+ )
+ *src = flow->dst_id, *dst = flow->src_id, *src_to_dst_direction = 0, flow->bidirectional = 1;
+ else
+ *src = flow->src_id, *dst = flow->dst_id, *src_to_dst_direction = 1;
+ }
+ else {
+ if(flow->src_ip == iph->saddr
+ && flow->dst_ip == iph->daddr
+ && flow->src_port == htons(*sport)
+ && flow->dst_port == htons(*dport)
+ )
+ *src = flow->src_id, *dst = flow->dst_id, *src_to_dst_direction = 1;
+ else
+ *src = flow->dst_id, *dst = flow->src_id, *src_to_dst_direction = 0, flow->bidirectional = 1;
+ }
return flow;
}
}
@@ -419,7 +490,7 @@ static struct ndpi_flow_info *get_ndpi_flow_info6(struct ndpi_workflow * workflo
iph.version = IPVERSION;
iph.saddr = iph6->ip6_src.u6_addr.u6_addr32[2] + iph6->ip6_src.u6_addr.u6_addr32[3];
iph.daddr = iph6->ip6_dst.u6_addr.u6_addr32[2] + iph6->ip6_dst.u6_addr.u6_addr32[3];
- iph.protocol = iph6->ip6_ctlun.ip6_un1.ip6_un1_nxt;
+ iph.protocol = iph6->ip6_hdr.ip6_un1_nxt;
if(iph.protocol == IPPROTO_DSTOPTS /* IPv6 destination option */) {
u_int8_t *options = (u_int8_t*)iph6 + sizeof(const struct ndpi_ipv6hdr);
@@ -429,27 +500,85 @@ static struct ndpi_flow_info *get_ndpi_flow_info6(struct ndpi_workflow * workflo
return(get_ndpi_flow_info(workflow, 6, vlan_id, &iph, iph6, ip_offset,
sizeof(struct ndpi_ipv6hdr),
- ntohs(iph6->ip6_ctlun.ip6_un1.ip6_un1_plen),
+ ntohs(iph6->ip6_hdr.ip6_un1_plen),
tcph, udph, sport, dport,
src, dst, proto, payload, payload_len, src_to_dst_direction));
}
/* ****************************************************** */
+void process_ndpi_collected_info(struct ndpi_workflow * workflow, struct ndpi_flow_info *flow) {
+ if(!flow->ndpi_flow) return;
+
+ snprintf(flow->host_server_name, sizeof(flow->host_server_name), "%s",
+ flow->ndpi_flow->host_server_name);
+
+ /* BITTORRENT */
+ if(flow->detected_protocol.app_protocol == NDPI_PROTOCOL_BITTORRENT) {
+ int i, j, n = 0;
+
+ for(i=0, j = 0; j < sizeof(flow->bittorent_hash)-1; i++) {
+ sprintf(&flow->bittorent_hash[j], "%02x", flow->ndpi_flow->protos.bittorrent.hash[i]);
+ j += 2, n += flow->ndpi_flow->protos.bittorrent.hash[i];
+ }
+
+ if(n == 0) flow->bittorent_hash[0] = '\0';
+ }
+ /* MDNS */
+ else if(flow->detected_protocol.app_protocol == NDPI_PROTOCOL_MDNS) {
+ snprintf(flow->info, sizeof(flow->info), "%s", flow->ndpi_flow->protos.mdns.answer);
+ }
+ /* UBNTAC2 */
+ else if(flow->detected_protocol.app_protocol == NDPI_PROTOCOL_UBNTAC2) {
+ snprintf(flow->info, sizeof(flow->info), "%s", flow->ndpi_flow->protos.ubntac2.version);
+ }
+ if(flow->detected_protocol.app_protocol != NDPI_PROTOCOL_DNS) {
+ /* SSH */
+ if(flow->detected_protocol.app_protocol == NDPI_PROTOCOL_SSH) {
+ snprintf(flow->ssh_ssl.client_info, sizeof(flow->ssh_ssl.client_info), "%s",
+ flow->ndpi_flow->protos.ssh.client_signature);
+ snprintf(flow->ssh_ssl.server_info, sizeof(flow->ssh_ssl.server_info), "%s",
+ flow->ndpi_flow->protos.ssh.server_signature);
+ }
+ /* SSL */
+ else if((flow->detected_protocol.app_protocol == NDPI_PROTOCOL_SSL)
+ || (flow->detected_protocol.master_protocol == NDPI_PROTOCOL_SSL)) {
+ snprintf(flow->ssh_ssl.client_info, sizeof(flow->ssh_ssl.client_info), "%s",
+ flow->ndpi_flow->protos.ssl.client_certificate);
+ snprintf(flow->ssh_ssl.server_info, sizeof(flow->ssh_ssl.server_info), "%s",
+ flow->ndpi_flow->protos.ssl.server_certificate);
+ }
+ }
+
+ if(flow->detection_completed && !flow->check_extra_packets) {
+ if(flow->detected_protocol.app_protocol == NDPI_PROTOCOL_UNKNOWN) {
+ if (workflow->__flow_giveup_callback != NULL)
+ workflow->__flow_giveup_callback(workflow, flow, workflow->__flow_giveup_udata);
+ } else {
+ if (workflow->__flow_detected_callback != NULL)
+ workflow->__flow_detected_callback(workflow, flow, workflow->__flow_detected_udata);
+ }
+
+ ndpi_free_flow_info_half(flow);
+ }
+}
+
+/* ****************************************************** */
+
/**
Function to process the packet:
determine the flow of a packet and try to decode it
@return: 0 if success; else != 0
-
+
@Note: ipsize = header->len - ip_offset ; rawsize = header->len
*/
-static unsigned int packet_processing(struct ndpi_workflow * workflow,
- const u_int64_t time,
- u_int16_t vlan_id,
- const struct ndpi_iphdr *iph,
- struct ndpi_ipv6hdr *iph6,
- u_int16_t ip_offset,
- u_int16_t ipsize, u_int16_t rawsize) {
+static struct ndpi_proto packet_processing(struct ndpi_workflow * workflow,
+ const u_int64_t time,
+ u_int16_t vlan_id,
+ const struct ndpi_iphdr *iph,
+ struct ndpi_ipv6hdr *iph6,
+ u_int16_t ip_offset,
+ u_int16_t ipsize, u_int16_t rawsize) {
struct ndpi_id_struct *src, *dst;
struct ndpi_flow_info *flow = NULL;
struct ndpi_flow_struct *ndpi_flow = NULL;
@@ -458,7 +587,8 @@ static unsigned int packet_processing(struct ndpi_workflow * workflow,
struct ndpi_udphdr *udph = NULL;
u_int16_t sport, dport, payload_len;
u_int8_t *payload;
- u_int8_t src_to_dst_direction= 1;
+ u_int8_t src_to_dst_direction = 1;
+ struct ndpi_proto nproto = { NDPI_PROTOCOL_UNKNOWN, NDPI_PROTOCOL_UNKNOWN };
if(iph)
flow = get_ndpi_flow_info(workflow, IPVERSION, vlan_id, iph, NULL,
@@ -478,80 +608,77 @@ static unsigned int packet_processing(struct ndpi_workflow * workflow,
workflow->stats.total_wire_bytes += rawsize + 24 /* CRC etc */,
workflow->stats.total_ip_bytes += rawsize;
ndpi_flow = flow->ndpi_flow;
- flow->packets++, flow->bytes += rawsize;
+
+ if(src_to_dst_direction)
+ flow->src2dst_packets++, flow->src2dst_bytes += rawsize;
+ else
+ flow->dst2src_packets++, flow->dst2src_bytes += rawsize;
+
flow->last_seen = time;
- } else {
- return(0);
+ } else { // flow is NULL
+ workflow->stats.total_discarded_bytes++;
+ return(nproto);
}
/* Protocol already detected */
- if(flow->detection_completed) return(0);
+ if(flow->detection_completed) {
+ if(flow->check_extra_packets && ndpi_flow != NULL && ndpi_flow->check_extra_packets) {
+ if(ndpi_flow->num_extra_packets_checked == 0 && ndpi_flow->max_extra_packets_to_check == 0) {
+ /* Protocols can set this, but we set it here in case they didn't */
+ ndpi_flow->max_extra_packets_to_check = MAX_EXTRA_PACKETS_TO_CHECK;
+ }
+ if(ndpi_flow->num_extra_packets_checked < ndpi_flow->max_extra_packets_to_check) {
+ ndpi_process_extra_packet(workflow->ndpi_struct, ndpi_flow,
+ iph ? (uint8_t *)iph : (uint8_t *)iph6,
+ ipsize, time, src, dst);
+ if (ndpi_flow->check_extra_packets == 0) {
+ flow->check_extra_packets = 0;
+ process_ndpi_collected_info(workflow, flow);
+ }
+ }
+ } else if (ndpi_flow != NULL) {
+ /* If this wasn't NULL we should do the half free */
+ /* TODO: When half_free is deprecated, get rid of this */
+ ndpi_free_flow_info_half(flow);
+ }
+
+ return(flow->detected_protocol);
+ }
flow->detected_protocol = ndpi_detection_process_packet(workflow->ndpi_struct, ndpi_flow,
iph ? (uint8_t *)iph : (uint8_t *)iph6,
ipsize, time, src, dst);
- if((flow->detected_protocol.protocol != NDPI_PROTOCOL_UNKNOWN)
- || ((proto == IPPROTO_UDP) && (flow->packets > 8))
- || ((proto == IPPROTO_TCP) && (flow->packets > 10))) {
+ if((flow->detected_protocol.app_protocol != NDPI_PROTOCOL_UNKNOWN)
+ || ((proto == IPPROTO_UDP) && ((flow->src2dst_packets + flow->dst2src_packets) > 8))
+ || ((proto == IPPROTO_TCP) && ((flow->src2dst_packets + flow->dst2src_packets) > 10))) {
/* New protocol detected or give up */
flow->detection_completed = 1;
+ /* Check if we should keep checking extra packets */
+ if (ndpi_flow->check_extra_packets)
+ flow->check_extra_packets = 1;
+
+ if(flow->detected_protocol.app_protocol == NDPI_PROTOCOL_UNKNOWN)
+ flow->detected_protocol = ndpi_detection_giveup(workflow->ndpi_struct,
+ flow->ndpi_flow);
+ process_ndpi_collected_info(workflow, flow);
}
- if(flow->detection_completed) {
- if(flow->detected_protocol.protocol == NDPI_PROTOCOL_UNKNOWN)
- flow->detected_protocol = ndpi_detection_giveup(workflow->ndpi_struct,
- flow->ndpi_flow);
- }
-
- snprintf(flow->host_server_name, sizeof(flow->host_server_name), "%s",
- flow->ndpi_flow->host_server_name);
-
- if(flow->detected_protocol.protocol == NDPI_PROTOCOL_BITTORRENT) {
- int i, j, n = 0;
-
- for(i=0, j = 0; j < sizeof(flow->bittorent_hash)-1; i++) {
- sprintf(&flow->bittorent_hash[j], "%02x", flow->ndpi_flow->bittorent_hash[i]);
- j += 2, n += flow->ndpi_flow->bittorent_hash[i];
- }
-
- if(n == 0) flow->bittorent_hash[0] = '\0';
- }
-
- if((proto == IPPROTO_TCP) && (flow->detected_protocol.protocol != NDPI_PROTOCOL_DNS)) {
- snprintf(flow->ssl.client_certificate, sizeof(flow->ssl.client_certificate), "%s",
- flow->ndpi_flow->protos.ssl.client_certificate);
- snprintf(flow->ssl.server_certificate, sizeof(flow->ssl.server_certificate), "%s",
- flow->ndpi_flow->protos.ssl.server_certificate);
- }
-
- if(flow->detection_completed) {
- if(flow->detected_protocol.protocol == NDPI_PROTOCOL_UNKNOWN) {
- if (workflow->__flow_giveup_callback != NULL)
- workflow->__flow_giveup_callback(workflow, flow, workflow->__flow_giveup_udata);
- } else {
- if (workflow->__flow_detected_callback != NULL)
- workflow->__flow_detected_callback(workflow, flow, workflow->__flow_detected_udata);
- }
-
- ndpi_free_flow_info_half(flow);
- }
-
- return 0;
+ return(flow->detected_protocol);
}
/* ****************************************************** */
-void ndpi_workflow_process_packet (struct ndpi_workflow * workflow,
- const struct pcap_pkthdr *header,
- const u_char *packet) {
+struct ndpi_proto ndpi_workflow_process_packet (struct ndpi_workflow * workflow,
+ const struct pcap_pkthdr *header,
+ const u_char *packet) {
/*
* Declare pointers to packet headers
*/
/* --- Ethernet header --- */
const struct ndpi_ethhdr *ethernet;
/* --- LLC header --- */
- const struct ndpi_llc_header *llc;
+ const struct ndpi_llc_header_snap *llc;
/* --- Cisco HDLC header --- */
const struct ndpi_chdlc *chdlc;
@@ -562,13 +689,18 @@ void ndpi_workflow_process_packet (struct ndpi_workflow * workflow,
const struct ndpi_wifi_header *wifi;
/* --- MPLS header --- */
- struct ndpi_mpls_header *mpls;
+ union mpls {
+ uint32_t u32;
+ struct ndpi_mpls_header mpls;
+ } mpls;
/** --- IP header --- **/
struct ndpi_iphdr *iph;
/** --- IPv6 header --- **/
struct ndpi_ipv6hdr *iph6;
+ struct ndpi_proto nproto = { NDPI_PROTOCOL_UNKNOWN, NDPI_PROTOCOL_UNKNOWN };
+
/* lengths and offsets */
u_int16_t eth_offset = 0;
u_int16_t radio_len;
@@ -605,7 +737,7 @@ void ndpi_workflow_process_packet (struct ndpi_workflow * workflow,
datalink_check:
switch(datalink_type) {
- case DLT_NULL :
+ case DLT_NULL:
if(ntohl(*((u_int32_t*)&packet[eth_offset])) == 2)
type = ETH_P_IP;
else
@@ -621,15 +753,16 @@ void ndpi_workflow_process_packet (struct ndpi_workflow * workflow,
type = ntohs(chdlc->proto_code);
break;
- /* Cisco PPP with HDLC framing - 104 */
+ /* Cisco PPP - 9 or 104 */
case DLT_C_HDLC:
+ case DLT_PPP:
chdlc = (struct ndpi_chdlc *) &packet[eth_offset];
ip_offset = sizeof(struct ndpi_chdlc); /* CHDLC_OFF = 4 */
type = ntohs(chdlc->proto_code);
break;
/* IEEE 802.3 Ethernet - 1 */
- case DLT_EN10MB :
+ case DLT_EN10MB:
ethernet = (struct ndpi_ethhdr *) &packet[eth_offset];
ip_offset = sizeof(struct ndpi_ethhdr) + eth_offset;
check = ntohs(ethernet->h_proto);
@@ -640,30 +773,34 @@ void ndpi_workflow_process_packet (struct ndpi_workflow * workflow,
type = check;
if(pyld_eth_len != 0) {
+ llc = (struct ndpi_llc_header_snap *)(&packet[ip_offset]);
/* check for LLC layer with SNAP extension */
- if(packet[ip_offset] == SNAP) {
- llc = (struct ndpi_llc_header *)(&packet[ip_offset]);
+ if(llc->dsap == SNAP || llc->ssap == SNAP) {
type = llc->snap.proto_ID;
ip_offset += + 8;
}
+ /* No SNAP extension - Spanning Tree pkt must be discarted */
+ else if(llc->dsap == BSTP || llc->ssap == BSTP) {
+ goto v4_warning;
+ }
}
break;
/* Linux Cooked Capture - 113 */
- case DLT_LINUX_SLL :
+ case DLT_LINUX_SLL:
type = (packet[eth_offset+14] << 8) + packet[eth_offset+15];
ip_offset = 16 + eth_offset;
break;
/* Radiotap link-layer - 127 */
- case DLT_IEEE802_11_RADIO :
+ case DLT_IEEE802_11_RADIO:
radiotap = (struct ndpi_radiotap_header *) &packet[eth_offset];
radio_len = radiotap->len;
/* Check Bad FCS presence */
if((radiotap->flags & BAD_FCS) == BAD_FCS) {
workflow->stats.total_discarded_bytes += header->len;
- return;
+ return(nproto);
}
/* Calculate 802.11 header length (variable) */
@@ -679,12 +816,12 @@ void ndpi_workflow_process_packet (struct ndpi_workflow * workflow,
break;
/* Check ether_type from LLC */
- llc = (struct ndpi_llc_header*)(packet + eth_offset + wifi_len + radio_len);
+ llc = (struct ndpi_llc_header_snap*)(packet + eth_offset + wifi_len + radio_len);
if(llc->dsap == SNAP)
type = ntohs(llc->snap.proto_ID);
/* Set IP header offset */
- ip_offset = wifi_len + radio_len + sizeof(struct ndpi_llc_header) + eth_offset;
+ ip_offset = wifi_len + radio_len + sizeof(struct ndpi_llc_header_snap) + eth_offset;
break;
case DLT_RAW:
@@ -693,7 +830,7 @@ void ndpi_workflow_process_packet (struct ndpi_workflow * workflow,
default:
/* printf("Unknown datalink %d\n", datalink_type); */
- return;
+ return(nproto);
}
/* check ether type */
@@ -703,18 +840,24 @@ void ndpi_workflow_process_packet (struct ndpi_workflow * workflow,
type = (packet[ip_offset+2] << 8) + packet[ip_offset+3];
ip_offset += 4;
vlan_packet = 1;
+ // double tagging for 802.1Q
+ if(type == 0x8100) {
+ vlan_id = ((packet[ip_offset] << 8) + packet[ip_offset+1]) & 0xFFF;
+ type = (packet[ip_offset+2] << 8) + packet[ip_offset+3];
+ ip_offset += 4;
+ }
break;
case MPLS_UNI:
case MPLS_MULTI:
- mpls = (struct ndpi_mpls_header *) &packet[ip_offset];
- label = ntohl(mpls->label);
- /* label = ntohl(*((u_int32_t*)&packet[ip_offset])); */
+ mpls.u32 = *((uint32_t *) &packet[ip_offset]);
+ mpls.u32 = ntohl(mpls.u32);
workflow->stats.mpls_count++;
type = ETH_P_IP, ip_offset += 4;
- while((label & 0x100) != 0x100) {
+ while(!mpls.mpls.s) {
+ mpls.u32 = *((uint32_t *) &packet[ip_offset]);
+ mpls.u32 = ntohl(mpls.u32);
ip_offset += 4;
- label = ntohl(mpls->label);
}
break;
case PPPoE:
@@ -768,11 +911,11 @@ void ndpi_workflow_process_packet (struct ndpi_workflow * workflow,
}
workflow->stats.total_discarded_bytes += header->len;
- return;
+ return(nproto);
}
} else if(iph->version == 6) {
iph6 = (struct ndpi_ipv6hdr *)&packet[ip_offset];
- proto = iph6->ip6_ctlun.ip6_un1.ip6_un1_nxt;
+ proto = iph6->ip6_hdr.ip6_un1_nxt;
ip_len = sizeof(struct ndpi_ipv6hdr);
if(proto == IPPROTO_DSTOPTS /* IPv6 destination option */) {
@@ -793,7 +936,7 @@ void ndpi_workflow_process_packet (struct ndpi_workflow * workflow,
ipv4_warning_used = 1;
}
workflow->stats.total_discarded_bytes += header->len;
- return;
+ return(nproto);
}
if(workflow->prefs.decode_tunnels && (proto == IPPROTO_UDP)) {
@@ -852,7 +995,7 @@ void ndpi_workflow_process_packet (struct ndpi_workflow * workflow,
offset += tag_len;
if(offset >= header->caplen)
- return; /* Invalid packet */
+ return(nproto); /* Invalid packet */
else {
eth_offset = offset;
goto datalink_check;
@@ -863,6 +1006,56 @@ void ndpi_workflow_process_packet (struct ndpi_workflow * workflow,
}
/* process the packet */
- packet_processing(workflow, time, vlan_id, iph, iph6,
- ip_offset, header->len - ip_offset, header->len);
+ return(packet_processing(workflow, time, vlan_id, iph, iph6,
+ ip_offset, header->caplen - ip_offset, header->caplen));
}
+
+/* ********************************************************** */
+/* http://home.thep.lu.se/~bjorn/crc/crc32_fast.c */
+/* ********************************************************** */
+
+static uint32_t crc32_for_byte(uint32_t r) {
+ int j;
+ for(j = 0; j < 8; ++j)
+ r = (r & 1? 0: (uint32_t)0xEDB88320L) ^ r >> 1;
+ return r ^ (uint32_t)0xFF000000L;
+}
+
+/* Any unsigned integer type with at least 32 bits may be used as
+ * accumulator type for fast crc32-calulation, but unsigned long is
+ * probably the optimal choice for most systems. */
+typedef unsigned long accum_t;
+
+static void init_tables(uint32_t* table, uint32_t* wtable) {
+ size_t i, j, k, w;
+ for(i = 0; i < 0x100; ++i)
+ table[i] = crc32_for_byte(i);
+ for(k = 0; k < sizeof(accum_t); ++k)
+ for(i = 0; i < 0x100; ++i) {
+ for(j = w = 0; j < sizeof(accum_t); ++j)
+ w = table[(uint8_t)(j == k? w ^ i: w)] ^ w >> 8;
+ wtable[(k << 8) + i] = w ^ (k? wtable[0]: 0);
+ }
+}
+
+static void __crc32(const void* data, size_t n_bytes, uint32_t* crc) {
+ static uint32_t table[0x100], wtable[0x100*sizeof(accum_t)];
+ size_t n_accum = n_bytes/sizeof(accum_t);
+ size_t i, j;
+ if(!*table)
+ init_tables(table, wtable);
+ for(i = 0; i < n_accum; ++i) {
+ accum_t a = *crc ^ ((accum_t*)data)[i];
+ for(j = *crc = 0; j < sizeof(accum_t); ++j)
+ *crc ^= wtable[(j << 8) + (uint8_t)(a >> 8*j)];
+ }
+ for(i = n_accum*sizeof(accum_t); i < n_bytes; ++i)
+ *crc = table[(uint8_t)*crc ^ ((uint8_t*)data)[i]] ^ *crc >> 8;
+}
+
+u_int32_t ethernet_crc32(const void* data, size_t n_bytes) {
+ u_int32_t crc = 0;
+ __crc32(data, n_bytes, &crc);
+ return crc;
+}
+
diff --git a/example/ndpi_util.h b/example/ndpi_util.h
index 3168a5010..7abebe4f5 100644
--- a/example/ndpi_util.h
+++ b/example/ndpi_util.h
@@ -36,34 +36,51 @@
#define MAX_IDLE_TIME 30000
#define IDLE_SCAN_BUDGET 1024
#define NUM_ROOTS 512
+#define MAX_EXTRA_PACKETS_TO_CHECK 7
#define MAX_NDPI_FLOWS 200000000
#define TICK_RESOLUTION 1000
-
+#define MAX_NUM_IP_ADDRESS 5 /* len of ip address array */
+#define UPDATED_TREE 1
+#define AGGRESSIVE_PERCENT 95.00
+#define DIR_SRC 10
+#define DIR_DST 20
+#define PORT_ARRAY_SIZE 20
+#define HOST_ARRAY_SIZE 20
+#define FLOWS_PACKETS_THRESHOLD 0.9
+#define FLOWS_PERCENT_THRESHOLD 1.0
+#define FLOWS_PERCENT_THRESHOLD_2 0.2
+#define FLOWS_THRESHOLD 1000
+#define PKTS_PERCENT_THRESHOLD 0.1
+#define MAX_TABLE_SIZE_1 4096
+#define MAX_TABLE_SIZE_2 8192
+#define INIT_VAL -1
// flow tracking
typedef struct ndpi_flow_info {
- u_int32_t lower_ip;
- u_int32_t upper_ip;
- u_int16_t lower_port;
- u_int16_t upper_port;
- u_int8_t detection_completed, protocol;
+ u_int32_t hashval;
+ u_int32_t src_ip;
+ u_int32_t dst_ip;
+ u_int16_t src_port;
+ u_int16_t dst_port;
+ u_int8_t detection_completed, protocol, bidirectional, check_extra_packets;
u_int16_t vlan_id;
struct ndpi_flow_struct *ndpi_flow;
- char lower_name[48], upper_name[48];
+ char src_name[48], dst_name[48];
u_int8_t ip_version;
u_int64_t last_seen;
- u_int64_t bytes;
- u_int32_t packets;
+ u_int64_t src2dst_bytes, dst2src_bytes;
+ u_int32_t src2dst_packets, dst2src_packets;
// result only, not used for flow identification
ndpi_protocol detected_protocol;
+ char info[96];
char host_server_name[192];
char bittorent_hash[41];
struct {
- char client_certificate[48], server_certificate[48];
- } ssl;
+ char client_info[48], server_info[48];
+ } ssh_ssl;
void *src_id, *dst_id;
} ndpi_flow_info_t;
@@ -118,6 +135,7 @@ typedef struct ndpi_workflow {
/* allocated by prefs */
void **ndpi_flows_root;
struct ndpi_detection_module_struct *ndpi_struct;
+ u_int32_t num_allocated_flows;
} ndpi_workflow_t;
@@ -137,9 +155,9 @@ void ndpi_free_flow_info_half(struct ndpi_flow_info *flow);
/* Process a packet and update the workflow */
-void ndpi_workflow_process_packet (struct ndpi_workflow * workflow,
- const struct pcap_pkthdr *header,
- const u_char *packet);
+struct ndpi_proto ndpi_workflow_process_packet(struct ndpi_workflow * workflow,
+ const struct pcap_pkthdr *header,
+ const u_char *packet);
/* flow callbacks for complete detected flow
@@ -158,5 +176,10 @@ static inline void ndpi_workflow_set_flow_giveup_callback(struct ndpi_workflow *
/* compare two nodes in workflow */
int ndpi_workflow_node_cmp(const void *a, const void *b);
+void process_ndpi_collected_info(struct ndpi_workflow * workflow, struct ndpi_flow_info *flow);
+u_int32_t ethernet_crc32(const void* data, size_t n_bytes);
+void ndpi_flow_info_freer(void *node);
+
+extern int nDPI_LogLevel;
#endif
diff --git a/example/protos.txt b/example/protos.txt
index 4c995f543..b3f24ddb5 100644
--- a/example/protos.txt
+++ b/example/protos.txt
@@ -10,7 +10,7 @@ tcp:3000@ntop
# Format:
# host:"<value>",host:"<value>",.....@<subproto>
-host:"googlesyndacation.com"@Google
+host:"googlesyndication.com"@Google
host:"venere.com"@Venere
host:"kataweb.it",host:"repubblica.it"@Repubblica
host:"ntop"@ntop
diff --git a/example/uthash.h b/example/uthash.h
new file mode 100644
index 000000000..f78a73b86
--- /dev/null
+++ b/example/uthash.h
@@ -0,0 +1,1096 @@
+/*
+Copyright (c) 2003-2017, Troy D. Hanson http://troydhanson.github.com/uthash/
+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.
+
+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 OWNER
+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.
+*/
+
+#ifndef UTHASH_H
+#define UTHASH_H
+
+#define UTHASH_VERSION 2.0.2
+
+#include <string.h> /* memcmp,strlen */
+#include <stddef.h> /* ptrdiff_t */
+#include <stdlib.h> /* exit() */
+
+/* These macros use decltype or the earlier __typeof GNU extension.
+ As decltype is only available in newer compilers (VS2010 or gcc 4.3+
+ when compiling c++ source) this code uses whatever method is needed
+ or, for VS2008 where neither is available, uses casting workarounds. */
+#if !defined(DECLTYPE) && !defined(NO_DECLTYPE)
+#if defined(_MSC_VER) /* MS compiler */
+#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */
+#define DECLTYPE(x) (decltype(x))
+#else /* VS2008 or older (or VS2010 in C mode) */
+#define NO_DECLTYPE
+#endif
+#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__)
+#define NO_DECLTYPE
+#else /* GNU, Sun and other compilers */
+#define DECLTYPE(x) (__typeof(x))
+#endif
+#endif
+
+#ifdef NO_DECLTYPE
+#define DECLTYPE(x)
+#define DECLTYPE_ASSIGN(dst,src) \
+do { \
+ char **_da_dst = (char**)(&(dst)); \
+ *_da_dst = (char*)(src); \
+} while (0)
+#else
+#define DECLTYPE_ASSIGN(dst,src) \
+do { \
+ (dst) = DECLTYPE(dst)(src); \
+} while (0)
+#endif
+
+/* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */
+#if defined(_WIN32)
+#if defined(_MSC_VER) && _MSC_VER >= 1600
+#include <stdint.h>
+#elif defined(__WATCOMC__) || defined(__MINGW32__) || defined(__CYGWIN__)
+#include <stdint.h>
+#else
+typedef unsigned int uint32_t;
+typedef unsigned char uint8_t;
+#endif
+#elif defined(__GNUC__) && !defined(__VXWORKS__)
+#include <stdint.h>
+#else
+typedef unsigned int uint32_t;
+typedef unsigned char uint8_t;
+#endif
+
+#ifndef uthash_fatal
+#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */
+#endif
+#ifndef uthash_malloc
+#define uthash_malloc(sz) malloc(sz) /* malloc fcn */
+#endif
+#ifndef uthash_free
+#define uthash_free(ptr,sz) free(ptr) /* free fcn */
+#endif
+#ifndef uthash_strlen
+#define uthash_strlen(s) strlen(s)
+#endif
+#ifndef uthash_memcmp
+#define uthash_memcmp(a,b,n) memcmp(a,b,n)
+#endif
+
+#ifndef uthash_noexpand_fyi
+#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */
+#endif
+#ifndef uthash_expand_fyi
+#define uthash_expand_fyi(tbl) /* can be defined to log expands */
+#endif
+
+/* initial number of buckets */
+#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */
+#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */
+#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */
+
+/* calculate the element whose hash handle address is hhp */
+#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))
+/* calculate the hash handle from element address elp */
+#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle *)(((char*)(elp)) + ((tbl)->hho)))
+
+#define HASH_VALUE(keyptr,keylen,hashv) \
+do { \
+ HASH_FCN(keyptr, keylen, hashv); \
+} while (0)
+
+#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \
+do { \
+ (out) = NULL; \
+ if (head) { \
+ unsigned _hf_bkt; \
+ HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \
+ if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) { \
+ HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \
+ } \
+ } \
+} while (0)
+
+#define HASH_FIND(hh,head,keyptr,keylen,out) \
+do { \
+ unsigned _hf_hashv; \
+ HASH_VALUE(keyptr, keylen, _hf_hashv); \
+ HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \
+} while (0)
+
+#ifdef HASH_BLOOM
+#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM)
+#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL)
+#define HASH_BLOOM_MAKE(tbl) \
+do { \
+ (tbl)->bloom_nbits = HASH_BLOOM; \
+ (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \
+ if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \
+ memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \
+ (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \
+} while (0)
+
+#define HASH_BLOOM_FREE(tbl) \
+do { \
+ uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \
+} while (0)
+
+#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U)))
+#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U)))
+
+#define HASH_BLOOM_ADD(tbl,hashv) \
+ HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U)))
+
+#define HASH_BLOOM_TEST(tbl,hashv) \
+ HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U)))
+
+#else
+#define HASH_BLOOM_MAKE(tbl)
+#define HASH_BLOOM_FREE(tbl)
+#define HASH_BLOOM_ADD(tbl,hashv)
+#define HASH_BLOOM_TEST(tbl,hashv) (1)
+#define HASH_BLOOM_BYTELEN 0U
+#endif
+
+#define HASH_MAKE_TABLE(hh,head) \
+do { \
+ (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \
+ sizeof(UT_hash_table)); \
+ if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \
+ memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \
+ (head)->hh.tbl->tail = &((head)->hh); \
+ (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \
+ (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \
+ (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \
+ (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \
+ HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
+ if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \
+ memset((head)->hh.tbl->buckets, 0, \
+ HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
+ HASH_BLOOM_MAKE((head)->hh.tbl); \
+ (head)->hh.tbl->signature = HASH_SIGNATURE; \
+} while (0)
+
+#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \
+do { \
+ (replaced) = NULL; \
+ HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \
+ if (replaced) { \
+ HASH_DELETE(hh, head, replaced); \
+ } \
+ HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \
+} while (0)
+
+#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \
+do { \
+ (replaced) = NULL; \
+ HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \
+ if (replaced) { \
+ HASH_DELETE(hh, head, replaced); \
+ } \
+ HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \
+} while (0)
+
+#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \
+do { \
+ unsigned _hr_hashv; \
+ HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \
+ HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \
+} while (0)
+
+#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \
+do { \
+ unsigned _hr_hashv; \
+ HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \
+ HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \
+} while (0)
+
+#define HASH_APPEND_LIST(hh, head, add) \
+do { \
+ (add)->hh.next = NULL; \
+ (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \
+ (head)->hh.tbl->tail->next = (add); \
+ (head)->hh.tbl->tail = &((add)->hh); \
+} while (0)
+
+#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \
+do { \
+ do { \
+ if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) \
+ break; \
+ } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \
+} while (0)
+
+#ifdef NO_DECLTYPE
+#undef HASH_AKBI_INNER_LOOP
+#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \
+do { \
+ char *_hs_saved_head = (char*)(head); \
+ do { \
+ DECLTYPE_ASSIGN(head, _hs_iter); \
+ if (cmpfcn(head, add) > 0) { \
+ DECLTYPE_ASSIGN(head, _hs_saved_head); \
+ break; \
+ } \
+ DECLTYPE_ASSIGN(head, _hs_saved_head); \
+ } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \
+} while (0)
+#endif
+
+#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \
+do { \
+ unsigned _ha_bkt; \
+ (add)->hh.hashv = (hashval); \
+ (add)->hh.key = (char*) (keyptr); \
+ (add)->hh.keylen = (unsigned) (keylen_in); \
+ if (!(head)) { \
+ (add)->hh.next = NULL; \
+ (add)->hh.prev = NULL; \
+ (head) = (add); \
+ HASH_MAKE_TABLE(hh, head); \
+ } else { \
+ void *_hs_iter = (head); \
+ (add)->hh.tbl = (head)->hh.tbl; \
+ HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn); \
+ if (_hs_iter) { \
+ (add)->hh.next = _hs_iter; \
+ if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) { \
+ HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add); \
+ } else { \
+ (head) = (add); \
+ } \
+ HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add); \
+ } else { \
+ HASH_APPEND_LIST(hh, head, add); \
+ } \
+ } \
+ (head)->hh.tbl->num_items++; \
+ HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \
+ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], &(add)->hh); \
+ HASH_BLOOM_ADD((head)->hh.tbl, hashval); \
+ HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \
+ HASH_FSCK(hh, head); \
+} while (0)
+
+#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \
+do { \
+ unsigned _hs_hashv; \
+ HASH_VALUE(keyptr, keylen_in, _hs_hashv); \
+ HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \
+} while (0)
+
+#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \
+ HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn)
+
+#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \
+ HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn)
+
+#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \
+do { \
+ unsigned _ha_bkt; \
+ (add)->hh.hashv = (hashval); \
+ (add)->hh.key = (char*) (keyptr); \
+ (add)->hh.keylen = (unsigned) (keylen_in); \
+ if (!(head)) { \
+ (add)->hh.next = NULL; \
+ (add)->hh.prev = NULL; \
+ (head) = (add); \
+ HASH_MAKE_TABLE(hh, head); \
+ } else { \
+ (add)->hh.tbl = (head)->hh.tbl; \
+ HASH_APPEND_LIST(hh, head, add); \
+ } \
+ (head)->hh.tbl->num_items++; \
+ HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \
+ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], &(add)->hh); \
+ HASH_BLOOM_ADD((head)->hh.tbl, hashval); \
+ HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \
+ HASH_FSCK(hh, head); \
+} while (0)
+
+#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \
+do { \
+ unsigned _ha_hashv; \
+ HASH_VALUE(keyptr, keylen_in, _ha_hashv); \
+ HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \
+} while (0)
+
+#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \
+ HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add)
+
+#define HASH_ADD(hh,head,fieldname,keylen_in,add) \
+ HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add)
+
+#define HASH_TO_BKT(hashv,num_bkts,bkt) \
+do { \
+ bkt = ((hashv) & ((num_bkts) - 1U)); \
+} while (0)
+
+/* delete "delptr" from the hash table.
+ * "the usual" patch-up process for the app-order doubly-linked-list.
+ * The use of _hd_hh_del below deserves special explanation.
+ * These used to be expressed using (delptr) but that led to a bug
+ * if someone used the same symbol for the head and deletee, like
+ * HASH_DELETE(hh,users,users);
+ * We want that to work, but by changing the head (users) below
+ * we were forfeiting our ability to further refer to the deletee (users)
+ * in the patch-up process. Solution: use scratch space to
+ * copy the deletee pointer, then the latter references are via that
+ * scratch pointer rather than through the repointed (users) symbol.
+ */
+#define HASH_DELETE(hh,head,delptr) \
+do { \
+ struct UT_hash_handle *_hd_hh_del; \
+ if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \
+ uthash_free((head)->hh.tbl->buckets, \
+ (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
+ HASH_BLOOM_FREE((head)->hh.tbl); \
+ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
+ head = NULL; \
+ } else { \
+ unsigned _hd_bkt; \
+ _hd_hh_del = &((delptr)->hh); \
+ if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \
+ (head)->hh.tbl->tail = \
+ (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \
+ (head)->hh.tbl->hho); \
+ } \
+ if ((delptr)->hh.prev != NULL) { \
+ ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \
+ (head)->hh.tbl->hho))->next = (delptr)->hh.next; \
+ } else { \
+ DECLTYPE_ASSIGN(head,(delptr)->hh.next); \
+ } \
+ if (_hd_hh_del->next != NULL) { \
+ ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \
+ (head)->hh.tbl->hho))->prev = \
+ _hd_hh_del->prev; \
+ } \
+ HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \
+ HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \
+ (head)->hh.tbl->num_items--; \
+ } \
+ HASH_FSCK(hh,head); \
+} while (0)
+
+
+/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
+#define HASH_FIND_STR(head,findstr,out) \
+ HASH_FIND(hh,head,findstr,(unsigned)uthash_strlen(findstr),out)
+#define HASH_ADD_STR(head,strfield,add) \
+ HASH_ADD(hh,head,strfield[0],(unsigned)uthash_strlen(add->strfield),add)
+#define HASH_REPLACE_STR(head,strfield,add,replaced) \
+ HASH_REPLACE(hh,head,strfield[0],(unsigned)uthash_strlen(add->strfield),add,replaced)
+#define HASH_FIND_INT(head,findint,out) \
+ HASH_FIND(hh,head,findint,sizeof(int),out)
+#define HASH_ADD_INT(head,intfield,add) \
+ HASH_ADD(hh,head,intfield,sizeof(int),add)
+#define HASH_REPLACE_INT(head,intfield,add,replaced) \
+ HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced)
+#define HASH_FIND_PTR(head,findptr,out) \
+ HASH_FIND(hh,head,findptr,sizeof(void *),out)
+#define HASH_ADD_PTR(head,ptrfield,add) \
+ HASH_ADD(hh,head,ptrfield,sizeof(void *),add)
+#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \
+ HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced)
+#define HASH_DEL(head,delptr) \
+ HASH_DELETE(hh,head,delptr)
+
+/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
+ * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
+ */
+#ifdef HASH_DEBUG
+#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0)
+#define HASH_FSCK(hh,head) \
+do { \
+ struct UT_hash_handle *_thh; \
+ if (head) { \
+ unsigned _bkt_i; \
+ unsigned _count; \
+ char *_prev; \
+ _count = 0; \
+ for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \
+ unsigned _bkt_count = 0; \
+ _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \
+ _prev = NULL; \
+ while (_thh) { \
+ if (_prev != (char*)(_thh->hh_prev)) { \
+ HASH_OOPS("invalid hh_prev %p, actual %p\n", \
+ _thh->hh_prev, _prev ); \
+ } \
+ _bkt_count++; \
+ _prev = (char*)(_thh); \
+ _thh = _thh->hh_next; \
+ } \
+ _count += _bkt_count; \
+ if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \
+ HASH_OOPS("invalid bucket count %u, actual %u\n", \
+ (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \
+ } \
+ } \
+ if (_count != (head)->hh.tbl->num_items) { \
+ HASH_OOPS("invalid hh item count %u, actual %u\n", \
+ (head)->hh.tbl->num_items, _count ); \
+ } \
+ /* traverse hh in app order; check next/prev integrity, count */ \
+ _count = 0; \
+ _prev = NULL; \
+ _thh = &(head)->hh; \
+ while (_thh) { \
+ _count++; \
+ if (_prev !=(char*)(_thh->prev)) { \
+ HASH_OOPS("invalid prev %p, actual %p\n", \
+ _thh->prev, _prev ); \
+ } \
+ _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \
+ _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \
+ (head)->hh.tbl->hho) : NULL ); \
+ } \
+ if (_count != (head)->hh.tbl->num_items) { \
+ HASH_OOPS("invalid app item count %u, actual %u\n", \
+ (head)->hh.tbl->num_items, _count ); \
+ } \
+ } \
+} while (0)
+#else
+#define HASH_FSCK(hh,head)
+#endif
+
+/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to
+ * the descriptor to which this macro is defined for tuning the hash function.
+ * The app can #include <unistd.h> to get the prototype for write(2). */
+#ifdef HASH_EMIT_KEYS
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \
+do { \
+ unsigned _klen = fieldlen; \
+ write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \
+ write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \
+} while (0)
+#else
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)
+#endif
+
+/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */
+#ifdef HASH_FUNCTION
+#define HASH_FCN HASH_FUNCTION
+#else
+#define HASH_FCN HASH_JEN
+#endif
+
+/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */
+#define HASH_BER(key,keylen,hashv) \
+do { \
+ unsigned _hb_keylen=(unsigned)keylen; \
+ const unsigned char *_hb_key=(const unsigned char*)(key); \
+ (hashv) = 0; \
+ while (_hb_keylen-- != 0U) { \
+ (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \
+ } \
+} while (0)
+
+
+/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at
+ * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */
+#define HASH_SAX(key,keylen,hashv) \
+do { \
+ unsigned _sx_i; \
+ const unsigned char *_hs_key=(const unsigned char*)(key); \
+ hashv = 0; \
+ for(_sx_i=0; _sx_i < keylen; _sx_i++) { \
+ hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \
+ } \
+} while (0)
+/* FNV-1a variation */
+#define HASH_FNV(key,keylen,hashv) \
+do { \
+ unsigned _fn_i; \
+ const unsigned char *_hf_key=(const unsigned char*)(key); \
+ hashv = 2166136261U; \
+ for(_fn_i=0; _fn_i < keylen; _fn_i++) { \
+ hashv = hashv ^ _hf_key[_fn_i]; \
+ hashv = hashv * 16777619U; \
+ } \
+} while (0)
+
+#define HASH_OAT(key,keylen,hashv) \
+do { \
+ unsigned _ho_i; \
+ const unsigned char *_ho_key=(const unsigned char*)(key); \
+ hashv = 0; \
+ for(_ho_i=0; _ho_i < keylen; _ho_i++) { \
+ hashv += _ho_key[_ho_i]; \
+ hashv += (hashv << 10); \
+ hashv ^= (hashv >> 6); \
+ } \
+ hashv += (hashv << 3); \
+ hashv ^= (hashv >> 11); \
+ hashv += (hashv << 15); \
+} while (0)
+
+#define HASH_JEN_MIX(a,b,c) \
+do { \
+ a -= b; a -= c; a ^= ( c >> 13 ); \
+ b -= c; b -= a; b ^= ( a << 8 ); \
+ c -= a; c -= b; c ^= ( b >> 13 ); \
+ a -= b; a -= c; a ^= ( c >> 12 ); \
+ b -= c; b -= a; b ^= ( a << 16 ); \
+ c -= a; c -= b; c ^= ( b >> 5 ); \
+ a -= b; a -= c; a ^= ( c >> 3 ); \
+ b -= c; b -= a; b ^= ( a << 10 ); \
+ c -= a; c -= b; c ^= ( b >> 15 ); \
+} while (0)
+
+#define HASH_JEN(key,keylen,hashv) \
+do { \
+ unsigned _hj_i,_hj_j,_hj_k; \
+ unsigned const char *_hj_key=(unsigned const char*)(key); \
+ hashv = 0xfeedbeefu; \
+ _hj_i = _hj_j = 0x9e3779b9u; \
+ _hj_k = (unsigned)(keylen); \
+ while (_hj_k >= 12U) { \
+ _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \
+ + ( (unsigned)_hj_key[2] << 16 ) \
+ + ( (unsigned)_hj_key[3] << 24 ) ); \
+ _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \
+ + ( (unsigned)_hj_key[6] << 16 ) \
+ + ( (unsigned)_hj_key[7] << 24 ) ); \
+ hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \
+ + ( (unsigned)_hj_key[10] << 16 ) \
+ + ( (unsigned)_hj_key[11] << 24 ) ); \
+ \
+ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
+ \
+ _hj_key += 12; \
+ _hj_k -= 12U; \
+ } \
+ hashv += (unsigned)(keylen); \
+ switch ( _hj_k ) { \
+ case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \
+ case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \
+ case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \
+ case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \
+ case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \
+ case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \
+ case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \
+ case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \
+ case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \
+ case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \
+ case 1: _hj_i += _hj_key[0]; \
+ } \
+ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
+} while (0)
+
+/* The Paul Hsieh hash function */
+#undef get16bits
+#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
+ || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
+#define get16bits(d) (*((const uint16_t *) (d)))
+#endif
+
+#if !defined (get16bits)
+#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \
+ +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+#define HASH_SFH(key,keylen,hashv) \
+do { \
+ unsigned const char *_sfh_key=(unsigned const char*)(key); \
+ uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \
+ \
+ unsigned _sfh_rem = _sfh_len & 3U; \
+ _sfh_len >>= 2; \
+ hashv = 0xcafebabeu; \
+ \
+ /* Main loop */ \
+ for (;_sfh_len > 0U; _sfh_len--) { \
+ hashv += get16bits (_sfh_key); \
+ _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \
+ hashv = (hashv << 16) ^ _sfh_tmp; \
+ _sfh_key += 2U*sizeof (uint16_t); \
+ hashv += hashv >> 11; \
+ } \
+ \
+ /* Handle end cases */ \
+ switch (_sfh_rem) { \
+ case 3: hashv += get16bits (_sfh_key); \
+ hashv ^= hashv << 16; \
+ hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \
+ hashv += hashv >> 11; \
+ break; \
+ case 2: hashv += get16bits (_sfh_key); \
+ hashv ^= hashv << 11; \
+ hashv += hashv >> 17; \
+ break; \
+ case 1: hashv += *_sfh_key; \
+ hashv ^= hashv << 10; \
+ hashv += hashv >> 1; \
+ } \
+ \
+ /* Force "avalanching" of final 127 bits */ \
+ hashv ^= hashv << 3; \
+ hashv += hashv >> 5; \
+ hashv ^= hashv << 4; \
+ hashv += hashv >> 17; \
+ hashv ^= hashv << 25; \
+ hashv += hashv >> 6; \
+} while (0)
+
+#ifdef HASH_USING_NO_STRICT_ALIASING
+/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads.
+ * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error.
+ * MurmurHash uses the faster approach only on CPU's where we know it's safe.
+ *
+ * Note the preprocessor built-in defines can be emitted using:
+ *
+ * gcc -m64 -dM -E - < /dev/null (on gcc)
+ * cc -## a.c (where a.c is a simple test file) (Sun Studio)
+ */
+#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86))
+#define MUR_GETBLOCK(p,i) p[i]
+#else /* non intel */
+#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 3UL) == 0UL)
+#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 3UL) == 1UL)
+#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 3UL) == 2UL)
+#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 3UL) == 3UL)
+#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL))
+#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__))
+#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24))
+#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16))
+#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8))
+#else /* assume little endian non-intel */
+#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24))
+#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16))
+#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8))
+#endif
+#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \
+ (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \
+ (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \
+ MUR_ONE_THREE(p))))
+#endif
+#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r))))
+#define MUR_FMIX(_h) \
+do { \
+ _h ^= _h >> 16; \
+ _h *= 0x85ebca6bu; \
+ _h ^= _h >> 13; \
+ _h *= 0xc2b2ae35u; \
+ _h ^= _h >> 16; \
+} while (0)
+
+#define HASH_MUR(key,keylen,hashv) \
+do { \
+ const uint8_t *_mur_data = (const uint8_t*)(key); \
+ const int _mur_nblocks = (int)(keylen) / 4; \
+ uint32_t _mur_h1 = 0xf88D5353u; \
+ uint32_t _mur_c1 = 0xcc9e2d51u; \
+ uint32_t _mur_c2 = 0x1b873593u; \
+ uint32_t _mur_k1 = 0; \
+ const uint8_t *_mur_tail; \
+ const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+(_mur_nblocks*4)); \
+ int _mur_i; \
+ for(_mur_i = -_mur_nblocks; _mur_i!=0; _mur_i++) { \
+ _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \
+ _mur_k1 *= _mur_c1; \
+ _mur_k1 = MUR_ROTL32(_mur_k1,15); \
+ _mur_k1 *= _mur_c2; \
+ \
+ _mur_h1 ^= _mur_k1; \
+ _mur_h1 = MUR_ROTL32(_mur_h1,13); \
+ _mur_h1 = (_mur_h1*5U) + 0xe6546b64u; \
+ } \
+ _mur_tail = (const uint8_t*)(_mur_data + (_mur_nblocks*4)); \
+ _mur_k1=0; \
+ switch((keylen) & 3U) { \
+ case 3: _mur_k1 ^= (uint32_t)_mur_tail[2] << 16; /* FALLTHROUGH */ \
+ case 2: _mur_k1 ^= (uint32_t)_mur_tail[1] << 8; /* FALLTHROUGH */ \
+ case 1: _mur_k1 ^= (uint32_t)_mur_tail[0]; \
+ _mur_k1 *= _mur_c1; \
+ _mur_k1 = MUR_ROTL32(_mur_k1,15); \
+ _mur_k1 *= _mur_c2; \
+ _mur_h1 ^= _mur_k1; \
+ } \
+ _mur_h1 ^= (uint32_t)(keylen); \
+ MUR_FMIX(_mur_h1); \
+ hashv = _mur_h1; \
+} while (0)
+#endif /* HASH_USING_NO_STRICT_ALIASING */
+
+/* iterate over items in a known bucket to find desired item */
+#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \
+do { \
+ if ((head).hh_head != NULL) { \
+ DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \
+ } else { \
+ (out) = NULL; \
+ } \
+ while ((out) != NULL) { \
+ if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \
+ if (uthash_memcmp((out)->hh.key, keyptr, keylen_in) == 0) { \
+ break; \
+ } \
+ } \
+ if ((out)->hh.hh_next != NULL) { \
+ DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \
+ } else { \
+ (out) = NULL; \
+ } \
+ } \
+} while (0)
+
+/* add an item to a bucket */
+#define HASH_ADD_TO_BKT(head,addhh) \
+do { \
+ head.count++; \
+ (addhh)->hh_next = head.hh_head; \
+ (addhh)->hh_prev = NULL; \
+ if (head.hh_head != NULL) { (head).hh_head->hh_prev = (addhh); } \
+ (head).hh_head=addhh; \
+ if ((head.count >= ((head.expand_mult+1U) * HASH_BKT_CAPACITY_THRESH)) \
+ && ((addhh)->tbl->noexpand != 1U)) { \
+ HASH_EXPAND_BUCKETS((addhh)->tbl); \
+ } \
+} while (0)
+
+/* remove an item from a given bucket */
+#define HASH_DEL_IN_BKT(hh,head,hh_del) \
+ (head).count--; \
+ if ((head).hh_head == hh_del) { \
+ (head).hh_head = hh_del->hh_next; \
+ } \
+ if (hh_del->hh_prev) { \
+ hh_del->hh_prev->hh_next = hh_del->hh_next; \
+ } \
+ if (hh_del->hh_next) { \
+ hh_del->hh_next->hh_prev = hh_del->hh_prev; \
+ }
+
+/* Bucket expansion has the effect of doubling the number of buckets
+ * and redistributing the items into the new buckets. Ideally the
+ * items will distribute more or less evenly into the new buckets
+ * (the extent to which this is true is a measure of the quality of
+ * the hash function as it applies to the key domain).
+ *
+ * With the items distributed into more buckets, the chain length
+ * (item count) in each bucket is reduced. Thus by expanding buckets
+ * the hash keeps a bound on the chain length. This bounded chain
+ * length is the essence of how a hash provides constant time lookup.
+ *
+ * The calculation of tbl->ideal_chain_maxlen below deserves some
+ * explanation. First, keep in mind that we're calculating the ideal
+ * maximum chain length based on the *new* (doubled) bucket count.
+ * In fractions this is just n/b (n=number of items,b=new num buckets).
+ * Since the ideal chain length is an integer, we want to calculate
+ * ceil(n/b). We don't depend on floating point arithmetic in this
+ * hash, so to calculate ceil(n/b) with integers we could write
+ *
+ * ceil(n/b) = (n/b) + ((n%b)?1:0)
+ *
+ * and in fact a previous version of this hash did just that.
+ * But now we have improved things a bit by recognizing that b is
+ * always a power of two. We keep its base 2 log handy (call it lb),
+ * so now we can write this with a bit shift and logical AND:
+ *
+ * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
+ *
+ */
+#define HASH_EXPAND_BUCKETS(tbl) \
+do { \
+ unsigned _he_bkt; \
+ unsigned _he_bkt_i; \
+ struct UT_hash_handle *_he_thh, *_he_hh_nxt; \
+ UT_hash_bucket *_he_new_buckets, *_he_newbkt; \
+ _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \
+ 2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
+ if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \
+ memset(_he_new_buckets, 0, \
+ 2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
+ tbl->ideal_chain_maxlen = \
+ (tbl->num_items >> (tbl->log2_num_buckets+1U)) + \
+ (((tbl->num_items & ((tbl->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \
+ tbl->nonideal_items = 0; \
+ for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \
+ { \
+ _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \
+ while (_he_thh != NULL) { \
+ _he_hh_nxt = _he_thh->hh_next; \
+ HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2U, _he_bkt); \
+ _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \
+ if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \
+ tbl->nonideal_items++; \
+ _he_newbkt->expand_mult = _he_newbkt->count / \
+ tbl->ideal_chain_maxlen; \
+ } \
+ _he_thh->hh_prev = NULL; \
+ _he_thh->hh_next = _he_newbkt->hh_head; \
+ if (_he_newbkt->hh_head != NULL) { _he_newbkt->hh_head->hh_prev = \
+ _he_thh; } \
+ _he_newbkt->hh_head = _he_thh; \
+ _he_thh = _he_hh_nxt; \
+ } \
+ } \
+ uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
+ tbl->num_buckets *= 2U; \
+ tbl->log2_num_buckets++; \
+ tbl->buckets = _he_new_buckets; \
+ tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \
+ (tbl->ineff_expands+1U) : 0U; \
+ if (tbl->ineff_expands > 1U) { \
+ tbl->noexpand=1; \
+ uthash_noexpand_fyi(tbl); \
+ } \
+ uthash_expand_fyi(tbl); \
+} while (0)
+
+
+/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
+/* Note that HASH_SORT assumes the hash handle name to be hh.
+ * HASH_SRT was added to allow the hash handle name to be passed in. */
+#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
+#define HASH_SRT(hh,head,cmpfcn) \
+do { \
+ unsigned _hs_i; \
+ unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \
+ struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \
+ if (head != NULL) { \
+ _hs_insize = 1; \
+ _hs_looping = 1; \
+ _hs_list = &((head)->hh); \
+ while (_hs_looping != 0U) { \
+ _hs_p = _hs_list; \
+ _hs_list = NULL; \
+ _hs_tail = NULL; \
+ _hs_nmerges = 0; \
+ while (_hs_p != NULL) { \
+ _hs_nmerges++; \
+ _hs_q = _hs_p; \
+ _hs_psize = 0; \
+ for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \
+ _hs_psize++; \
+ _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \
+ ((void*)((char*)(_hs_q->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ if (! (_hs_q) ) { break; } \
+ } \
+ _hs_qsize = _hs_insize; \
+ while ((_hs_psize > 0U) || ((_hs_qsize > 0U) && (_hs_q != NULL))) {\
+ if (_hs_psize == 0U) { \
+ _hs_e = _hs_q; \
+ _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \
+ ((void*)((char*)(_hs_q->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ _hs_qsize--; \
+ } else if ( (_hs_qsize == 0U) || (_hs_q == NULL) ) { \
+ _hs_e = _hs_p; \
+ if (_hs_p != NULL){ \
+ _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ? \
+ ((void*)((char*)(_hs_p->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ } \
+ _hs_psize--; \
+ } else if (( \
+ cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \
+ DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \
+ ) <= 0) { \
+ _hs_e = _hs_p; \
+ if (_hs_p != NULL){ \
+ _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ? \
+ ((void*)((char*)(_hs_p->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ } \
+ _hs_psize--; \
+ } else { \
+ _hs_e = _hs_q; \
+ _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \
+ ((void*)((char*)(_hs_q->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ _hs_qsize--; \
+ } \
+ if ( _hs_tail != NULL ) { \
+ _hs_tail->next = ((_hs_e != NULL) ? \
+ ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \
+ } else { \
+ _hs_list = _hs_e; \
+ } \
+ if (_hs_e != NULL) { \
+ _hs_e->prev = ((_hs_tail != NULL) ? \
+ ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \
+ } \
+ _hs_tail = _hs_e; \
+ } \
+ _hs_p = _hs_q; \
+ } \
+ if (_hs_tail != NULL){ \
+ _hs_tail->next = NULL; \
+ } \
+ if ( _hs_nmerges <= 1U ) { \
+ _hs_looping=0; \
+ (head)->hh.tbl->tail = _hs_tail; \
+ DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \
+ } \
+ _hs_insize *= 2U; \
+ } \
+ HASH_FSCK(hh,head); \
+ } \
+} while (0)
+
+/* This function selects items from one hash into another hash.
+ * The end result is that the selected items have dual presence
+ * in both hashes. There is no copy of the items made; rather
+ * they are added into the new hash through a secondary hash
+ * hash handle that must be present in the structure. */
+#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \
+do { \
+ unsigned _src_bkt, _dst_bkt; \
+ void *_last_elt=NULL, *_elt; \
+ UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \
+ ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \
+ if (src != NULL) { \
+ for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \
+ for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \
+ _src_hh != NULL; \
+ _src_hh = _src_hh->hh_next) { \
+ _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \
+ if (cond(_elt)) { \
+ _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \
+ _dst_hh->key = _src_hh->key; \
+ _dst_hh->keylen = _src_hh->keylen; \
+ _dst_hh->hashv = _src_hh->hashv; \
+ _dst_hh->prev = _last_elt; \
+ _dst_hh->next = NULL; \
+ if (_last_elt_hh != NULL) { _last_elt_hh->next = _elt; } \
+ if (dst == NULL) { \
+ DECLTYPE_ASSIGN(dst,_elt); \
+ HASH_MAKE_TABLE(hh_dst,dst); \
+ } else { \
+ _dst_hh->tbl = (dst)->hh_dst.tbl; \
+ } \
+ HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \
+ HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \
+ (dst)->hh_dst.tbl->num_items++; \
+ _last_elt = _elt; \
+ _last_elt_hh = _dst_hh; \
+ } \
+ } \
+ } \
+ } \
+ HASH_FSCK(hh_dst,dst); \
+} while (0)
+
+#define HASH_CLEAR(hh,head) \
+do { \
+ if (head != NULL) { \
+ uthash_free((head)->hh.tbl->buckets, \
+ (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \
+ HASH_BLOOM_FREE((head)->hh.tbl); \
+ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
+ (head)=NULL; \
+ } \
+} while (0)
+
+#define HASH_OVERHEAD(hh,head) \
+ ((head != NULL) ? ( \
+ (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \
+ ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \
+ sizeof(UT_hash_table) + \
+ (HASH_BLOOM_BYTELEN))) : 0U)
+
+#ifdef NO_DECLTYPE
+#define HASH_ITER(hh,head,el,tmp) \
+for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \
+ (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL)))
+#else
+#define HASH_ITER(hh,head,el,tmp) \
+for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \
+ (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL)))
+#endif
+
+/* obtain a count of items in the hash */
+#define HASH_COUNT(head) HASH_CNT(hh,head)
+#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U)
+
+typedef struct UT_hash_bucket {
+ struct UT_hash_handle *hh_head;
+ unsigned count;
+
+ /* expand_mult is normally set to 0. In this situation, the max chain length
+ * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
+ * the bucket's chain exceeds this length, bucket expansion is triggered).
+ * However, setting expand_mult to a non-zero value delays bucket expansion
+ * (that would be triggered by additions to this particular bucket)
+ * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
+ * (The multiplier is simply expand_mult+1). The whole idea of this
+ * multiplier is to reduce bucket expansions, since they are expensive, in
+ * situations where we know that a particular bucket tends to be overused.
+ * It is better to let its chain length grow to a longer yet-still-bounded
+ * value, than to do an O(n) bucket expansion too often.
+ */
+ unsigned expand_mult;
+
+} UT_hash_bucket;
+
+/* random signature used only to find hash tables in external analysis */
+#define HASH_SIGNATURE 0xa0111fe1u
+#define HASH_BLOOM_SIGNATURE 0xb12220f2u
+
+typedef struct UT_hash_table {
+ UT_hash_bucket *buckets;
+ unsigned num_buckets, log2_num_buckets;
+ unsigned num_items;
+ struct UT_hash_handle *tail; /* tail hh in app order, for fast append */
+ ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */
+
+ /* in an ideal situation (all buckets used equally), no bucket would have
+ * more than ceil(#items/#buckets) items. that's the ideal chain length. */
+ unsigned ideal_chain_maxlen;
+
+ /* nonideal_items is the number of items in the hash whose chain position
+ * exceeds the ideal chain maxlen. these items pay the penalty for an uneven
+ * hash distribution; reaching them in a chain traversal takes >ideal steps */
+ unsigned nonideal_items;
+
+ /* ineffective expands occur when a bucket doubling was performed, but
+ * afterward, more than half the items in the hash had nonideal chain
+ * positions. If this happens on two consecutive expansions we inhibit any
+ * further expansion, as it's not helping; this happens when the hash
+ * function isn't a good fit for the key domain. When expansion is inhibited
+ * the hash will still work, albeit no longer in constant time. */
+ unsigned ineff_expands, noexpand;
+
+ uint32_t signature; /* used only to find hash tables in external analysis */
+#ifdef HASH_BLOOM
+ uint32_t bloom_sig; /* used only to test bloom exists in external analysis */
+ uint8_t *bloom_bv;
+ uint8_t bloom_nbits;
+#endif
+
+} UT_hash_table;
+
+typedef struct UT_hash_handle {
+ struct UT_hash_table *tbl;
+ void *prev; /* prev element in app order */
+ void *next; /* next element in app order */
+ struct UT_hash_handle *hh_prev; /* previous hh in bucket order */
+ struct UT_hash_handle *hh_next; /* next hh in bucket order */
+ void *key; /* ptr to enclosing struct's key */
+ unsigned keylen; /* enclosing struct's key len */
+ unsigned hashv; /* result of hash-fcn(key) */
+} UT_hash_handle;
+
+#endif /* UTHASH_H */