/** * @brief Main function logic * * Parse command line options and start running threads */ int main(int argc, char* argv[]) { int opt, idx, limit, only_calls, no_incomplete, i; const char *device, *outfile; char bpf[512]; const char *keyfile; const char *match_expr; int match_insensitive = 0, match_invert = 0; int no_interface = 0, quiet = 0, rtp_capture = 0; vector_t *infiles = vector_create(0, 1); // Program otptions static struct option long_options[] = { { "help", no_argument, 0, 'h' }, { "version", no_argument, 0, 'V' }, { "device", required_argument, 0, 'd' }, { "input", required_argument, 0, 'I' }, { "output", required_argument, 0, 'O' }, #if defined(WITH_GNUTLS) || defined(WITH_OPENSSL) { "keyfile", required_argument, 0, 'k' }, #endif { "calls", no_argument, 0, 'c' }, { "rtp", no_argument, 0, 'r' }, { "limit", no_argument, 0, 'l' }, { "icase", no_argument, 0, 'i' }, { "invert", no_argument, 0, 'v' }, { "no-interface", no_argument, 0, 'N' }, { "dump-config", no_argument, 0, 'D' }, #ifdef USE_EEP { "eep-listen", required_argument, 0, 'L' }, { "eep-send", required_argument, 0, 'H' }, #endif { "quiet", no_argument, 0, 'q' }, }; // Initialize configuration options init_options(); // Get initial values for configurable arguments device = setting_get_value(SETTING_CAPTURE_DEVICE); outfile = setting_get_value(SETTING_CAPTURE_OUTFILE); keyfile = setting_get_value(SETTING_CAPTURE_KEYFILE); limit = setting_get_intvalue(SETTING_CAPTURE_LIMIT); only_calls = setting_enabled(SETTING_SIP_CALLS); no_incomplete = setting_enabled(SETTING_SIP_NOINCOMPLETE); rtp_capture = setting_enabled(SETTING_CAPTURE_RTP); // Parse command line arguments opterr = 0; char *options = "hVd:I:O:pqtW:k:crl:ivNqDL:H:"; while ((opt = getopt_long(argc, argv, options, long_options, &idx)) != -1) { switch (opt) { case 'h': usage(); return 0; case 'V': version(); return 0; case 'd': device = optarg; break; case 'I': vector_append(infiles, optarg); break; case 'O': outfile = optarg; break; case 'l': if(!(limit = atoi(optarg))) { fprintf(stderr, "Invalid limit value.\n"); return 0; } break; #if defined(WITH_GNUTLS) || defined(WITH_OPENSSL) case 'k': keyfile = optarg; break; #endif case 'c': only_calls = 1; setting_set_value(SETTING_SIP_CALLS, SETTING_ON); break; case 'r': rtp_capture = 1; setting_set_value(SETTING_CAPTURE_RTP, SETTING_ON); break; case 'i': match_insensitive++; break; case 'v': match_invert++; break; case 'N': no_interface = 1; setting_set_value(SETTING_CAPTURE_STORAGE, "none"); break; case 'q': quiet = 1; break; case 'D': key_bindings_dump(); settings_dump(); return 0; // Dark options for dummy ones case 'p': case 't': case 'W': break; #ifdef USE_EEP case 'L': capture_eep_set_server_url(optarg); break; case 'H': capture_eep_set_client_url(optarg); break; #endif case '?': if (strchr(options, optopt)) { fprintf(stderr, "-%c option requires an argument.\n", optopt); } else if (isprint(optopt)) { fprintf(stderr, "Unknown option -%c.\n", optopt); } else { fprintf(stderr, "Unknown option character '\\x%x'.\n", optopt); } return 1; default: break; } } #if defined(WITH_GNUTLS) || defined(WITH_OPENSSL) // Set capture decrypt key file capture_set_keyfile(keyfile); // Check if we have a keyfile and is valid if (keyfile && !tls_check_keyfile(keyfile)) { fprintf(stderr, "%s does not contain a valid RSA private key.\n", keyfile); return 1; } #endif // Check if given argument is a file if (argc == 2 && (access(argv[1], F_OK) == 0)) { // Old legacy option to open pcaps without other arguments printf("%s seems to be a file: You forgot -I flag?\n", argv[1]); return 0; } // Initialize SIP Messages Storage sip_init(limit, only_calls, no_incomplete); // Set capture options capture_init(limit, rtp_capture); #ifdef USE_EEP // Initialize EEP if enabled capture_eep_init(); #endif // If we have an input file, load it if (vector_count(infiles)) { for (i = 0; i < vector_count(infiles); i++) { // Try to load file if (capture_offline(vector_item(infiles, i), outfile) != 0) return 1; } } else { // Check if all capture data is valid if (capture_online(device, outfile) != 0) return 1; } // Remove Input files vector vector_destroy(infiles); // More arguments pending! if (argv[optind]) { // Assume first pending argument is match expression match_expr = argv[optind++]; // Try to build the bpf filter string with the rest memset(bpf, 0, sizeof(bpf)); for (i = optind; i < argc; i++) sprintf(bpf + strlen(bpf), "%s ", argv[i]); // Check if this BPF filter is valid if (capture_set_bpf_filter(bpf) != 0) { // BPF Filter invalid, check incluiding match_expr match_expr = 0; // There is no need to parse all payload at this point // Build the bpf filter string memset(bpf, 0, sizeof(bpf)); for (i = optind - 1; i < argc; i++) sprintf(bpf + strlen(bpf), "%s ", argv[i]); // Check bpf filter is valid again if (capture_set_bpf_filter(bpf) != 0) { fprintf(stderr, "Couldn't install filter %s: %s\n", bpf, capture_last_error()); return 1; } } // Set the capture filter if (match_expr) if (sip_set_match_expression(match_expr, match_insensitive, match_invert)) { fprintf(stderr, "Unable to parse expression %s\n", match_expr); return 1; } } // Start a capture thread if (capture_launch_thread() != 0) { ncurses_deinit(); fprintf(stderr, "Failed to launch capture thread.\n"); return 1; } if (!no_interface) { // Initialize interface ncurses_init(); // This is a blocking call. // Create the first panel and wait for user input ui_create_panel(PANEL_CALL_LIST); wait_for_input(); } else { setbuf(stdout, NULL); while(capture_get_status() != CAPTURE_OFFLINE) { if (!quiet) printf("\rDialog count: %d", sip_calls_count()); usleep(500 * 1000); } if (!quiet) printf("\rDialog count: %d\n", sip_calls_count()); } // Capture deinit capture_deinit(); // Deinitialize interface ncurses_deinit(); // Deinitialize configuration options deinit_options(); // Deallocate sip stored messages sip_deinit(); // Leaving! return 0; }
void parse_packet(u_char *mode, const struct pcap_pkthdr *header, const u_char *packet) { // IP header data struct ip *ip4; #ifdef WITH_IPV6 // IPv6 header data struct ip6_hdr *ip6; #endif // IP version uint32_t ip_ver; // IP protocol uint8_t ip_proto; // IP header size uint32_t ip_hl = 0; // Fragment offset uint32_t ip_off = 0; // Fragmentation flag uint8_t ip_frag = 0; // Fragmentation offset uint16_t ip_frag_off = 0; //! Source Address char ip_src[ADDRESSLEN]; //! Destination Address char ip_dst[ADDRESSLEN]; // Source and Destination Ports u_short sport, dport; // UDP header data struct udphdr *udp; // UDP header size uint16_t udp_off; // TCP header data struct tcphdr *tcp; // TCP header size uint16_t tcp_off; // Packet payload data u_char *payload = NULL; // Packet payload size uint32_t size_payload = header->caplen; // SIP message transport int transport; // Media structure for RTP packets rtp_stream_t *stream; // Captured packet info capture_packet_t *pkt; // Ignore packets while capture is paused if (capture_is_paused()) return; // Check if we have reached capture limit if (capinfo.limit && sip_calls_count() >= capinfo.limit) return; // Get IP header ip4 = (struct ip *) (packet + capinfo.link_hl); #ifdef WITH_IPV6 // Get IPv6 header ip6 = (struct ip6_hdr *) (packet + capinfo.link_hl); #endif // Get IP version ip_ver = ip4->ip_v; switch (ip_ver) { case 4: ip_hl = ip4->ip_hl * 4; ip_proto = ip4->ip_p; ip_off = ntohs(ip4->ip_off); ip_frag = ip_off & (IP_MF | IP_OFFMASK); ip_frag_off = (ip_frag) ? (ip_off & IP_OFFMASK) * 8 : 0; // TODO Get fragment information inet_ntop(AF_INET, &ip4->ip_src, ip_src, sizeof(ip_src)); inet_ntop(AF_INET, &ip4->ip_dst, ip_dst, sizeof(ip_dst)); break; #ifdef WITH_IPV6 case 6: ip_hl = sizeof(struct ip6_hdr); ip_proto = ip6->ip6_nxt; if (ip_proto == IPPROTO_FRAGMENT) { struct ip6_frag *ip6f = (struct ip6_frag *) (ip6 + ip_hl); ip_frag_off = ntohs(ip6f->ip6f_offlg & IP6F_OFF_MASK); // TODO Get fragment information } inet_ntop(AF_INET6, &ip6->ip6_src, ip_src, sizeof(ip_src)); inet_ntop(AF_INET6, &ip6->ip6_dst, ip_dst, sizeof(ip_dst)); break; #endif default: return; } // Only interested in UDP packets if (ip_proto == IPPROTO_UDP) { // Get UDP header udp = (struct udphdr *)((u_char *)(ip4) + ip_hl); udp_off = (ip_frag_off) ? 0 : sizeof(struct udphdr); // Set packet ports sport = htons(udp->uh_sport); dport = htons(udp->uh_dport); // Get actual payload size size_payload -= capinfo.link_hl + ip_hl + udp_off; #ifdef WITH_IPV6 if (ip_ver == 6) size_payload -= ntohs(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen); #endif // Get payload start payload = (u_char *) (udp) + udp_off; // Set transport UDP transport = CAPTURE_PACKET_SIP_UDP; } else if (ip_proto == IPPROTO_TCP) { // Get TCP header tcp = (struct tcphdr *)((u_char *)(ip4 )+ ip_hl); tcp_off = (ip_frag_off) ? 0 : (tcp->th_off * 4); // Set packet ports sport = htons(tcp->th_sport); dport = htons(tcp->th_dport); // Get actual payload size size_payload -= capinfo.link_hl + ip_hl + tcp_off; #ifdef WITH_IPV6 if (ip_ver == 6) size_payload -= ntohs(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen); #endif // Get payload start payload = (u_char *)(tcp) + tcp_off; // Set transport TCP transport = CAPTURE_PACKET_SIP_TCP; } else { // Not handled protocol return; } if ((int32_t)size_payload < 0) size_payload = 0; // Create a structure for this captured packet pkt = capture_packet_create(header, packet, header->caplen); capture_packet_set_type(pkt, transport); // For TCP and UDP, use payload directly from the packet capture_packet_set_payload(pkt, NULL, size_payload); #ifdef WITH_OPENSSL // Check if packet is TLS if (capinfo.keyfile && transport == CAPTURE_PACKET_SIP_TCP) tls_process_segment(ip4, pkt); #endif // Check if packet is WS or WSS if (transport == CAPTURE_PACKET_SIP_TCP) capture_ws_check_packet(pkt); // We're only interested in packets with payload if (capture_packet_get_payload_len(pkt)) { // Parse this header and payload if (sip_load_message(pkt, ip_src, sport, ip_dst, dport)) { // Store this packets in output file dump_packet(capinfo.pd, header, packet); return; } // Check if this packet belongs to a RTP stream // TODO Store this packet in the stream if ((stream = rtp_check_stream(pkt, ip_src, sport, ip_dst, dport))) { // We have an RTP packet! capture_packet_set_type(pkt, CAPTURE_PACKET_RTP); // Store this pacekt if capture rtp is enabled if (capinfo.rtp_capture) { call_add_rtp_packet(stream_get_call(stream), pkt); } else { capture_packet_destroy(pkt); } // Store this packets in output file dump_packet(capinfo.pd, header, packet); return; } } // Not an interesting packet ... capture_packet_destroy(pkt); }
void parse_packet(u_char *info, const struct pcap_pkthdr *header, const u_char *packet) { // Capture info capture_info_t *capinfo = (capture_info_t *) info; // UDP header data struct udphdr *udp; // UDP header size uint16_t udp_off; // TCP header data struct tcphdr *tcp; // TCP header size uint16_t tcp_off; // Packet data u_char data[MAX_CAPTURE_LEN]; // Packet payload data u_char *payload = NULL; // Whole packet size uint32_t size_capture = header->caplen; // Packet payload size uint32_t size_payload = size_capture - capinfo->link_hl; // Captured packet info packet_t *pkt; // Ignore packets while capture is paused if (capture_paused()) return; // Check if we have reached capture limit if (capture_cfg.limit && sip_calls_count() >= capture_cfg.limit) return; // Check maximum capture length if (header->caplen > MAX_CAPTURE_LEN) return; // Copy packet payload memcpy(data, packet, header->caplen); // Check if we have a complete IP packet if (!(pkt = capture_packet_reasm_ip(capinfo, header, data, &size_payload, &size_capture))) return; // Only interested in UDP packets if (pkt->proto == IPPROTO_UDP) { // Get UDP header udp = (struct udphdr *)((u_char *)(data) + (size_capture - size_payload)); udp_off = sizeof(struct udphdr); // Set packet ports pkt->src.port = htons(udp->uh_sport); pkt->dst.port = htons(udp->uh_dport); // Remove UDP Header from payload size_payload -= udp_off; if ((int32_t)size_payload < 0) size_payload = 0; // Remove TCP Header from payload payload = (u_char *) (udp) + udp_off; // Complete packet with Transport information packet_set_type(pkt, PACKET_SIP_UDP); packet_set_payload(pkt, payload, size_payload); } else if (pkt->proto == IPPROTO_TCP) { // Get TCP header tcp = (struct tcphdr *)((u_char *)(data) + (size_capture - size_payload)); tcp_off = (tcp->th_off * 4); // Set packet ports pkt->src.port = htons(tcp->th_sport); pkt->dst.port = htons(tcp->th_dport); // Get actual payload size size_payload -= tcp_off; if ((int32_t)size_payload < 0) size_payload = 0; // Get payload start payload = (u_char *)(tcp) + tcp_off; // Complete packet with Transport information packet_set_type(pkt, PACKET_SIP_TCP); packet_set_payload(pkt, payload, size_payload); // Create a structure for this captured packet if (!(pkt = capture_packet_reasm_tcp(pkt, tcp, payload, size_payload))) return; #if defined(WITH_GNUTLS) || defined(WITH_OPENSSL) // Check if packet is TLS if (capture_cfg.keyfile) { tls_process_segment(pkt, tcp); } #endif // Check if packet is WS or WSS capture_ws_check_packet(pkt); } else { // Not handled protocol packet_destroy(pkt); return; } // Avoid parsing from multiples sources. // Avoid parsing while screen in being redrawn capture_lock(); // Check if we can handle this packet if (capture_packet_parse(pkt) == 0) { #ifdef USE_EEP // Send this packet through eep capture_eep_send(pkt); #endif // Store this packets in output file dump_packet(capture_cfg.pd, pkt); // If storage is disabled, delete frames payload if (capture_cfg.storage == 0) { packet_free_frames(pkt); } // Allow Interface refresh and user input actions capture_unlock(); return; } // Not an interesting packet ... packet_destroy(pkt); // Allow Interface refresh and user input actions capture_unlock(); }
sip_msg_t * sip_check_packet(packet_t *packet) { sip_msg_t *msg; sip_call_t *call; char callid[1024], xcallid[1024]; address_t src, dst; u_char payload[MAX_SIP_PAYLOAD]; bool newcall = false; // Max SIP payload allowed if (packet->payload_len > MAX_SIP_PAYLOAD) return NULL; // Get Addresses from packet src = packet->src; dst = packet->dst; // Initialize local variables memset(callid, 0, sizeof(callid)); memset(xcallid, 0, sizeof(xcallid)); // Get payload from packet(s) memset(payload, 0, MAX_SIP_PAYLOAD); memcpy(payload, packet_payload(packet), packet_payloadlen(packet)); // Get the Call-ID of this message if (!sip_get_callid((const char*) payload, callid)) return NULL; // Create a new message from this data if (!(msg = msg_create((const char*) payload))) return NULL; // Get Method and request for the following checks // There is no need to parse all payload at this point // If no response or request code is found, this is not a SIP message if (!sip_get_msg_reqresp(msg, payload)) { // Deallocate message memory msg_destroy(msg); return NULL; } // Find the call for this msg if (!(call = sip_find_by_callid(callid))) { // Check if payload matches expression if (!sip_check_match_expression((const char*) payload)) goto skip_message; // User requested only INVITE starting dialogs if (calls.only_calls && msg->reqresp != SIP_METHOD_INVITE) goto skip_message; // Only create a new call if the first msg // is a request message in the following gorup if (calls.ignore_incomplete && msg->reqresp > SIP_METHOD_MESSAGE) goto skip_message; // Get the Call-ID of this message sip_get_xcallid((const char*) payload, xcallid); // Rotate call list if limit has been reached if (calls.limit == sip_calls_count()) sip_calls_rotate(); // Create the call if not found if (!(call = call_create(callid, xcallid))) goto skip_message; // Add this Call-Id to hash table htable_insert(calls.callids, call->callid, call); // Set call index call->index = ++calls.last_index; // Mark this as a new call newcall = true; } // At this point we know we're handling an interesting SIP Packet msg->packet = packet; // Always parse first call message if (call_msg_count(call) == 0) { // Parse SIP payload sip_parse_msg_payload(msg, payload); // If this call has X-Call-Id, append it to the parent call if (strlen(call->xcallid)) { call_add_xcall(sip_find_by_callid(call->xcallid), call); } } // Add the message to the call call_add_message(call, msg); if (call_is_invite(call)) { // Parse media data sip_parse_msg_media(msg, payload); // Update Call State call_update_state(call, msg); // Check if this call should be in active call list if (call_is_active(call)) { if (sip_call_is_active(call)) { vector_append(calls.active, call); } } else { if (sip_call_is_active(call)) { vector_remove(calls.active, call); } } } if (newcall) { // Append this call to the call list vector_append(calls.list, call); } // Mark the list as changed calls.changed = true; // Return the loaded message return msg; skip_message: // Deallocate message memory msg_destroy(msg); return NULL; }
void parse_packet(u_char *mode, const struct pcap_pkthdr *header, const u_char *packet) { // Datalink Header size int size_link; // IP version uint32_t ip_ver; // IP header data struct ip *ip4; #ifdef WITH_IPV6 // IPv6 header data struct ip6_hdr *ip6; #endif // IP protocol uint8_t ip_proto; // IP segment length uint32_t ip_len; // IP header size uint32_t size_ip; // Fragment offset uint16_t ip_off = 0; // Fragmentation flag uint8_t ip_frag = 0; // Fragmentation offset uint16_t ip_frag_off = 0; //! Source Address char ip_src[INET6_ADDRSTRLEN + 1]; //! Destination Address char ip_dst[INET6_ADDRSTRLEN + 1]; // UDP header data struct udphdr *udp; // UDP header size uint16_t udp_size; // TCP header data struct tcphdr *tcp; // TCP header size uint16_t tcp_size; // Packet payload data u_char *msg_payload = NULL; // Packet payload size uint32_t size_payload; // Parsed message data sip_msg_t *msg; // Total packet size uint32_t size_packet; // SIP message transport int transport; /* 0 UDP, 1 TCP, 2 TLS */ // Source and Destination Ports u_short sport, dport; // Ignore packets while capture is paused if (capture_is_paused()) return; // Check if we have reached capture limit if (capinfo.limit && sip_calls_count() >= capinfo.limit) return; // Get link header size from datalink type size_link = datalink_size(capinfo.link); // Get IP header ip4 = (struct ip*) (packet + size_link); #ifdef WITH_IPV6 // Get IPv6 header ip6 = (struct ip6_hdr*)(packet + size_link); #endif // Get IP version ip_ver = ip4->ip_v; switch(ip_ver) { case 4: size_ip = ip4->ip_hl * 4; ip_proto = ip4->ip_p; ip_len = ntohs(ip4->ip_len); inet_ntop(AF_INET, &ip4->ip_src, ip_src, sizeof(ip_src)); inet_ntop(AF_INET, &ip4->ip_dst, ip_dst, sizeof(ip_dst)); ip_off = ntohs(ip4->ip_off); ip_frag = ip_off & (IP_MF | IP_OFFMASK); ip_frag_off = (ip_frag) ? (ip_off & IP_OFFMASK) * 8 : 0; break; #ifdef WITH_IPV6 case 6: size_ip = sizeof(struct ip6_hdr); ip_proto = ip6->ip6_nxt; ip_len = ntohs(ip6->ip6_plen); inet_ntop(AF_INET6, &ip6->ip6_src, ip_src, INET6_ADDRSTRLEN); inet_ntop(AF_INET6, &ip6->ip6_dst, ip_dst, INET6_ADDRSTRLEN); if (ip_proto == IPPROTO_FRAGMENT) { struct ip6_frag *ip6f = (struct ip6_frag *) (ip6 + ip_len); ip_frag_off = ntohs(ip6f->ip6f_offlg & IP6F_OFF_MASK); } break; #endif default: return; } // Only interested in UDP packets if (ip_proto == IPPROTO_UDP) { // Set transport UDP transport = 0; // Get UDP header udp = (struct udphdr*) (packet + size_link + size_ip); udp_size = (ip_frag_off) ? 0 : sizeof(struct udphdr); // Set packet ports sport = udp->uh_sport; dport = udp->uh_dport; size_payload = htons(udp->uh_ulen) - udp_size; if ((int32_t)size_payload > 0 ) { // Get packet payload msg_payload = malloc(size_payload + 1); memset(msg_payload, 0, size_payload + 1); memcpy(msg_payload, (u_char *) (packet + size_link + size_ip + udp_size), size_payload); } // Total packet size size_packet = size_link + size_ip + udp_size + size_payload; } else if (ip_proto == IPPROTO_TCP) { // Set transport TCP transport = 1; tcp = (struct tcphdr*) (packet + size_link + size_ip); tcp_size = (ip_frag_off) ? 0 : (tcp->th_off * 4); // Set packet ports sport = tcp->th_sport; dport = tcp->th_dport; // We're only interested in packets with payload size_payload = ip_len - (size_ip + tcp_size); if ((int32_t)size_payload > 0) { // Get packet payload msg_payload = malloc(size_payload + 1); memset(msg_payload, 0, size_payload + 1); memcpy(msg_payload, (u_char *) (packet + size_link + size_ip + tcp_size), size_payload); } // Total packet size size_packet = size_link + size_ip + tcp_size + size_payload; #ifdef WITH_OPENSSL if (!msg_payload || !strstr((const char*) msg_payload, "SIP/2.0")) { if (capture_get_keyfile()) { // Allocate memory for the payload msg_payload = malloc(size_payload + 1); memset(msg_payload, 0, size_payload + 1); // Try to decrypt the packet tls_process_segment(ip4, &msg_payload, &size_payload); // Check if we have decoded payload if (size_payload <= 0) free(msg_payload); // Set Transport TLS transport = 2; } } #endif } else { // Not handled protocol return; } // Increase capture stats if (ip4->ip_v == 4 && capinfo.devices) { if(is_local_address(ip4->ip_src.s_addr)) { capinfo.local_ports[htons(sport)]++; capinfo.remote_ports[htons(dport)]++; } else { capinfo.remote_ports[htons(sport)]++; capinfo.local_ports[htons(dport)]++; } } // We're only interested in packets with payload if (size_payload <= 0) return; // Parse this header and payload msg = sip_load_message(header, ip_src, sport, ip_dst, dport, msg_payload); free(msg_payload); // This is not a sip message, Bye! if (!msg) return; // Store Transport attribute if (transport == 0) { msg_set_attribute(msg, SIP_ATTR_TRANSPORT, "UDP"); } else if (transport == 1) { msg_set_attribute(msg, SIP_ATTR_TRANSPORT, "TCP"); } else if (transport == 2) { msg_set_attribute(msg, SIP_ATTR_TRANSPORT, "TLS"); } // Set message PCAP data msg->pcap_packet = malloc(size_packet); memcpy(msg->pcap_packet, packet, size_packet); // Store this packets in output file dump_packet(capinfo.pd, header, packet); }