This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
stellar-stellar/decoders/lpi_plus/lpip_extend.c
2024-11-06 16:34:26 +08:00

557 lines
16 KiB
C

#include "lpip_extend.h"
#include <stdlib.h>
#include <string.h>
const char *dns_name="DNS";
const char *imap_name="IMAP";
const char *quic_name="QUIC";
const char *ftps_name="FTPS";
const char *openvpn_name="OpenVPN";
const char *smtp_name="SMTP";
const char *ftp_control_name="FTP_Control";
#define P_CONTROL_HARD_RESET_CLIENT_V1 (0x01 << 3)
#define P_CONTROL_HARD_RESET_CLIENT_V2 (0x07 << 3)
#define P_CONTROL_HARD_RESET_SERVER_V1 (0x02 << 3)
#define P_CONTROL_HARD_RESET_SERVER_V2 (0x08 << 3)
#define P_ACK_V1 (0x05 << 3)
#define P_CONTROL_V1 (0x04 << 3)
#define P_OPCODE_MASK 0xF8
#define P_SHA1_HMAC_SIZE 20
#define P_HMAC_128 16 // (RSA-)MD5, (RSA-)MD4, ..others
#define P_HMAC_160 20 // (RSA-|DSA-)SHA(1), ..others, SHA1 is openvpn default
#define P_HMAC_NONE 0
#define P_HARD_RESET_PACKET_ID_OFFSET(hmac_size) (9 + hmac_size)
#define P_PACKET_ID_ARRAY_LEN_OFFSET(hmac_size) (P_HARD_RESET_PACKET_ID_OFFSET(hmac_size) + 8)
#define P_HARD_RESET_CLIENT_MAX_COUNT 5
static inline uint32_t get_packet_id(const uint8_t * payload, uint8_t hms) {
return(ntohl(*(uint32_t*)(payload + P_HARD_RESET_PACKET_ID_OFFSET(hms))));
}
static inline int8_t check_pkid_and_detect_hmac_size(const uint8_t * payload) {
// try to guess
if((int)get_packet_id(payload, P_HMAC_160) != 0)
return P_HMAC_160;
if((int)get_packet_id(payload, P_HMAC_128) != 0 )
return P_HMAC_128;
if((int)get_packet_id(payload, P_HMAC_NONE) != 0)
return P_HMAC_NONE;
return(-1);
}
int lpi_plus_match_openvpn(const unsigned char *payload, size_t payload_sz, int c2s_pkt_cnt, int s2c_pkt_cnt, struct ovpn_ctx *ovpn, unsigned char trans_proto, bool is_c2s_flow)
{
const uint8_t * session_remote;
uint8_t opcode;
uint8_t alen;
int8_t hmac_size;
int8_t failed = 0;
if (trans_proto!=IPPROTO_UDP && trans_proto!=IPPROTO_TCP)return 0;
if (c2s_pkt_cnt > 5)
{
if (ovpn->contain_local_session_id_pkt_num == P_HARD_RESET_CLIENT_MAX_COUNT - 1 && ovpn->valid_opcode_cnt == P_HARD_RESET_CLIENT_MAX_COUNT)
return 1;
return 0;
}
if (payload_sz >= 40)
{
// skip openvpn TCP transport packet size
if (trans_proto==IPPROTO_TCP)payload += 2, payload_sz -= 2;
opcode = payload[0] & P_OPCODE_MASK;
if (is_c2s_flow == 1 &&
(opcode == P_CONTROL_HARD_RESET_CLIENT_V1 ||
opcode == P_CONTROL_HARD_RESET_CLIENT_V2 ||
opcode == P_ACK_V1 ||
opcode == P_CONTROL_V1))
{
ovpn->valid_opcode_cnt++;
}
// for UDP, trying to identify by opcode and first packet length
if (trans_proto==IPPROTO_UDP)
{
if ((c2s_pkt_cnt == 1 || s2c_pkt_cnt == 1) && (((payload_sz == 112) && ((opcode == 168) || (opcode == 192))) || ((payload_sz == 80) && ((opcode == 184) || (opcode == 88) || (opcode == 160) || (opcode == 168) || (opcode == 200)))))
{
return 1;
}
}
if(s2c_pkt_cnt >= 1 && is_c2s_flow == false)return 0;
if (is_c2s_flow == true && c2s_pkt_cnt <= P_HARD_RESET_CLIENT_MAX_COUNT && (opcode == P_CONTROL_HARD_RESET_CLIENT_V1 || opcode == P_CONTROL_HARD_RESET_CLIENT_V2 || opcode == P_ACK_V1 || opcode == P_CONTROL_V1))
{
if (check_pkid_and_detect_hmac_size(payload) >= 0)
{
if (c2s_pkt_cnt > 1)
{
if (memcmp(&ovpn->local_session_id, payload + 1, 8) == 0)
{
ovpn->contain_local_session_id_pkt_num++;
}
}
memcpy(&ovpn->local_session_id, payload + 1, 8);
}
}
else if (c2s_pkt_cnt >= 1 && c2s_pkt_cnt <= P_HARD_RESET_CLIENT_MAX_COUNT &&
(opcode == P_CONTROL_HARD_RESET_SERVER_V1 || opcode == P_CONTROL_HARD_RESET_SERVER_V2))
{
hmac_size = check_pkid_and_detect_hmac_size(payload);
if (hmac_size >= 0)
{
size_t offset = P_PACKET_ID_ARRAY_LEN_OFFSET(hmac_size);
alen = payload[offset];
if (alen > 0)
{
offset += 1 + alen * 4;
if ((offset + 8) <= payload_sz)
{
session_remote = &payload[offset];
if (memcmp(&ovpn->local_session_id, session_remote, 8) == 0)
{
return 1;
}
else
{
failed = 1;
}
}
else
failed = 1;
}
else
failed = 1;
}
else
failed = 1;
}
else
failed = 1;
if (failed)
return 0;
}
return 0;
}
#define GQUIC_PUBLIC_FLAG_VERSION 0x01
#define GQUIC_PUBLIC_FLAG_RST 0x02
#define GQUIC_PUBLIC_FLAG_NONCE 0x04
#define GQUIC_PUBLIC_FLAG_CID 0x08
#define GQUIC_PUBLIC_FLAG_PKT_NUM 0x30
//https://github.com/quicwg/base-drafts/wiki/QUIC-Versions
enum _QUIC_VERSION
{
QUIC_VERSION_UNKNOWN=0,
//NetApp
QUANT_VERSION_00=0x45474700,
QUANT_VERSION_FF=0x454747FF,
//Private Octopus
PICOQUIC_VERSION_30=0x50435130,
//google
GQUIC_VERSION_Q001=0x51303031,
GQUIC_VERSION_Q002=0x51303032,
GQUIC_VERSION_Q003=0x51303033,
GQUIC_VERSION_Q004=0x51303034,
GQUIC_VERSION_Q005=0x51303035,
GQUIC_VERSION_Q006=0x51303036,
GQUIC_VERSION_Q007=0x51303037,
GQUIC_VERSION_Q008=0x51303038,
GQUIC_VERSION_Q009=0x51303039,
GQUIC_VERSION_Q010=0x51303130,
GQUIC_VERSION_Q011=0x51303131,
GQUIC_VERSION_Q012=0x51303132,
GQUIC_VERSION_Q013=0x51303133,
GQUIC_VERSION_Q014=0x51303134,
GQUIC_VERSION_Q015=0x51303135,
GQUIC_VERSION_Q016=0x51303136,
GQUIC_VERSION_Q017=0x51303137,
GQUIC_VERSION_Q018=0x51303138,
GQUIC_VERSION_Q019=0x51303139,
GQUIC_VERSION_Q020=0x51303230,
GQUIC_VERSION_Q021=0x51303231,
GQUIC_VERSION_Q022=0x51303232,
GQUIC_VERSION_Q023=0x51303233,
GQUIC_VERSION_Q024=0x51303234,
GQUIC_VERSION_Q025=0x51303235,
GQUIC_VERSION_Q026=0x51303236,
GQUIC_VERSION_Q027=0x51303237,
GQUIC_VERSION_Q028=0x51303238,
GQUIC_VERSION_Q029=0x51303239,
GQUIC_VERSION_Q030=0x51303330,
GQUIC_VERSION_Q031=0x51303331,
GQUIC_VERSION_Q032=0x51303332,
GQUIC_VERSION_Q033=0x51303333,
GQUIC_VERSION_Q034=0x51303334,
GQUIC_VERSION_Q035=0x51303335,
GQUIC_VERSION_Q036=0x51303336,
GQUIC_VERSION_Q037=0x51303337,
GQUIC_VERSION_Q038=0x51303338,
GQUIC_VERSION_Q039=0x51303339,
GQUIC_VERSION_Q040=0x51303430,
GQUIC_VERSION_Q041=0x51303431,
GQUIC_VERSION_Q042=0x51303432,
GQUIC_VERSION_Q043=0x51303433,
GQUIC_VERSION_Q044=0x51303434,
GQUIC_VERSION_Q045=0x51303435,
GQUIC_VERSION_Q046=0x51303436,
GQUIC_VERSION_Q047=0x51303437,
GQUIC_VERSION_Q048=0x51303438,
GQUIC_VERSION_Q049=0x51303439,
GQUIC_VERSION_Q050=0x51303530,
GQUIC_VERSION_Q051=0x51303531,
GQUIC_VERSION_Q052=0x51303532,
GQUIC_VERSION_Q053=0x51303533,
GQUIC_VERSION_Q054=0x51303534,
GQUIC_VERSION_Q055=0x51303535,
GQUIC_VERSION_Q056=0x51303536,
GQUIC_VERSION_Q057=0x51303537,
GQUIC_VERSION_Q058=0x51303538,
GQUIC_VERSION_Q059=0x51303539,
GQUIC_VERSION_Q099=0x51303939,
//Google QUIC with TLS 48 - 49 (T048 - T049)
GQUIC_VERSION_T048=0x54303438,
GQUIC_VERSION_T049=0x54303439,
//Google QUIC with TLS 50 - 59 (T050 - T059)
GQUIC_VERSION_T050=0x54303530,
GQUIC_VERSION_T051=0x54303531,
GQUIC_VERSION_T052=0x54303532,
GQUIC_VERSION_T053=0x54303533,
GQUIC_VERSION_T054=0x54303534,
GQUIC_VERSION_T055=0x54303535,
GQUIC_VERSION_T056=0x54303536,
GQUIC_VERSION_T057=0x54303537,
GQUIC_VERSION_T058=0x54303538,
GQUIC_VERSION_T059=0x54303539,
//Google QUIC with TLS 99 (T099)
GQUIC_VERSION_T099=0x54303939,
//Google Proxied QUIC
PQUIC_VERSION_PROX=0x50524f58,
//quic-go
QUIC_GO_VERSION_00=0x51474F00,
QUIC_GO_VERSION_FF=0x51474FFF,
//quicly
QUICLY_VERSION_00=0x91c17000,
QUICLY_VERSION_FF=0x91c170FF,
//Microsoft
MSQUIC_VERSION_00=0xabcd0000,
MSQUIC_VERSION_0F=0xabcd000F,
//Mozilla
MOZQUIC_VERSION_00=0xf123f0c0,
MOZQUIC_VERSION_0F=0xf123f0cF,
//Facebook
MVFST_VERSION_00=0xfaceb000,
MVFST_VERSION_01=0xfaceb001,
MVFST_VERSION_02=0xfaceb002,
MVFST_VERSION_03=0xfaceb003,
MVFST_VERSION_04=0xfaceb004,
MVFST_VERSION_05=0xfaceb005,
MVFST_VERSION_06=0xfaceb006,
MVFST_VERSION_07=0xfaceb007,
MVFST_VERSION_08=0xfaceb008,
MVFST_VERSION_09=0xfaceb009,
MVFST_VERSION_0A=0xfaceb00A,
MVFST_VERSION_0B=0xfaceb00B,
MVFST_VERSION_0C=0xfaceb00C,
MVFST_VERSION_0D=0xfaceb00D,
MVFST_VERSION_0E=0xfaceb00E,
MVFST_VERSION_0F=0xfaceb00F,
//IETF
IQUIC_VERSION_RFC9000=0x00000001,
IQUIC_VERSION_I001=0xFF000001,
IQUIC_VERSION_I002=0xFF000002,
IQUIC_VERSION_I003=0xFF000003,
IQUIC_VERSION_I004=0xFF000004,
IQUIC_VERSION_I005=0xFF000005,
IQUIC_VERSION_I006=0xFF000006,
IQUIC_VERSION_I007=0xFF000007,
IQUIC_VERSION_I008=0xFF000008,
IQUIC_VERSION_I009=0xFF000009,
IQUIC_VERSION_I010=0xFF00000A,
IQUIC_VERSION_I011=0xFF00000B,
IQUIC_VERSION_I012=0xFF00000C,
IQUIC_VERSION_I013=0xFF00000D,
IQUIC_VERSION_I014=0xFF00000E,
IQUIC_VERSION_I015=0xFF00000F,
IQUIC_VERSION_I016=0xFF000010,
IQUIC_VERSION_I017=0xFF000011,
IQUIC_VERSION_I018=0xFF000012,
IQUIC_VERSION_I019=0xFF000013,
IQUIC_VERSION_I020=0xFF000014,
IQUIC_VERSION_I021=0xFF000015,
IQUIC_VERSION_I022=0xFF000016,
IQUIC_VERSION_I023=0xFF000017,
IQUIC_VERSION_I024=0xFF000018,
IQUIC_VERSION_I025=0xFF000019,
IQUIC_VERSION_I026=0xFF00001A,
IQUIC_VERSION_I027=0xFF00001B,
IQUIC_VERSION_I028=0xFF00001C,
IQUIC_VERSION_I029=0xFF00001D,
IQUIC_VERSION_I030=0xFF00001E,
IQUIC_VERSION_I031=0xFF00001F,
IQUIC_VERSION_I032=0xFF000020
};
int lpi_plus_match_quic(int is_c2s_flow, const unsigned char *payload, size_t payload_sz)
{
enum _QUIC_VERSION quic_version = QUIC_VERSION_UNKNOWN;
char public_flags=payload[0];
int used_len = 1;
if(payload_sz<5)
{
goto QUIC_UNKNOWN;
}
// Q001~Q043: 0x80 is currently unused, and must be set to 0
// The most significant bit (0x80) of byte 0 (the first byte) is set to 1 for long headers
(payload[0] & 0x80) ? (quic_version = (enum _QUIC_VERSION)ntohl(*(unsigned int *)(payload + 1))) : QUIC_VERSION_UNKNOWN;
if (
(quic_version == GQUIC_VERSION_Q099) ||
(quic_version == PICOQUIC_VERSION_30) ||
(quic_version == PQUIC_VERSION_PROX) ||
(quic_version == GQUIC_VERSION_T099) ||
(quic_version >= GQUIC_VERSION_Q044 && quic_version <= GQUIC_VERSION_Q050) ||
(quic_version >= GQUIC_VERSION_Q051 && quic_version <= GQUIC_VERSION_Q059) ||
(quic_version >= GQUIC_VERSION_T048 && quic_version <= GQUIC_VERSION_T049) ||
(quic_version >= GQUIC_VERSION_T050 && quic_version <= GQUIC_VERSION_T059) ||
(quic_version >= QUANT_VERSION_00 && quic_version <= QUANT_VERSION_FF) ||
(quic_version >= QUIC_GO_VERSION_00 && quic_version <= QUIC_GO_VERSION_FF) ||
(quic_version >= QUICLY_VERSION_00 && quic_version <= QUICLY_VERSION_FF) ||
(quic_version >= MSQUIC_VERSION_00 && quic_version <= MSQUIC_VERSION_0F) ||
(quic_version >= MOZQUIC_VERSION_00 && quic_version <= MOZQUIC_VERSION_0F) ||
(quic_version >= MVFST_VERSION_00 && quic_version <= MVFST_VERSION_0F) ||
(quic_version >= IQUIC_VERSION_I001 && quic_version <= IQUIC_VERSION_I032) ||
(quic_version == IQUIC_VERSION_RFC9000))
{
goto QUIC_KNOWN;
}
if(is_c2s_flow==0 && public_flags & GQUIC_PUBLIC_FLAG_VERSION)
{
goto QUIC_UNKNOWN;
}
if((!public_flags)&GQUIC_PUBLIC_FLAG_PKT_NUM)
{
if(public_flags&GQUIC_PUBLIC_FLAG_VERSION) //Public Reset Packet
{
goto QUIC_UNKNOWN;// todo
}
else // Version Negotiation Packet
{
goto QUIC_UNKNOWN;
}
}
if(public_flags&GQUIC_PUBLIC_FLAG_CID)
{
used_len+=sizeof(unsigned long long); // CID length
}
if(payload_sz>=(size_t)(used_len+sizeof(int)) && public_flags&GQUIC_PUBLIC_FLAG_VERSION && (*(unsigned char *)(payload+used_len)==0x51))
{
quic_version=(enum _QUIC_VERSION)ntohl(*(unsigned int *)(payload+used_len));
used_len+=sizeof(int); // skip version
}
if(quic_version<GQUIC_VERSION_Q001 || quic_version>GQUIC_VERSION_Q043)
{
goto QUIC_UNKNOWN;
}
QUIC_KNOWN:
return 1;
QUIC_UNKNOWN:
return 0;
}
struct dns_header
{
u_int16_t id;
#if __BYTE_ORDER == __LITTLE_ENDIAN
u_char rd:1;
u_char tc:1;
u_char aa:1;
u_char opcode:4;
u_char qr:1;
u_char rcode:4;
u_char z:3;
u_char ra:1;
#elif __BYTE_ORDER == __BIG_ENDIAN
u_char qr:1;
u_char opcode:4;
u_char aa:1;
u_char tc:1;
u_char rd:1;
u_char ra:1;
u_char z:3;
u_char rcode:4;
#endif
u_int16_t qdcount;
u_int16_t ancount;
u_int16_t aucount;//authority count
u_int16_t adcount;//additional count
};
__thread lpi_module_t per_thread_result;
lpi_module_t* lpi_plus_extended_guess(struct lpi_plus_detect_context *ctx, lpi_protocol_t lpi_proto, const char *payload, size_t payload_sz)
{
unsigned short sport=ctx->lpi_data.client_port, dport=ctx->lpi_data.server_port;
memset(&per_thread_result, 0, sizeof(per_thread_result));
lpi_module_t *result=&per_thread_result;
// FTPS
if((sport == 989 || sport == 990 || dport == 989 || dport == 990) && lpi_proto == LPI_PROTO_SSL)
{
result->protocol=(lpi_protocol_t)LPI_PROTP_FTPS;
result->category=LPI_CATEGORY_FILES;
result->priority=3;
result->name=ftps_name;
result->lpi_callback=NULL;
return result;
}
//QUIC
if((lpi_proto==LPI_PROTO_UNKNOWN || lpi_proto==LPI_PROTO_UDP))
{
if(lpi_plus_match_quic(ctx->current_is_c2s_flow, (const unsigned char *)payload, payload_sz))
{
//lpi_quic
result->protocol=LPI_PROTO_UDP_QUIC;
result->category=LPI_CATEGORY_WEB;
result->priority=9;
result->name=quic_name;
result->lpi_callback=NULL;
return result;
}
}
//DNS
if((sport==53 || dport==53) && (payload_sz>12) && (lpi_proto==LPI_PROTO_UNKNOWN || lpi_proto==LPI_PROTO_UDP))
{
struct dns_header *dns_hdr=NULL;
if(ctx->lpi_data.trans_proto==IPPROTO_TCP)
{
dns_hdr=(struct dns_header *)(payload+2);
}
else
{
dns_hdr=(struct dns_header *)payload;
}
if((dns_hdr->qr==1 && ntohs(dns_hdr->qdcount)==1) ||
(dns_hdr->qr==0 && ntohs(dns_hdr->qdcount)==1 && ntohs(dns_hdr->aucount)==0 && ntohs(dns_hdr->ancount)==0))
{
result->protocol=LPI_PROTO_DNS;
result->category=LPI_CATEGORY_SERVICES;
result->priority=10;
result->name=dns_name;
result->lpi_callback=NULL;
return result;
}
}
//IMAP
if(((sport==143 || dport==143))
&& ((lpi_proto==LPI_PROTO_UNKNOWN) && ctx->lpi_data.trans_proto==IPPROTO_TCP)
&& (
(payload_sz>=11 && memmem(payload, payload_sz, " CAPABILITY", 11)!=NULL) ||
(payload_sz>=7 && memmem(payload, payload_sz, " LOGIN ", 7)!=NULL)
)
)
{
result->protocol=LPI_PROTO_IMAP;
result->category=LPI_CATEGORY_MAIL;
result->priority=2;
result->name=imap_name;
result->lpi_callback=NULL;
return result;
}
if( (lpi_proto==LPI_PROTO_UNKNOWN || lpi_proto == LPI_PROTO_UDP)&&
1 == lpi_plus_match_openvpn((const unsigned char *)payload,
payload_sz,
ctx->detected_c2s_pkt,
ctx->detected_s2c_pkt,
&ctx->ovpn,
ctx->lpi_data.trans_proto,
ctx->current_is_c2s_flow))
{
result->protocol=LPI_PROTO_OPENVPN;
result->category=LPI_CATEGORY_TUNNELLING;
result->priority=4;
result->name=openvpn_name;
result->lpi_callback=NULL;
return result;
}
//SMTP
if (lpi_proto == LPI_PROTO_FTP_CONTROL && (ctx->detected_c2s_pkt+ctx->detected_s2c_pkt) == 1 && ctx->current_is_c2s_flow==0)
{
if ((((payload_sz >= 4 && (memcmp(payload, "220-", 4) == 0)) || memcmp(payload, "220 ", 4) == 0)) &&
(((payload_sz >= 7 && (memmem(payload, payload_sz, " ESMTP ", 7) != NULL)) ||
memmem(payload, payload_sz, " esmtp ", 7) != NULL)))
{
// lpi_smtp
result->protocol = LPI_PROTO_SMTP;
result->category = LPI_CATEGORY_MAIL;
result->priority = 2;
result->name = smtp_name;
return result;
}
}
if (lpi_proto == LPI_PROTO_SMTP && (ctx->detected_c2s_pkt+ctx->detected_s2c_pkt) == 1 && ctx->current_is_c2s_flow==0)
{
if (
(payload_sz >= 4) &&
(memcmp(payload, "220 ", 4) == 0 || memcmp(payload, "220-", 4) == 0) &&
(memmem(payload, payload_sz, "FTPd", 4) != NULL ||
memmem(payload, payload_sz, "ftpd", 4) != NULL ||
memmem(payload, payload_sz, "FTPD", 4) != NULL))
{
// lpi_ftpcontrol
result->protocol = LPI_PROTO_FTP_CONTROL;
result->category = LPI_CATEGORY_FILES;
result->priority = 3;
result->name = ftp_control_name;
return result;
}
}
return NULL;
}