/* * soulseek.c * * Copyright (C) 2009-2011 by ipoque GmbH * Copyright (C) 2011-15 - 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 . * */ #include "ndpi_protocols.h" #ifdef NDPI_PROTOCOL_SOULSEEK static void ndpi_int_soulseek_add_connection(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) { struct ndpi_packet_struct *packet = &flow->packet; struct ndpi_id_struct *src = flow->src; struct ndpi_id_struct *dst = flow->dst; ndpi_int_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_SOULSEEK, NDPI_REAL_PROTOCOL); if (src != NULL) { src->soulseek_last_safe_access_time = packet->tick_timestamp; } if (dst != NULL) { dst->soulseek_last_safe_access_time = packet->tick_timestamp; } return; } void ndpi_search_soulseek_tcp(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) { struct ndpi_packet_struct *packet = &flow->packet; struct ndpi_id_struct *src = flow->src; struct ndpi_id_struct *dst = flow->dst; NDPI_LOG(NDPI_PROTOCOL_SOULSEEK, ndpi_struct, NDPI_LOG_DEBUG, "Soulseek: search soulseec tcp \n"); if (packet->detected_protocol_stack[0] == NDPI_PROTOCOL_SOULSEEK) { NDPI_LOG(NDPI_PROTOCOL_SOULSEEK, ndpi_struct, NDPI_LOG_DEBUG, "packet marked as Soulseek\n"); if (src != NULL) NDPI_LOG(NDPI_PROTOCOL_SOULSEEK, ndpi_struct, NDPI_LOG_DEBUG, " SRC bitmask: %u, packet tick %llu , last safe access timestamp: %llu\n", NDPI_COMPARE_PROTOCOL_TO_BITMASK(src->detected_protocol_bitmask, NDPI_PROTOCOL_SOULSEEK) != 0 ? 1 : 0, (u_int64_t) packet->tick_timestamp, (u_int64_t) src->soulseek_last_safe_access_time); if (dst != NULL) NDPI_LOG(NDPI_PROTOCOL_SOULSEEK, ndpi_struct, NDPI_LOG_DEBUG, " DST bitmask: %u, packet tick %llu , last safe ts: %llu\n", NDPI_COMPARE_PROTOCOL_TO_BITMASK(dst->detected_protocol_bitmask, NDPI_PROTOCOL_SOULSEEK) != 0 ? 1 : 0, (u_int64_t) packet->tick_timestamp, (u_int64_t) dst->soulseek_last_safe_access_time); if (packet->payload_packet_len == 431) { if (dst != NULL) { dst->soulseek_last_safe_access_time = packet->tick_timestamp; } return; } if (packet->payload_packet_len == 12 && get_l32(packet->payload, 4) == 0x02) { if (src != NULL) { src->soulseek_last_safe_access_time = packet->tick_timestamp; if (packet->tcp != NULL && src->soulseek_listen_port == 0) { src->soulseek_listen_port = get_l32(packet->payload, 8); return; } } } if (src != NULL && ((u_int32_t) (packet->tick_timestamp - src->soulseek_last_safe_access_time) < ndpi_struct->soulseek_connection_ip_tick_timeout)) { NDPI_LOG(NDPI_PROTOCOL_SOULSEEK, ndpi_struct, NDPI_LOG_DEBUG, "Soulseek: SRC update last safe access time and SKIP_FOR_TIME \n"); src->soulseek_last_safe_access_time = packet->tick_timestamp; } if (dst != NULL && ((u_int32_t) (packet->tick_timestamp - dst->soulseek_last_safe_access_time) < ndpi_struct->soulseek_connection_ip_tick_timeout)) { NDPI_LOG(NDPI_PROTOCOL_SOULSEEK, ndpi_struct, NDPI_LOG_DEBUG, "Soulseek: DST update last safe access time and SKIP_FOR_TIME \n"); dst->soulseek_last_safe_access_time = packet->tick_timestamp; } } if (dst != NULL && dst->soulseek_listen_port != 0 && dst->soulseek_listen_port == ntohs(packet->tcp->dest) && ((u_int32_t) (packet->tick_timestamp - dst->soulseek_last_safe_access_time) < ndpi_struct->soulseek_connection_ip_tick_timeout)) { NDPI_LOG(NDPI_PROTOCOL_SOULSEEK, ndpi_struct, NDPI_LOG_DEBUG, "Soulseek: Plain detection on Port : %u packet_tick_timestamp: %u soulseeek_last_safe_access_time: %u soulseek_connection_ip_ticktimeout: %u\n", dst->soulseek_listen_port, packet->tick_timestamp, dst->soulseek_last_safe_access_time, ndpi_struct->soulseek_connection_ip_tick_timeout); ndpi_int_soulseek_add_connection(ndpi_struct, flow); return; } if (flow->l4.tcp.soulseek_stage == 0) { u_int32_t index = 0; if (packet->payload_packet_len >= 12 && packet->payload_packet_len < 300 && get_l32(packet->payload, 4) == 1) { while (!get_u_int16_t(packet->payload, index + 2) && (index + get_l32(packet->payload, index)) < packet->payload_packet_len - 4) { if (get_l32(packet->payload, index) < 8) /*Minimum soulsek login msg is 8B */ break; if (index + get_l32(packet->payload, index) + 4 <= index) { /* avoid overflow */ break; } index += get_l32(packet->payload, index) + 4; } if (index + get_l32(packet->payload, index) == packet->payload_packet_len - 4 && !get_u_int16_t(packet->payload, 10)) { /*This structure seems to be soulseek proto */ index = get_l32(packet->payload, 8) + 12; // end of "user name" if ((index + 4) <= packet->payload_packet_len && !get_u_int16_t(packet->payload, index + 2)) // for passwd len { index += get_l32(packet->payload, index) + 4; //end of "Passwd" if ((index + 4 + 4) <= packet->payload_packet_len && !get_u_int16_t(packet->payload, index + 6)) // to read version,hashlen { index += get_l32(packet->payload, index + 4) + 8; // enf of "hash value" if (index == get_l32(packet->payload, 0)) { NDPI_LOG(NDPI_PROTOCOL_SOULSEEK, ndpi_struct, NDPI_LOG_DEBUG, "Soulseek Login Detected\n"); ndpi_int_soulseek_add_connection(ndpi_struct, flow); return; } } } } } if (packet->payload_packet_len > 8 && packet->payload_packet_len < 200 && get_l32(packet->payload, 0) == packet->payload_packet_len - 4) { //Server Messages: const u_int32_t msgcode = get_l32(packet->payload, 4); if (msgcode == 0x7d) { flow->l4.tcp.soulseek_stage = 1 + packet->packet_direction; NDPI_LOG(NDPI_PROTOCOL_SOULSEEK, ndpi_struct, NDPI_LOG_DEBUG, "Soulseek Messages Search\n"); return; } else if (msgcode == 0x02 && packet->payload_packet_len == 12) { const u_int32_t soulseek_listen_port = get_l32(packet->payload, 8); if (src != NULL) { src->soulseek_last_safe_access_time = packet->tick_timestamp; if (packet->tcp != NULL && src->soulseek_listen_port == 0) { src->soulseek_listen_port = soulseek_listen_port; NDPI_LOG(NDPI_PROTOCOL_SOULSEEK, ndpi_struct, NDPI_LOG_DEBUG, "\n Listen Port Saved : %u", src->soulseek_listen_port); ndpi_int_soulseek_add_connection(ndpi_struct, flow); return; } } } //Peer Messages : Peer Init Message Detection if (get_l32(packet->payload, 0) == packet->payload_packet_len - 4) { const u_int32_t typelen = get_l32(packet->payload, packet->payload_packet_len - 9); const u_int8_t type = packet->payload[packet->payload_packet_len - 5]; const u_int32_t namelen = get_l32(packet->payload, 5); if (packet->payload[4] == 0x01 && typelen == 1 && namelen <= packet->payload_packet_len && (4 + 1 + 4 + namelen + 4 + 1 + 4) == packet->payload_packet_len && (type == 'F' || type == 'P' || type == 'D')) { NDPI_LOG(NDPI_PROTOCOL_SOULSEEK, ndpi_struct, NDPI_LOG_DEBUG, "soulseek detected\n"); ndpi_int_soulseek_add_connection(ndpi_struct, flow); return; } NDPI_LOG(NDPI_PROTOCOL_SOULSEEK, ndpi_struct, NDPI_LOG_DEBUG, "1\n"); } NDPI_LOG(NDPI_PROTOCOL_SOULSEEK, ndpi_struct, NDPI_LOG_DEBUG, "3\n"); //Peer Message : Pierce Firewall if (packet->payload_packet_len == 9 && get_l32(packet->payload, 0) == 5 && packet->payload[4] <= 0x10 && get_u_int32_t(packet->payload, 5) != 0x00000000) { flow->l4.tcp.soulseek_stage = 1 + packet->packet_direction; NDPI_LOG(NDPI_PROTOCOL_SOULSEEK, ndpi_struct, NDPI_LOG_TRACE, "Soulseek Size 9 Pierce Firewall\n"); return; } } if (packet->payload_packet_len > 25 && packet->payload[4] == 0x01 && !get_u_int16_t(packet->payload, 7) && !get_u_int16_t(packet->payload, 2)) { const u_int32_t usrlen = get_l32(packet->payload, 5); if (usrlen <= packet->payload_packet_len - 4 + 1 + 4 + 4 + 1 + 4) { const u_int32_t typelen = get_l32(packet->payload, 4 + 1 + 4 + usrlen); const u_int8_t type = packet->payload[4 + 1 + 4 + usrlen + 4]; if (typelen == 1 && (type == 'F' || type == 'P' || type == 'D')) { NDPI_LOG(NDPI_PROTOCOL_SOULSEEK, ndpi_struct, NDPI_LOG_DEBUG, "soulseek detected Pattern command(D|P|F).\n"); ndpi_int_soulseek_add_connection(ndpi_struct, flow); return; } } } } else if (flow->l4.tcp.soulseek_stage == 2 - packet->packet_direction) { if (packet->payload_packet_len > 8) { if ((packet->payload[0] || packet->payload[1]) && get_l32(packet->payload, 4) == 9) { /* 9 is search result */ NDPI_LOG(NDPI_PROTOCOL_SOULSEEK, ndpi_struct, NDPI_LOG_DEBUG, "soulseek detected Second Pkt\n"); ndpi_int_soulseek_add_connection(ndpi_struct, flow); return; } if (get_l32(packet->payload, 0) == packet->payload_packet_len - 4) { const u_int32_t msgcode = get_l32(packet->payload, 4); if (msgcode == 0x03 && packet->payload_packet_len >= 12) //Server Message : Get Peer Address { const u_int32_t usrlen = get_l32(packet->payload, 8); if (usrlen <= packet->payload_packet_len && 4 + 4 + 4 + usrlen == packet->payload_packet_len) { NDPI_LOG(NDPI_PROTOCOL_SOULSEEK, ndpi_struct, NDPI_LOG_DEBUG, "Soulseek Request Get Peer Address Detected\n"); ndpi_int_soulseek_add_connection(ndpi_struct, flow); return; } } } } if (packet->payload_packet_len == 8 && get_l32(packet->payload, 4) == 0x00000004) { NDPI_LOG(NDPI_PROTOCOL_SOULSEEK, ndpi_struct, NDPI_LOG_DEBUG, "soulseek detected\n"); ndpi_int_soulseek_add_connection(ndpi_struct, flow); return; } if (packet->payload_packet_len == 4 && get_u_int16_t(packet->payload, 2) == 0x00 && get_u_int16_t(packet->payload, 0) != 0x00) { NDPI_LOG(NDPI_PROTOCOL_SOULSEEK, ndpi_struct, NDPI_LOG_DEBUG, "soulseek detected\n"); ndpi_int_soulseek_add_connection(ndpi_struct, flow); return; } else if (packet->payload_packet_len == 4) { flow->l4.tcp.soulseek_stage = 3; return; } } else if (flow->l4.tcp.soulseek_stage == 1 + packet->packet_direction) { if (packet->payload_packet_len > 8) { if (packet->payload[4] == 0x03 && get_l32(packet->payload, 5) == 0x00000031) { NDPI_LOG(NDPI_PROTOCOL_SOULSEEK, ndpi_struct, NDPI_LOG_DEBUG, "soulseek detected Second Pkt with SIGNATURE :: 0x0331000000 \n"); ndpi_int_soulseek_add_connection(ndpi_struct, flow); return; } } } if (flow->l4.tcp.soulseek_stage == 3 && packet->payload_packet_len == 8 && !get_u_int32_t(packet->payload, 4)) { NDPI_LOG(NDPI_PROTOCOL_SOULSEEK, ndpi_struct, NDPI_LOG_DEBUG, "soulseek detected bcz of 8B pkt\n"); ndpi_int_soulseek_add_connection(ndpi_struct, flow); return; } if (flow->l4.tcp.soulseek_stage && flow->packet_counter < 11) { } else { NDPI_ADD_PROTOCOL_TO_BITMASK(flow->excluded_protocol_bitmask, NDPI_PROTOCOL_SOULSEEK); } } #endif