diff options
Diffstat (limited to 'example')
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 Binary files differnew file mode 100644 index 000000000..133df898b --- /dev/null +++ b/example/MacOS/ndpiExample/ndpiExample/capture.pcap 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 */ |