416 lines
10 KiB
C++
416 lines
10 KiB
C++
|
|
/*
|
||
|
|
*
|
||
|
|
* Copyright (c) 2011-2016 The University of Waikato, Hamilton, New Zealand.
|
||
|
|
* All rights reserved.
|
||
|
|
*
|
||
|
|
* This file is part of libprotoident.
|
||
|
|
*
|
||
|
|
* This code has been developed by the University of Waikato WAND
|
||
|
|
* research group. For further information please see http://www.wand.net.nz/
|
||
|
|
*
|
||
|
|
* libprotoident 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.
|
||
|
|
*
|
||
|
|
* libprotoident is distributed in the hope that 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
|
*
|
||
|
|
*
|
||
|
|
*/
|
||
|
|
|
||
|
|
#define __STDC_FORMAT_MACROS
|
||
|
|
#define __STDC_LIMIT_MACROS
|
||
|
|
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <assert.h>
|
||
|
|
//#include <libtrace.h>
|
||
|
|
#include <inttypes.h>
|
||
|
|
#include <sys/types.h>
|
||
|
|
#include <stdint.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <signal.h>
|
||
|
|
|
||
|
|
#include "libprotoident.h"
|
||
|
|
#include "proto_manager.h"
|
||
|
|
|
||
|
|
bool init_called = false;
|
||
|
|
LPIModuleMap TCP_protocols;
|
||
|
|
LPIModuleMap UDP_protocols;
|
||
|
|
|
||
|
|
lpi_module_t *lpi_icmp = NULL;
|
||
|
|
lpi_module_t *lpi_unsupported = NULL;
|
||
|
|
lpi_module_t *lpi_unknown_tcp = NULL;
|
||
|
|
lpi_module_t *lpi_unknown_udp = NULL;
|
||
|
|
|
||
|
|
static LPINameMap lpi_names;
|
||
|
|
static LPIProtocolMap lpi_protocols;
|
||
|
|
static LPICategoryMap lpi_categories;
|
||
|
|
static LPICategoryProtocolMap lpi_category_protocols;
|
||
|
|
|
||
|
|
int lpi_init_library(int level) {
|
||
|
|
|
||
|
|
if (init_called) {
|
||
|
|
fprintf(stderr, "WARNING: lpi_init_library has already been called\n");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (register_tcp_protocols(&TCP_protocols) == -1)
|
||
|
|
return -1;
|
||
|
|
|
||
|
|
if (register_udp_protocols(&UDP_protocols) == -1)
|
||
|
|
return -1;
|
||
|
|
|
||
|
|
init_other_protocols(&lpi_names, &lpi_protocols, &lpi_category_protocols);
|
||
|
|
|
||
|
|
register_names(&TCP_protocols, &lpi_names, &lpi_protocols, &lpi_category_protocols);
|
||
|
|
register_names(&UDP_protocols, &lpi_names, &lpi_protocols, &lpi_category_protocols);
|
||
|
|
|
||
|
|
register_category_names(&lpi_categories);
|
||
|
|
|
||
|
|
init_called = true;
|
||
|
|
|
||
|
|
if (TCP_protocols.empty() && UDP_protocols.empty()) {
|
||
|
|
fprintf(stderr, "WARNING: No protocol modules loaded\n");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
void lpi_free_library() {
|
||
|
|
|
||
|
|
free_protocols(&TCP_protocols);
|
||
|
|
free_protocols(&UDP_protocols);
|
||
|
|
|
||
|
|
if (lpi_icmp != NULL) {
|
||
|
|
delete lpi_icmp;
|
||
|
|
lpi_icmp = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (lpi_unsupported != NULL) {
|
||
|
|
delete lpi_unsupported;
|
||
|
|
lpi_unsupported = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (lpi_unknown_tcp != NULL) {
|
||
|
|
delete lpi_unknown_tcp;
|
||
|
|
lpi_unknown_tcp = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (lpi_unknown_udp != NULL) {
|
||
|
|
delete lpi_unknown_udp;
|
||
|
|
lpi_unknown_udp = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
init_called = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
void lpi_init_data(lpi_data_t *data) {
|
||
|
|
|
||
|
|
data->payload[0] = 0;
|
||
|
|
data->payload[1] = 0;
|
||
|
|
data->seen_syn[0] = false;
|
||
|
|
data->seen_syn[1] = false;
|
||
|
|
data->seqno[0] = 0;
|
||
|
|
data->seqno[1] = 0;
|
||
|
|
data->observed[0] = 0;
|
||
|
|
data->observed[1] = 0;
|
||
|
|
data->server_port = 0;
|
||
|
|
data->client_port = 0;
|
||
|
|
data->trans_proto = 0;
|
||
|
|
data->payload_len[0] = 0;
|
||
|
|
data->payload_len[1] = 0;
|
||
|
|
data->ips[0] = 0;
|
||
|
|
data->ips[1] = 0;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
typedef enum {
|
||
|
|
TRACE_IPPROTO_IP = 0, /**< IP pseudo protocol number */
|
||
|
|
TRACE_IPPROTO_ICMP = 1, /**< Internet Control Message protocol */
|
||
|
|
TRACE_IPPROTO_IGMP = 2, /**< Internet Group Management Protocol */
|
||
|
|
TRACE_IPPROTO_IPIP = 4, /**< IP encapsulated in IP */
|
||
|
|
TRACE_IPPROTO_TCP = 6, /**< Transmission Control Protocol */
|
||
|
|
TRACE_IPPROTO_UDP = 17, /**< User Datagram Protocol */
|
||
|
|
TRACE_IPPROTO_IPV6 = 41, /**< IPv6 over IPv4 */
|
||
|
|
TRACE_IPPROTO_ROUTING = 43, /**< IPv6 Routing header */
|
||
|
|
TRACE_IPPROTO_FRAGMENT = 44, /**< IPv6 Fragmentation header */
|
||
|
|
TRACE_IPPROTO_RSVP = 46, /**< Resource Reservation Protocol */
|
||
|
|
TRACE_IPPROTO_GRE = 47, /**< General Routing Encapsulation */
|
||
|
|
TRACE_IPPROTO_ESP = 50, /**< Encapsulated Security Payload [RFC2406] */
|
||
|
|
TRACE_IPPROTO_AH = 51, /**< Authentication Header [RFC2402] */
|
||
|
|
TRACE_IPPROTO_ICMPV6 = 58, /**< ICMPv6 */
|
||
|
|
TRACE_IPPROTO_NONE = 59, /**< IPv6 no next header */
|
||
|
|
TRACE_IPPROTO_DSTOPTS = 60, /**< IPv6 destination options */
|
||
|
|
TRACE_IPPROTO_OSPF = 89, /**< Open Shortest Path First routing protocol */
|
||
|
|
TRACE_IPPROTO_PIM = 103, /**< Protocol Independant Multicast */
|
||
|
|
TRACE_IPPROTO_SCTP = 132 /**< Stream Control Transmission Protocol */
|
||
|
|
} libtrace_ipproto_t;
|
||
|
|
|
||
|
|
|
||
|
|
static lpi_module_t *test_protocol_list(LPIModuleList *ml, lpi_data_t *data) {
|
||
|
|
|
||
|
|
LPIModuleList::iterator l_it;
|
||
|
|
|
||
|
|
/* Turns out naively looping through the modules is quicker
|
||
|
|
* than trying to do intelligent stuff with threads. Most
|
||
|
|
* callbacks complete very quickly so threading overhead is a
|
||
|
|
* major problem */
|
||
|
|
for (l_it = ml->begin(); l_it != ml->end(); ++ l_it) {
|
||
|
|
lpi_module_t *module = *l_it;
|
||
|
|
|
||
|
|
/* To save time, I'm going to break on the first successful
|
||
|
|
* match. A threaded version would wait for all the modules
|
||
|
|
* to run, storing all successful results in a list of some
|
||
|
|
* sort and selecting an appropriate result from there.
|
||
|
|
*/
|
||
|
|
|
||
|
|
if (module->lpi_callback(data, module))
|
||
|
|
return module;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
static lpi_module_t *guess_protocol(LPIModuleMap *modmap, lpi_data_t *data) {
|
||
|
|
|
||
|
|
lpi_module_t *proto = NULL;
|
||
|
|
|
||
|
|
LPIModuleMap::iterator m_it;
|
||
|
|
|
||
|
|
/* Deal with each priority in turn - want to match higher priority
|
||
|
|
* rules first.
|
||
|
|
*/
|
||
|
|
|
||
|
|
for (m_it = modmap->begin(); m_it != modmap->end(); ++ m_it) {
|
||
|
|
LPIModuleList *ml = m_it->second;
|
||
|
|
|
||
|
|
proto = test_protocol_list(ml, data);
|
||
|
|
|
||
|
|
if (proto != NULL)
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return proto;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
lpi_module_t *lpi_guess_protocol(lpi_data_t *data) {
|
||
|
|
|
||
|
|
lpi_module_t *p = NULL;
|
||
|
|
|
||
|
|
if (!init_called) {
|
||
|
|
fprintf(stderr, "lpi_init_library was never called - cannot guess the protocol\n");
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
switch(data->trans_proto) {
|
||
|
|
case TRACE_IPPROTO_ICMP:
|
||
|
|
return lpi_icmp;
|
||
|
|
case TRACE_IPPROTO_TCP:
|
||
|
|
p = guess_protocol(&TCP_protocols, data);
|
||
|
|
if (p == NULL)
|
||
|
|
p = lpi_unknown_tcp;
|
||
|
|
return p;
|
||
|
|
|
||
|
|
case TRACE_IPPROTO_UDP:
|
||
|
|
p = guess_protocol(&UDP_protocols, data);
|
||
|
|
if (p == NULL)
|
||
|
|
p = lpi_unknown_udp;
|
||
|
|
return p;
|
||
|
|
default:
|
||
|
|
return lpi_unsupported;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
return p;
|
||
|
|
}
|
||
|
|
|
||
|
|
lpi_category_t lpi_categorise(lpi_module_t *module) {
|
||
|
|
|
||
|
|
if (module == NULL)
|
||
|
|
return LPI_CATEGORY_NO_CATEGORY;
|
||
|
|
|
||
|
|
return module->category;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
const char *lpi_print_category(lpi_category_t category) {
|
||
|
|
|
||
|
|
switch(category) {
|
||
|
|
case LPI_CATEGORY_WEB:
|
||
|
|
return "Web";
|
||
|
|
case LPI_CATEGORY_MAIL:
|
||
|
|
return "Mail";
|
||
|
|
case LPI_CATEGORY_CHAT:
|
||
|
|
return "Chat";
|
||
|
|
case LPI_CATEGORY_P2P:
|
||
|
|
return "P2P";
|
||
|
|
case LPI_CATEGORY_P2P_STRUCTURE:
|
||
|
|
return "P2P_Structure";
|
||
|
|
case LPI_CATEGORY_KEY_EXCHANGE:
|
||
|
|
return "Key_Exchange";
|
||
|
|
case LPI_CATEGORY_ECOMMERCE:
|
||
|
|
return "ECommerce";
|
||
|
|
case LPI_CATEGORY_GAMING:
|
||
|
|
return "Gaming";
|
||
|
|
case LPI_CATEGORY_ENCRYPT:
|
||
|
|
return "Encryption";
|
||
|
|
case LPI_CATEGORY_MONITORING:
|
||
|
|
return "Measurement";
|
||
|
|
case LPI_CATEGORY_NEWS:
|
||
|
|
return "News";
|
||
|
|
case LPI_CATEGORY_MALWARE:
|
||
|
|
return "Malware";
|
||
|
|
case LPI_CATEGORY_SECURITY:
|
||
|
|
return "Security";
|
||
|
|
case LPI_CATEGORY_ANTISPAM:
|
||
|
|
return "Antispam";
|
||
|
|
case LPI_CATEGORY_VOIP:
|
||
|
|
return "VOIP";
|
||
|
|
case LPI_CATEGORY_TUNNELLING:
|
||
|
|
return "Tunnelling";
|
||
|
|
case LPI_CATEGORY_NAT:
|
||
|
|
return "NAT_Traversal";
|
||
|
|
case LPI_CATEGORY_STREAMING:
|
||
|
|
return "Streaming";
|
||
|
|
case LPI_CATEGORY_SERVICES:
|
||
|
|
return "Services";
|
||
|
|
case LPI_CATEGORY_DATABASES:
|
||
|
|
return "Databases";
|
||
|
|
case LPI_CATEGORY_FILES:
|
||
|
|
return "File_Transfer";
|
||
|
|
case LPI_CATEGORY_REMOTE:
|
||
|
|
return "Remote_Access";
|
||
|
|
case LPI_CATEGORY_TELCO:
|
||
|
|
return "Telco_Services";
|
||
|
|
case LPI_CATEGORY_P2PTV:
|
||
|
|
return "P2PTV";
|
||
|
|
case LPI_CATEGORY_RCS:
|
||
|
|
return "Revision_Control";
|
||
|
|
case LPI_CATEGORY_LOGGING:
|
||
|
|
return "Logging";
|
||
|
|
case LPI_CATEGORY_PRINTING:
|
||
|
|
return "Printing";
|
||
|
|
case LPI_CATEGORY_TRANSLATION:
|
||
|
|
return "Translation";
|
||
|
|
case LPI_CATEGORY_CDN:
|
||
|
|
return "CDN";
|
||
|
|
case LPI_CATEGORY_CLOUD:
|
||
|
|
return "Cloud";
|
||
|
|
case LPI_CATEGORY_NOTIFICATION:
|
||
|
|
return "Notification";
|
||
|
|
case LPI_CATEGORY_SERIALISATION:
|
||
|
|
return "Serialisation";
|
||
|
|
case LPI_CATEGORY_BROADCAST:
|
||
|
|
return "Broadcast";
|
||
|
|
case LPI_CATEGORY_LOCATION:
|
||
|
|
return "Location";
|
||
|
|
case LPI_CATEGORY_CACHING:
|
||
|
|
return "Caching";
|
||
|
|
case LPI_CATEGORY_ICS:
|
||
|
|
return "ICS";
|
||
|
|
case LPI_CATEGORY_MOBILE_APP:
|
||
|
|
return "Mobile App";
|
||
|
|
case LPI_CATEGORY_IPCAMERAS:
|
||
|
|
return "IP Cameras";
|
||
|
|
case LPI_CATEGORY_EDUCATIONAL:
|
||
|
|
return "Educational";
|
||
|
|
case LPI_CATEGORY_MESSAGE_QUEUE:
|
||
|
|
return "Message_Queuing";
|
||
|
|
case LPI_CATEGORY_ICMP:
|
||
|
|
return "ICMP";
|
||
|
|
case LPI_CATEGORY_MIXED:
|
||
|
|
return "Mixed";
|
||
|
|
case LPI_CATEGORY_NOPAYLOAD:
|
||
|
|
return "No_Payload";
|
||
|
|
case LPI_CATEGORY_UNKNOWN:
|
||
|
|
return "Unknown";
|
||
|
|
case LPI_CATEGORY_UNSUPPORTED:
|
||
|
|
return "Unsupported";
|
||
|
|
case LPI_CATEGORY_NO_CATEGORY:
|
||
|
|
return "Uncategorised";
|
||
|
|
case LPI_CATEGORY_LAST:
|
||
|
|
return "Invalid_Category";
|
||
|
|
}
|
||
|
|
|
||
|
|
return "Invalid_Category";
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
const char *lpi_print(lpi_protocol_t proto) {
|
||
|
|
|
||
|
|
LPINameMap::iterator it;
|
||
|
|
|
||
|
|
it = lpi_names.find(proto);
|
||
|
|
|
||
|
|
if (it == lpi_names.end()) {
|
||
|
|
return "NULL";
|
||
|
|
}
|
||
|
|
return (it->second);
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
lpi_protocol_t lpi_get_protocol_by_name(char *name) {
|
||
|
|
|
||
|
|
LPIProtocolMap::iterator it;
|
||
|
|
|
||
|
|
it = lpi_protocols.find(name);
|
||
|
|
|
||
|
|
if (it == lpi_protocols.end()) {
|
||
|
|
return LPI_PROTO_UNKNOWN;
|
||
|
|
}
|
||
|
|
|
||
|
|
return (it->second);
|
||
|
|
}
|
||
|
|
|
||
|
|
lpi_category_t lpi_get_category_by_name(char *name) {
|
||
|
|
|
||
|
|
LPICategoryMap::iterator it;
|
||
|
|
|
||
|
|
it = lpi_categories.find(name);
|
||
|
|
|
||
|
|
if (it == lpi_categories.end()) {
|
||
|
|
return LPI_CATEGORY_UNKNOWN;
|
||
|
|
}
|
||
|
|
|
||
|
|
return (it->second);
|
||
|
|
}
|
||
|
|
|
||
|
|
lpi_category_t lpi_get_category_by_protocol(lpi_protocol_t protocol) {
|
||
|
|
|
||
|
|
LPICategoryProtocolMap::iterator it;
|
||
|
|
|
||
|
|
it = lpi_category_protocols.find(protocol);
|
||
|
|
|
||
|
|
if (it == lpi_category_protocols.end()) {
|
||
|
|
return LPI_CATEGORY_UNKNOWN;
|
||
|
|
}
|
||
|
|
|
||
|
|
return (it->second);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool lpi_is_protocol_inactive(lpi_protocol_t proto) {
|
||
|
|
|
||
|
|
LPINameMap::iterator it;
|
||
|
|
|
||
|
|
it = lpi_names.find(proto);
|
||
|
|
|
||
|
|
if (it == lpi_names.end()) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
|
||
|
|
}
|
||
|
|
|