void parse_dynamic_trunking_protocol(struct Ferret *ferret, struct NetFrame *frame, const unsigned char *px, unsigned length) { unsigned offset; if (length < 1) { FRAMERR(frame, "truncated\n"); return; } /* Version */ if (px[0] != 0x01) { FRAMERR(frame, "unexpected\n"); return; } /* Look for TLV values */ for (offset=1; offset+4<length; ) { unsigned tag = ex16be(px+offset+0); unsigned length = ex16be(px+offset+2); if (tag == 0 && length == 0) break; if (length < 4) { FRAMERR(frame, "unexpected\n"); return; } length -= 4; offset += 4; switch (tag) { case 0x0001: /* domain */ JOTDOWN(ferret, JOT_MACADDR("ID-MAC", frame->src_mac), JOT_PRINT("DTP-Domain", px+offset, length), 0); break; case 0x0002: /* status */ break; case 0x0003: /* Dtptype */ break; case 0x0004: /* neighbor */ /* TODO: is this interesting? */ break; default: FRAMERR(frame, "unknown 0x%x\n", tag); } offset += length; } }
void process_gre_pptp(struct Seaper *seap, struct NetFrame *frame, const unsigned char *px, unsigned length) { unsigned flags; unsigned offset; unsigned payload_length; unsigned call_id; unsigned sequence_number; unsigned acknowledgement_number; /* 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |C|R|K|S|s|Recur|A| Flags | Ver | Protocol Type | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Key (HW) Payload Length | Key (LW) Call ID | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Sequence Number (Optional) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Acknowledgment Number (Optional) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ flags = ex16be(px); payload_length = ex16be(px+4); call_id = ex16be(px+6); if ((flags&0xE80F) != 0x2001) { FRAMERR_UNKNOWN_UNSIGNED(frame, "gre", flags); return; } offset = 8; if (flags & 0x1000) { sequence_number = ex32be(px+offset); offset += 4; } if (flags & 0x0080) { acknowledgement_number = ex32be(px+offset); offset += 4; } if (offset >= length) { FRAMERR_TRUNCATED(frame, "gre"); return; } process_pptp(seap, frame, px+offset, length-offset); }
void process_cisco00000c(struct Ferret *ferret, struct NetFrame *frame, const unsigned char *px, unsigned length) { unsigned offset=0; unsigned pid; if (offset+2 > length) { FRAMERR(frame, "%s: truncated\n", "cisco"); return; } pid = ex16be(px); SAMPLE(ferret,"Cisco", JOT_NUM("0x00000c-pid", pid)); offset+= 2; switch (pid) { case 0x2000: parse_CDP(ferret, frame, px+offset, length-offset); break; case 0x010b: parse_PVSTP(ferret, frame, px+offset, length-offset); break; case 0x2003: /* Cisco VLAN Trunking Protocol */ process_cisco_vtp(ferret, frame, px+offset, length-offset); break; case 0x2004: /* Cisco Dynamic Trunking Protocol */ parse_dynamic_trunking_protocol(ferret, frame, px+offset, length-offset); break; default: FRAMERR(frame, "%s: unknown value: 0x%x\n", "cisco", pid); } }
/** * Formats in traditional IPv6 format, something like [FFFF:FFFF:FFFF:FFFF] */ void format_ipv6(char *valbuf, size_t sizeof_valbuf, size_t *r_vallen, const unsigned char *value) { unsigned i; unsigned nulls=0; size_t vallen = *r_vallen; valbuf[vallen++] = '['; /* Loop through all 8 bytes, formatting each one */ for (i=0; i<8; i++) { const unsigned n = ex16be(value+i*2); if (n == 0) { if (nulls == 0) { if (vallen+2<sizeof(valbuf)) valbuf[vallen++] = ':'; nulls = 1; } else if (nulls == 1) ; else if (vallen + 7 < sizeof_valbuf) { valbuf[vallen++] = ':'; append_hexdigit(valbuf, sizeof_valbuf, &vallen, (n>>12)&0xF); append_hexdigit(valbuf, sizeof_valbuf, &vallen, (n>> 8)&0xF); append_hexdigit(valbuf, sizeof_valbuf, &vallen, (n>> 4)&0xF); append_hexdigit(valbuf, sizeof_valbuf, &vallen, (n>> 0)&0xF); } } else { if (nulls == 1)
unsigned smellslike_aim_oscar(const unsigned char *px, unsigned length) { unsigned pdu_length; unsigned offset=0; unsigned i; /* +--------+--------+ | 0x2a | channel| +--------+--------+ | seqno | +--------+--------+ | length | +--------+--------+ | | 0x2a: This is always the first byte of a PDU CHANNEL: 0x01 - New Connection Negotiation 0x02 - SNAC data (non connection-oriented data) 0x03 - FLAP-level Error 0x04 - Close Connection Negotiation */ for (i=0; i<2; i++) { /* make sure we have enbough bytes in the header */ if (offset+6 > length) return 0; /* make sure the first byte is the expect '0x2a' that's at the * start of all AIM/OSCAR pdus */ if (px[offset] != 0x2a) return 0; /* make sure the channel is within the expected range */ if (px[offset+1] < 0x01 || 0x04 <= px[offset+1]) return 0; /* check to see if the length is precisely the size of the TCP * payload. This wouldn't work if the packet was fragmented, of * course */ pdu_length = ex16be(px+offset+4); if (length < 6+pdu_length) return 0; /* TCP packet too small*/ if (length == 6+pdu_length) return 1; /* TCP packet just right */ /* the packet was too long. this may be due to multiple pdus in * a single TCP packet. Therefore, we are going to check the next * one */ offset += 6 + pdu_length; } /* We've made it through two loops of the above sequence. There * might be even more, but we'll end here because we have enough * to be pretty sure */ return 1; }
void process_gre(struct Seaper *seap, struct NetFrame *frame, const unsigned char *px, unsigned length) { unsigned flags; unsigned version; unsigned protocol; unsigned offset; /* 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |C|R|K|S|s|Recur| Flags | Ver | Protocol Type | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Checksum (optional) | Offset (optional) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Key (optional) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Sequence Number (optional) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Routing (optional) +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ if (length < 8) { FRAMERR_TRUNCATED(frame, "gre"); return; } flags = ex16be(px); version = px[1]&0x7; protocol = ex16be(px+2); offset = 4; if (version == 1 && protocol == 0x880b) process_gre_pptp(seap, frame, px, length); else { FRAMERR_UNKNOWN_UNSIGNED(frame, "gre", version); } }
void process_icmp(struct Ferret *ferret, struct NetFrame *frame, const unsigned char *px, unsigned length) { unsigned type = px[0]; unsigned code = px[1]; unsigned checksum = ex16be(px+2); UNUSEDPARM(length); UNUSEDPARM(frame); UNUSEDPARM(checksum); ferret->statistics.icmp++; frame->layer4_protocol = LAYER4_ICMP; JOTDOWN(ferret, JOT_SZ("TEST","icmp"), JOT_NUM("type",type), JOT_NUM("code",code), 0); }
void process_icmpv6(struct Ferret *ferret, struct NetFrame *frame, const unsigned char *px, unsigned length) { unsigned type = px[0]; unsigned code = px[1]; unsigned checksum = ex16be(px+2); UNUSEDPARM(length); UNUSEDPARM(frame); UNUSEDPARM(checksum); frame->layer4_protocol = LAYER4_ICMP; JOTDOWN(ferret, JOT_SZ("TEST","icmp"), JOT_NUM("type",type), JOT_NUM("code",code), 0); if (frame->dst_ipv6[0] == 0xFF) JOTDOWN(ferret, JOT_MACADDR("ID-MAC", frame->src_mac), JOT_IPv6("ipv6", frame->src_ipv6, 16), 0); }
void process_dns(struct Ferret *ferret, struct NetFrame *frame, const unsigned char *px, unsigned length) { unsigned offset; struct DNS dns[1]; unsigned record_count; unsigned total_records; unsigned i; /* Count the number of DNS packets we process. This includes * all types of DNS */ ferret->statistics.dns++; memset(dns, 0, sizeof(dns[0])); if (length < 12) { /* Regress: defcon2008-dns2.pcap(95639) */ ; //FRAMERR(frame, "dns: frame too short\n"); return; } /* Parse the DNS header, the 'fixed' portion of the packet * before the variable length records +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ID | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |QR| Opcode |AA|TC|RD|RA| Z | RCODE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | QDCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ANCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | NSCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ARCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ ID A 16 bit identifier assigned by the program that generates any kind of query. This identifier is copied the corresponding reply and can be used by the requester to match up replies to outstanding queries. QR A one bit field that specifies whether this message is a query (0), or a response (1). OPCODE A four bit field that specifies kind of query in this message. This value is set by the originator of a query and copied into the response. The values are: 0 a standard query (QUERY) 1 an inverse query (IQUERY) 2 a server status request (STATUS) 3-15 reserved for future use AA Authoritative Answer - this bit is valid in responses, and specifies that the responding name server is an authority for the domain name in question section. Note that the contents of the answer section may have multiple owner names because of aliases. The AA bit corresponds to the name which matches the query name, or the first owner name in the answer section. TC TrunCation - specifies that this message was truncated due to length greater than that permitted on the transmission channel. RD Recursion Desired - this bit may be set in a query and is copied into the response. If RD is set, it directs the name server to pursue the query recursively. Recursive query support is optional. RA Recursion Available - this be is set or cleared in a response, and denotes whether recursive query support is available in the name server. Z Reserved for future use. Must be zero in all queries and responses. RCODE Response code - this 4 bit field is set as part of responses. The values have the following interpretation: 0 No error condition 1 Format error - The name server was unable to interpret the query. 2 Server failure - The name server was unable to process this query due to a problem with the name server. 3 Name Error - Meaningful only for responses from an authoritative name server, this code signifies that the domain name referenced in the query does not exist. 4 Not Implemented - The name server does not support the requested kind of query. 5 Refused - The name server refuses to perform the specified operation for policy reasons. For example, a name server may not wish to provide the information to the particular requester, or a name server may not wish to perform a particular operation (e.g., zone 6-15 Reserved for future use. QDCOUNT an unsigned 16 bit integer specifying the number of entries in the question section. ANCOUNT an unsigned 16 bit integer specifying the number of resource records in the answer section. NSCOUNT an unsigned 16 bit integer specifying the number of name server resource records in the authority records section. ARCOUNT an unsigned 16 bit integer specifying the number of resource records in the additional records section. */ dns->id = ex16be(px+0); dns->is_response = ((px[2]&0x80) != 0); dns->opcode = (px[2]>>3)&0x01F; dns->flags = ex16be(px+2)&0x7F0; dns->rcode = px[3]&0x0F; dns->question_count = ex16be(px+4); dns->answer_count = ex16be(px+6); dns->authority_count = ex16be(px+8); dns->additional_count = ex16be(px+10); /* Remember a total count of the records. There are a lot of corrupted packets * with data after the counted records, so we need to stop parsing once all * the counted records have been parsed. */ total_records = dns->question_count + dns->answer_count + dns->authority_count + dns->additional_count; offset = 12; record_count = 0; /* After parsing the fixed header, we no PRE-PROCESS the variable length records. * All we want to do at this point is to find their locations within the packet. * The reason we want to pre-process these is that some records will refer to * other records in the same packet. A good example are CNAME records * that require looking up other records in order to fully resolve. We can * do the resolution easier if we can preprocess the list first */ while (offset < length && record_count < 100) { struct DNSRECORD *rec = &dns->records[record_count]; /* Even if there is remaining data in the packet, do not parse past * the total count of all the records */ if (record_count >= total_records) { SAMPLE(ferret,"dns", JOT_NUM("too-many-records", total_records)); break; } /* NAME * The first part of a DNS record is the variable length name. The name * consists of a sequence of LABELS. Each label starts with a tag. The * tag can be one of three things: * - a value of zero, which ends the name * - a length from 1-63, which means we need to continue processing * more labels * - a two-byte 'pointer' to the remainder of the name, which means that * we stop pre-processing the name. We don't actually parse out the * full name at this stage in the code (so we won't follow that pointer), * we are just concerned with skipping the name at this point. */ rec->name_offset = offset; while (offset < length) { /* Test for end label */ if (px[offset] == 0x00) { offset++; break; } /* Test for compression 'pointer' */ if (px[offset] & 0xC0) { offset += 2; break; } /* Skip the 'length' number of bytes, plus the length byte itself */ offset += px[offset] + 1; if (offset > length) { FRAMERR(frame, "dns: past end of packet\n"); return; } } /* Now parse out the 'type' and 'class' fields. These are the * 4 bytes immediately following the name */ if (offset + 4 > length) { FRAMERR(frame, "dns: past end of packet\n"); return; } rec->type = ex16be(px+offset+0); rec->clss = ex16be(px+offset+2); offset += 4; record_count++; /* If this is a 'question' record, then we don't do any further processing. * Since question records are just asking for data, they don't contain * any data themselves. Otherwise, if the record will contain data that * we need to also parse */ if (record_count <= dns->question_count) continue; /* This bit of code parses out the remainder of the data in the record. For * the most part, we just need to parse the 'length' field for the record * data, remember it for use later, then skip the remainder of this record * and continue processing the next record */ if (offset + 6 > length) { /* Regress: defcon2008-dns2.pcap(88069) */ FRAMERR(frame, "dns: past end of packet\n"); return; } rec->ttl = ex32be(px+offset+0); rec->rdata_length = ex16be(px+offset+4); offset += 6; rec->rdata_offset = offset; offset += rec->rdata_length; if (offset > length) { FRAMERR(frame, "dns: past end of packet\n"); return; } } dns->record_count = record_count; /* We stored the records in one large array, but there are four kinds * of records (questions, answers, authority, additional). We figure out * which records belong to which type according to the counts */ if (dns->question_count > record_count) { dns->question_count = record_count; FRAMERR(frame, "%s: bad record count\n", "DNS"); } if (dns->answer_count > record_count - dns->question_count) { dns->answer_count = record_count - dns->question_count; /* Regress: defcon2008-dns2.pcap(158112) */ ; //FRAMERR(frame, "%s: bad record count\n", "DNS"); } if (dns->authority_count > record_count - dns->question_count - dns->answer_count) { dns->authority_count = record_count - dns->question_count - dns->answer_count; FRAMERR(frame, "%s: bad record count\n", "DNS"); } if (dns->additional_count > record_count - dns->question_count - dns->answer_count - dns->authority_count) { dns->additional_count = record_count - dns->question_count - dns->answer_count - dns->authority_count; FRAMERR(frame, "%s: bad record count\n", "DNS"); } dns->questions = &dns->records[0]; dns->answers = &dns->records[dns->question_count]; dns->authorities = &dns->records[dns->question_count + dns->answer_count]; dns->additionals = &dns->records[dns->question_count + dns->answer_count + dns->authority_count]; /* * First, we parse out all the question records. These don't contain any data * themselves, but they do give us interesting information about what a client * is looking for. Also, some protocols, such as NetBIOS and mDNS/Bonjour will * tell us additional information about the client. */ if (dns->is_response && dns->rcode == 0) for (i=0; i<dns->question_count; i++) { struct DNSRECORD *rec = &dns->questions[i]; dns_parse_question_record(ferret, frame, px, length, rec, dns); } /* Now parse all the resource records after the questions */ for (i=0; i<dns->answer_count; i++) { struct DNSRECORD *rec = &dns->answers[i]; dns_parse_resource_record(ferret, frame, px, length, rec, dns); } for (i=0; i<dns->authority_count; i++) { struct DNSRECORD *rec = &dns->authorities[i]; dns_parse_resource_record(ferret, frame, px, length, rec, dns); } for (i=0; i<dns->additional_count; i++) { struct DNSRECORD *rec = &dns->additionals[i]; dns_parse_resource_record(ferret, frame, px, length, rec, dns); } #if 0 switch (dns->opcode) { case 0x00: /*query request*/ case 0x10: /*query response */ switch (dns->rcode) { case 0: case 3: /* No such name */ SAMPLE(ferret,"DNS", JOT_NUM("rcode", dns->rcode)); break; case 2: /* Server error */ SAMPLE(ferret,"DNS", JOT_NUM("rcode", dns->rcode)); break; default: FRAMERR(frame, "dns: unknown rcode=%d (opcode=%d)\n", dns->rcode, dns->opcode); } break; case 0x06: /*release*/ switch (dns->rcode) { case 0: for (i=0; i<dns->additional_count; i++) { char name[256]; unsigned name_length; struct DNSRECORD *rec = &dns->additionals[i]; if (rec->type == 0x8001) FRAMERR(frame, "test\n"); name_length = dns_extract_name(frame, px, length, rec->name_offset, name, sizeof(name)); switch (rec->type) { case 0x0020: /*NETBIOS */ switch (rec->clss) { case 0x0001: /*INTERNET*/ { unsigned ip_address = ex32be(px+rec->rdata_offset+2); char netbios_name[256]; if (rec->rdata_length != 6) FRAMERR(frame, "dns: data not 4-bytes long, was %d-bytes instead (class=%d, type=%d, name=%s)\n", rec->rdata_length, rec->clss, rec->type, name); translate_netbios_name(frame, name, netbios_name, sizeof(netbios_name)); JOTDOWN(ferret, JOT_SZ("proto","NETBIOS"), JOT_SZ("op","release"), JOT_DST("ip.src", frame), JOT_PRINT("name", netbios_name, strlen(netbios_name)), JOT_IPv4("address", ip_address), 0); JOTDOWN(ferret, JOT_IPv4("ID-IP", ip_address), JOT_PRINT("netbios", netbios_name, strlen(netbios_name)), 0); } break; default: FRAMERR(frame, "dns: unknown class=%d (type=%d, name=%s)\n", rec->clss, rec->type, name); } break; default: FRAMERR(frame, "dns: unknown type=%d (class=%d, name=%s)\n", rec->type, rec->clss, name); } } } break; case 0x05: /*netbios registration request*/ if (frame->dst_port == 53) dns_dynamic_update(ferret, frame, px, length, dns); else process_request_update(ferret, frame, px, length, dns); break; case 0x08: for (i=0; i<dns->additional_count; i++) DECODEANSWER(ferret, frame, px, length, dns, &dns->additionals[i], "refresh"); break; case 0x01: /*inverse query request*/ case 0x11: /*inverse query reqsponse*/ case 0x02: /*status request*/ case 0x12: /*status response*/ case 0x04: /*notify request*/ case 0x14: /*notify response*/ case 0x15: /*update response*/ case 0x0f: /*multi-home registration*/ for (i=0; i<dns->additional_count; i++) DECODEANSWER(ferret, frame, px, length, dns, &dns->additionals[i], "multi-home"); break; default: FRAMERR(frame, "dns: unknown opcode %d\n", dns->opcode); } #endif }
/** * This function extracts a DNS name from the packet and formats it according * to DNS rules. Essentially, this means that if a '.' dot character appears inside * of a label, then we'll show '\.' escaped dot. Likewise, if the '\' escape * character appears inside of a label, we'll '\\' escape it as well. Otherwise, * we'll show all binary and spaces as they are natively in the stream. */ unsigned dns_extract_name(struct NetFrame *frame, const unsigned char *px, unsigned length, unsigned offset, char *name, unsigned sizeof_name) { int recurse_count = 0; unsigned name_offset = 0; name[0] = '\0'; /* For all labels ... */ while (offset < length) { unsigned len; /* The 'empty' label ends the DNS name */ if (px[offset] == 0x00) break; /* Look for a Lempel-Ziv compression, which points backwards in the packet * to some other repeated name, as much as 16k into the packet */ if (px[offset] & 0xC0) { /* Check for repeated recursion. We can point to another label, * or to annother pointer. Indeed, a vulnerability in older * DNS implementations is where if the pointer pointed to itself, * then the DNS would go into an infinite loop endlessly following * that pointer */ if (recurse_count > 100) { FRAMERR(frame, "dns: name: recursion exceeded %d\n", recurse_count); break; } recurse_count++; /* This is actually a 2-byte field, so we need to check for the * extra byte has not run past the end of the packet */ if (offset+2 > length) { FRAMERR(frame, "dns: name: not enough bytes\n"); strcpy_s(name, sizeof_name, "(err)"); return 5; } /* Extract the lower 14-bits and use that as the new offset. Note that * this means we can use this compression features that point to a name * past the 16k boundary, but that isn't really a big problem because * packets really never get that big. */ offset = ex16be(px+offset)&0x3FFF; continue; } /* Otherwise, we have a normal label. The first step is to grab the * length of this label and make sure we haven 't gone past the end * of the packet */ len = px[offset++]; if (offset >= length) { FRAMERR(frame, "dns: name: not enough bytes\n"); strcpy_s(name, sizeof_name, "(err)"); return 5; } if (offset+len > length) { FRAMERR(frame, "dns: name: not enough bytes\n"); strcpy_s(name, sizeof_name, "(err)"); return 5; } /* If there were already a label in the name, make sure that there * is the '.' character between the previoius label an this label. */ if (name_offset > 0) { if (name_offset+1 >= sizeof_name) { FRAMERR(frame, "dns: name: too long\n"); strcpy_s(name, sizeof_name, "(err)"); return 5; } name[name_offset++] = '.'; } /* Make sure there is enough space left in the name, as well as enough * psace for the NUL terminating character */ if (name_offset+len+1 >= sizeof_name) { FRAMERR(frame, "dns: name: too long\n"); strcpy_s(name, sizeof_name, "(err)"); return 5; } /* Copy over the name */ memcpy(name+name_offset, px+offset, len); name_offset += len; name[name_offset] = '\0'; /* Now go onto the next label in the DNS name */ offset += len; } return name_offset; }
void decode_message(struct TCPRECORD *sess, struct NetFrame *frame, const unsigned char *px, unsigned length, unsigned is_outgoing) { struct FerretEngine *eng = sess->eng; struct Ferret *ferret = eng->ferret; const unsigned char *msg = px; unsigned msg_length = length; unsigned msg_offset = 0; if (msg_length > 2 && ex16be(msg+msg_offset) == 0x501) { /*unsigned flags = ex16be(msg+msg_offset);*/ unsigned len=0; msg_offset += 2; if (msg_offset+2 < msg_length) { len = ex16be(msg+msg_offset); msg_offset += len+2; } if (msg_offset+2 < msg_length) msg_offset += 2; /* block info */ if (msg_offset+2 < msg_length) { len = ex16be(msg+msg_offset); msg_offset += 2; /* block length */ } msg_offset += 4; /* character set */ if (len > 4) len -= 4; /* subtract the charset info from the block lenght*/ if (msg_offset > msg_length) { FRAMERR(frame, "%s: integer overflow\n", "AIM"); return; } if (msg_offset + len > msg_length) len = msg_length - msg_offset; if (len > 6 && strnicmp((const char*)msg+msg_offset, "<HTML>", 6)==0) { unsigned char *msg2 = alloca(len); unsigned msg2_len; msg2_len = strip_html_tags(msg+msg_offset, len, msg2, len); if (is_outgoing) JOTDOWN(ferret, JOT_SRC("ID-IP",frame), JOT_PRINT("AIM-Message-To", sess->str[1].the_string, sess->str[1].length), JOT_PRINT("AIM-Message", msg2, msg2_len), 0); else JOTDOWN(ferret, JOT_DST("ID-IP",frame), JOT_PRINT("AIM-Message-From", sess->str[1].the_string, sess->str[1].length), JOT_PRINT("AIM-Message", msg2, msg2_len), 0); } else { if (is_outgoing) JOTDOWN(ferret, JOT_SRC("ID-IP",frame), JOT_PRINT("AIM-Message-To", sess->str[1].the_string, sess->str[1].length), JOT_PRINT("AIM-Message", msg+msg_offset, msg_length-msg_offset), 0); else JOTDOWN(ferret, JOT_DST("ID-IP",frame), JOT_PRINT("AIM-Message-From", sess->str[1].the_string, sess->str[1].length), JOT_PRINT("AIM-Message", msg+msg_offset, msg_length-msg_offset), 0); } } else { while (msg_offset<msg_length && msg[msg_offset] < 26) msg_offset++; if (is_outgoing) JOTDOWN(ferret, JOT_SRC("ID-IP",frame), JOT_PRINT("AIM-Message-To", sess->str[1].the_string, sess->str[1].length), JOT_PRINT("AIM-Message", msg+msg_offset, msg_length-msg_offset), 0); else JOTDOWN(ferret, JOT_DST("ID-IP",frame), JOT_PRINT("AIM-Message-From", sess->str[1].the_string, sess->str[1].length), JOT_PRINT("AIM-Message", msg+msg_offset, msg_length-msg_offset), 0); } }
/** * Parse the "rendez-vous" TLV within a packet. Since this is a TLV, it has * already been reassembled by our string frag parser. */ static void parse_message_filexfer_rendezvous(struct TCPRECORD *sess, struct NetFrame *frame, const unsigned char *px, unsigned length) { unsigned offset=0; /* skip some fields */ offset += 2+8; /* verify we have the file transfer ID */ while (offset < 2+8+16) { if (px[offset] != (unsigned char)("\x09\x46\x13\x43\x4c\x7f\x11\xd1\x82\x22\x44\x45\x53\x54\x00\x00"[offset-8-2])) return; /* not a file transfer command */ /*TODO: SAMPLE this */ offset++; } /* go through the embeded TLVs */ while (offset<length) { unsigned tag; unsigned len; if (offset+4>length) break; tag = ex16be(px+offset+0); len = ex16be(px+offset+2); offset += 4; /* TLV: Unknown Value ID: Unknown (0x000a) Length: 2 Value TLV: Unknown Value ID: Unknown (0x000f) Length: 0 Value TLV: Internal IP Value ID: Internal IP (0x0003) Length: 4 Value: 12625930 TLV: External Port Value ID: External Port (0x0005) Length: 2 Value: 5190 TLV: Extended Data Value ID: Extended Data (0x2711) Length: 17 Value */ switch (tag) { case 0x000a: case 0x000f: case 0x0010: break; case 3: /* Internet IP */ { unsigned j; unsigned ip=0; for (j=0; j<4 && offset+j<length; j++) ip = ip << 8 | px[offset+j]; JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP",frame), JOT_SZ("AIM", "File-Transfer"), JOT_IPv4("Internal-IP", ip), 0); } break; case 5: /* Internal Port */ { unsigned j; unsigned port=0; for (j=0; j<2 && offset+j<length; j++) port = port << 8 | px[offset+j]; JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP",frame), JOT_SZ("AIM", "File-Transfer"), JOT_NUM("Internal-Port", port), 0); } break; case 0x2711: /* filename */ if (len > length-offset) len = length-offset; if (len > 4) { len -= 4; offset += 4; } while (offset < length && len && px[offset] < 26) { offset++; len--; } JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP",frame), JOT_SZ("AIM", "File-Transfer"), JOT_PRINT("Filename", px+offset, len), 0); break; default: /* TODO: SAMPLE this */ FRAMERR(frame, "%s: unknown\n", "AIM"); break; } offset += len; } }
void parse_PVSTP(struct Ferret *ferret, struct NetFrame *frame, const unsigned char *px, unsigned length) { unsigned port_id; if (length < 4) { FRAMERR(frame, "truncated\n"); return; } /* Protocol Identifier */ if (ex16be(px+0) != 0) { FRAMERR(frame, "unexpected\n"); return; } frame->layer3_protocol = LAYER3_STP; /* Protocol Version Identifier */ if (px[2] != 0) { FRAMERR(frame, "unexpected\n"); return; } /* BPDU type */ switch (px[3]) { case 0: if (length < 28) { FRAMERR(frame, "truncated\n"); return; } JOTDOWN(ferret, JOT_MACADDR("ID-MAC", frame->src_mac), JOT_SZ("Type", "bridge"), JOT_MACADDR("root", px+7), 0); JOTDOWN(ferret, JOT_MACADDR("ID-MAC", frame->src_mac), JOT_SZ("Type", "bridge"), JOT_MACADDR("ID", px+19), 0); port_id = ex16be(px+25); JOTDOWN(ferret, JOT_MACADDR("ID-MAC", frame->src_mac), JOT_SZ("Type", "bridge"), JOT_NUM("port-id", port_id), 0); break; case 0x80: JOTDOWN(ferret, JOT_MACADDR("ID-MAC", frame->src_mac), JOT_SZ("Type", "bridge"), 0); break; default: FRAMERR(frame, "unexpected\n"); return; } }
void parse_atalk_ddp(struct Ferret *ferret, struct NetFrame *frame, const unsigned char *px, unsigned length) { unsigned offset=0; struct { unsigned hop_count; unsigned datagram_length; unsigned checksum; unsigned protocol_type; unsigned address_src; unsigned address_dst; unsigned port_src; unsigned port_dst; } ddp; ferret->statistics.atalk++; if (length < 13) { FRAMERR(frame, "%s: truncated\n", "DDP"); return; } ddp.hop_count = px[0]>>4; ddp.datagram_length = (px[0]&0xF)<<8 | px[1]; ddp.checksum = ex16be(px+2); ddp.address_dst = ex16be(px+4)<<8; ddp.address_src = ex16be(px+6)<<8; ddp.address_dst |= px[8]; ddp.address_src |= px[9]; ddp.port_dst = px[10]; ddp.port_src = px[11]; ddp.protocol_type = px[12]; if (length > ddp.datagram_length) { if (length-ddp.datagram_length == 4) ferret->statistics.remaining_4++; /*hints that an FCS trails*/ length = ddp.datagram_length; } frame->ipver = ADDRESS_ATALK_EDDP; frame->src_ipv4 = ddp.address_src; frame->dst_ipv4 = ddp.address_dst; frame->src_port = ddp.port_src; frame->dst_port = ddp.port_dst; /* skip the header */ offset += 13; /* If this is a broadcast packet, we can make the assumption * that the sender is on the local subnet */ JOTDOWN(ferret, JOT_MACADDR("ID-MAC", frame->src_mac), JOT_SRC("AppleTalk", frame), 0); /* Parse the next layer */ SAMPLE(ferret, "ATALK-DDP",JOT_NUM("protocol", ddp.protocol_type)); SAMPLE(ferret, "ATALK-DDP",JOT_NUM("dst-port", ddp.port_dst)); switch (ddp.protocol_type) { case 0x02: /* NBP - Name Binding Protocol */ parse_atalk_nbp(ferret, frame, px+offset, length-offset); break; case 0x06: /* ZIP (Zone Information Protocol) */ break; case 0x01: /* RTMP (Routing Table Maintenance Protocol), works like RIP */ case 0x03: /* ATP (Appletalk Transfer Protocol) */ case 0x04: /* Echo, works like ICMP Echo */ case 0x05: /* RTMP requests */ case 0x07: /* ADSP (Appletalk Data Stream Protocol) */ case 0x08: /* SNMP, same as normal SNMP */ case 0x16: /* IP over AppleTalk */ default: FRAMERR(frame, "%s: unknown protocol=%d, srcport=%d, dstport=%d\n", "DDP", ddp.protocol_type, ddp.port_src, ddp.port_dst); } }
void squirrel_ethernet_frame(struct Squirrel *squirrel, struct NetFrame *frame, const unsigned char *px, unsigned length) { unsigned offset; unsigned ethertype; unsigned oui; if (length <= 14) { ; /*FRAMERR(frame, "wifi.data: too short\n");*/ return; } frame->src_mac = px+6; frame->dst_mac = px+0; offset = 12; /* Look for SAP header */ if (offset + 6 >= length) { FRAMERR(frame, "wifi.sap: too short\n"); return; } ethertype = ex16be(px+offset); offset += 2; switch (ethertype) { case 0x0800: squirrel_ip(squirrel, frame, px+offset, length-offset); break; case 0x0806: squirrel_arp(squirrel, frame, px+offset, length-offset); break; case 0x888e: /*802.11x authentication*/ //squirrel_802_1x_auth(squirrel, frame, px+offset, length-offset); break; case 0x86dd: /* IPv6*/ //squirrel_ipv6(squirrel, frame, px+offset, length-offset); break; case 0x809b: //squirrel_ipv6(squirrel, frame, px+offset, length-offset); break; case 0x872d: /* Cisco OWL */ break; case 0x9000: /* Loopback */ break; default: if (ethertype < 1518) { if (memcmp(px+offset, "\xaa\xaa\x03", 3) != 0) { return; } offset +=3 ; oui = ex24be(px+offset); if (squirrel->filter.snap_oui_count) { if (filter_has_port(squirrel->filter.snap_ouis, squirrel->filter.snap_oui_count, oui)) frame->flags.found.filtered = 1; } /* Look for OUI code */ switch (oui){ case 0x000000: /* fall through below */ break; case 0x004096: /* Cisco Wireless */ return; break; case 0x00000c: offset +=3; if (offset < length) { ;//squirrel_cisco00000c(squirrel, frame, px+offset, length-offset); } return; case 0x080007: break; /*apple*/ default: FRAMERR(frame, "Unknown SAP OUI: 0x%06x\n", oui); return; } offset +=3; /* EtherType */ if (offset+2 >= length) { FRAMERR(frame, "ethertype: packet too short\n"); return; } } if (ethertype == length-offset && ex16be(px+offset) == 0xAAAA) { ; } else FRAMERR_BADVAL(frame, "ethertype", ethertype); } }
void process_dhcp(struct Seaper *seap, struct NetFrame *frame, const unsigned char *px, unsigned length) { unsigned offset; struct DHCP dhcp; memset(&dhcp, 0, sizeof(dhcp)); if (length < 200) { FRAMERR(frame, "dhcp: frame too short\n"); return; } dhcp.op = px[0]; dhcp.hardware_type = px[1]; dhcp.hardware_address_length = px[2]; dhcp.hops = px[3]; dhcp.transaction_id = ex32be(px+4); dhcp.seconds_elapsed = ex16be(px+8); dhcp.flags = ex16be(px+10); dhcp.ciaddr = ex32be(px+12); dhcp.yiaddr = ex32be(px+16); dhcp.siaddr = ex32be(px+20); dhcp.giaddr = ex32be(px+24); memcpy(dhcp.chaddr, px+28, 16); dhcp.chaddr[16] = '\0'; memcpy(dhcp.sname, px+28+16, 64); dhcp.sname[64] = '\0'; memcpy(dhcp.file, px+28+16+64, 128); dhcp.file[128] = '\0'; offset = 28+16+64+128; if (offset+4 > length) return; if (memcmp(px+offset, "\x63\x82\x53\x63", 4) != 0) return; offset += 4; /* Process special options */ dhcp.msg = dhcp_number(px, length, 53); switch (dhcp.msg) { case 8: /* inform */ /* Process vendor specific information */ { const unsigned char *spec; unsigned spec_length; const unsigned char *id; unsigned id_length; dhcp_get_option(px, length, 43, &spec, &spec_length); dhcp_get_option(px, length, 60, &id, &id_length); if (spec_length && id_length) { process_record(seap, "application", REC_PRINTABLE, id, id_length, "info", REC_PRINTABLE, spec, spec_length, 0); } } } process_dhcp_options(seap, frame, px, length, offset, &dhcp); if (dhcp.overload_filename) process_dhcp_options(seap, frame, px, 28+16+64+128, 28+16+64, &dhcp); if (dhcp.overload_servername) process_dhcp_options(seap, frame, px, 28+16+64, 28+16, &dhcp); SAMPLE("BOOTP", "type", REC_UNSIGNED, &dhcp.op, sizeof(dhcp.op)); switch (dhcp.op) { case 1: /*BOOTP request */ break; case 2: /*BOOTP reply*/ switch (dhcp.msg) { case 2: break; case 5: /*ack*/ break; case 6: /*DHCP NACK*/ { const unsigned char *dst_mac; unsigned src_ip; if (dhcp.hardware_address_length != 6) { FRAMERR(frame, "dhcp: expected hardware address length = 6, found length = %d\n", dhcp.hardware_address_length); break; } if (memcmp(dhcp.chaddr, "\0\0\0\0\0\0", 6) == 0) { FRAMERR(frame, "dhcp: expected hardware address, but found [00:00:00:00:00:00]\n"); break; } else dst_mac = &dhcp.chaddr[0]; if (dhcp.server_identifier) src_ip = dhcp.server_identifier; else if (dhcp.siaddr) src_ip = dhcp.siaddr; else src_ip = frame->src_ipv4; process_record(seap, "proto", REC_SZ, "DHCP", -1, "op", REC_SZ, "NACK", -1, "src.ip", REC_IPv4, &src_ip, sizeof(src_ip), "dst.mac", REC_MACADDR, dst_mac, 6, 0); } break; case 8: break; default: FRAMERR(frame, "dhcp: unknown dhcp msg type %d\n", dhcp.msg); break; } break; default: FRAMERR(frame, "dhcp: unknown bootp op code %d\n", dhcp.op); break; } }
void process_tcp(struct Ferret *ferret, struct NetFrame *frame, const unsigned char *px, unsigned length) { struct { unsigned src_port; unsigned dst_port; unsigned seqno; unsigned ackno; unsigned header_length; unsigned flags; unsigned window; unsigned checksum; unsigned urgent; } tcp; ferret->statistics.tcp++; if (length == 0) { FRAMERR(frame, "tcp: frame empty\n"); return; } if (length < 20) { FRAMERR(frame, "tcp: frame too short\n"); return; } /* 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Source Port | Destination Port | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Sequence Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Acknowledgment Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Data | |U|A|P|R|S|F| | | Offset| Reserved |R|C|S|S|Y|I| Window | | | |G|K|H|T|N|N| | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Checksum | Urgent Pointer | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Options | Padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | data | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ tcp.src_port = ex16be(px+0); tcp.dst_port = ex16be(px+2); tcp.seqno = ex32be(px+4); tcp.ackno = ex32be(px+8); tcp.header_length = px[12]>>2; tcp.flags = px[13]; tcp.window = ex16be(px+14); tcp.checksum = ex16be(px+16); tcp.urgent = ex16be(px+18); frame->src_port = tcp.src_port; frame->dst_port = tcp.dst_port; if (tcp.header_length < 20) { /* Regress: defcon2008\dump027.pcap(39901) */ //FRAMERR(frame, "tcp: header too short, expected length=20, found length=%d\n", tcp.header_length); return; } if (tcp.header_length > length) { //FRAMERR(frame, "tcp: header too short, expected length=%d, found length=%d\n", tcp.header_length, length); return; } if ((tcp.flags & 0x20) && tcp.urgent > 0) { FRAMERR(frame, "tcp: found %d bytes of urgent data\n", tcp.urgent); return; } /* Check the checksum */ if (!validate_tcp_checksum(px, length, frame->src_ipv4, frame->dst_ipv4)) { /* Regress: defcon2008-msnmsgr.pcap(24066) */ ferret->statistics.errs_tcp_checksum++; return; } /*TODO: need to check checksum */ if (tcp.header_length > 20) { unsigned o = 20; unsigned max = tcp.header_length; while (o < tcp.header_length) { unsigned tag = px[o++]; unsigned len; if (tag == 0) break; if (tag == 1) continue; if (o >= max) { FRAMERR(frame, "tcp: options too long\n"); break; } len = px[o++]; if (len < 2) { FRAMERR(frame, "tcp: invalid length field\n"); break; } if (o+len-2 > max) { FRAMERR(frame, "tcp: options too long\n"); break; } switch (tag) { case 0x02: /* max seg size */ if (len != 4) FRAMERR(frame, "tcp: unknown length: option=%d, length=%d\n", tag, len); break; case 0x04: /* SACK permitted */ if (len != 2) FRAMERR(frame, "tcp: unknown length: option=%d, length=%d\n", tag, len); break; case 0x05: /* SACK */ break; case 0x08: /*timestamp*/ break; case 0x03: /*window scale*/ break; default: FRAMERR(frame, "tcp: unknown option=%d, length=%d\n", tag, len); } o += len-2; } } SAMPLE(ferret,"TCP", JOT_NUM("flags", tcp.flags)); /* Process an "acknowledgement". Among other things, this will identify * when packets have been missed: if the other side claims to have * received a packet, but we never saw it, then we know that it was * dropped somewhere on the network (probably because we are getting * a weak signal via wireless). */ if (tcp.flags & TCP_ACK) { tcp_ack_data(ferret, frame, tcp.ackno); } switch (tcp.flags & 0x3F) { case TCP_SYN: tcp_syn(ferret, frame); break; case TCP_SYN|TCP_ACK: tcp_synack(ferret, frame); break; case TCP_FIN: case TCP_FIN|TCP_ACK: case TCP_FIN|TCP_ACK|TCP_PSH: tcp_fin(ferret, frame); break; case TCP_ACK: case TCP_ACK|TCP_PSH: if (length > tcp.header_length) tcp_data(ferret, frame, px+tcp.header_length, length-tcp.header_length, tcp.seqno, tcp.ackno); break; case TCP_RST: case TCP_RST|TCP_ACK: break; case 0x40|TCP_ACK: break; case TCP_RST|TCP_ACK|TCP_FIN: case TCP_RST|TCP_ACK|TCP_PSH: break; default: FRAMERR(frame, "tcp: unexpected combo of flags: 0x%03x\n", tcp.flags); } }
/* * Cisco Discovery Protocol */ static void parse_CDP(struct Ferret *ferret, struct NetFrame *frame, const unsigned char *px, unsigned length) { unsigned offset=0; unsigned version; //unsigned ttl; //unsigned checksum; if (offset+4 > length) { FRAMERR(frame, "%s: truncated\n", "cisco"); return; } frame->layer3_protocol = LAYER3_MGMT; version = px[offset++]; //ttl = px[offset++]; //checksum = ex16be(px+2); offset += 2; SAMPLE(ferret,"Cisco Discovery Protocol", JOT_NUM("version",version)); while (offset < length) { unsigned tag; unsigned len; unsigned i; if (offset+4 > length) { FRAMERR(frame, "%s: truncated\n", "cisco"); return; } tag = ex16be(px+offset); len = ex16be(px+offset+2); offset += 4; if (len < 4) { FRAMERR(frame, "%s: bad value: 0x%x\n", "cdp", tag); return; } else len -= 4; if (len > length-offset) len = length-offset; SAMPLE(ferret,"Cisco Discovery Protocol", JOT_NUM("tag", tag)); switch (tag) { case 0x0000: return; case 0x0001: /* Device ID */ JOTDOWN(ferret, JOT_MACADDR("ID-MAC", frame->src_mac), JOT_PRINT("Cisco Device ID", px+offset,len), 0); break; case 0x0002: /* Addresses */ if (len < 4) { FRAMERR(frame, "%s: truncated\n", "cdp"); break; } i=0; { unsigned address_count = ex32be(px+offset); i += 4; while (address_count && i<len) { unsigned protocol_type; unsigned protocol_length; unsigned protocol = 0; unsigned address_length; if (i-len < 5) break; address_count--; protocol_type = px[offset+i++]; protocol_length = px[offset+i++]; if (protocol_length != 1) FRAMERR(frame, "%s: unknown value: 0x%x\n", "cdp", protocol_length); while (protocol_length && i<len) { protocol <<= 8; protocol |= px[offset+i++]; protocol_length--; } address_length = ex16be(px+offset+i); i+= 2; switch (protocol_type) { case 1: switch (protocol) { case 0xCC: /*IPv4 address */ if (address_length != 4) FRAMERR(frame, "%s: unknown value: 0x%x\n", "cdp", address_length); else if (len-i < 4) FRAMERR(frame, "%s: truncated\n", "cdp"); else { unsigned ip = ex32be(px+offset+i); JOTDOWN(ferret, JOT_MACADDR("ID-MAC", frame->src_mac), JOT_IPv4("ip", ip), 0); JOTDOWN(ferret, JOT_IPv4("ID-IP", ip), JOT_MACADDR("mac", frame->src_mac), 0); } break; default: SAMPLE(ferret,"CDP", JOT_NUM("ip-protocol-type", protocol)); FRAMERR(frame, "%s: unknown value: 0x%x\n", "cdp", protocol); } break; default: SAMPLE(ferret,"CDP", JOT_NUM("address-protocol-type", protocol_type)); FRAMERR(frame, "%s: unknown value: 0x%x\n", "cdp", protocol_type); break; } } } break; case 0x0003: /* Port ID*/ JOTDOWN(ferret, JOT_MACADDR("ID-MAC", frame->src_mac), JOT_PRINT("Cisco Port ID", px+offset,len), 0); break; case 0x0004: { unsigned n = 0; for (i=0; i<len; i++) { n <<= 8; n |= px[offset + i]; } if (n & 0x00000001) JOTDOWN(ferret, JOT_MACADDR("ID-MAC", frame->src_mac), JOT_SZ("Capabilities", "router"), 0); if (n & 0x00000002) JOTDOWN(ferret, JOT_MACADDR("ID-MAC", frame->src_mac), JOT_SZ("Capabilities", "bridge"), 0); if (n & 0x00000004) JOTDOWN(ferret, JOT_MACADDR("ID-MAC", frame->src_mac), JOT_SZ("Capabilities", "source route bridge"), 0); if (n & 0x00000008) JOTDOWN(ferret, JOT_MACADDR("ID-MAC", frame->src_mac), JOT_SZ("Capabilities", "switch"), 0); if (n & 0x00000010) JOTDOWN(ferret, JOT_MACADDR("ID-MAC", frame->src_mac), JOT_SZ("Capabilities", "host"), 0); if (n & 0x00000020) JOTDOWN(ferret, JOT_MACADDR("ID-MAC", frame->src_mac), JOT_SZ("Capabilities", "IGMP"), 0); if (n & 0x00000040) JOTDOWN(ferret, JOT_MACADDR("ID-MAC", frame->src_mac), JOT_SZ("Capabilities", "repeater"), 0); } break; case 0x0005: /* IOS Version */ for (i=0; i<len; i++) if (!isspace(px[offset+i])) break; JOTDOWN(ferret, JOT_MACADDR("ID-MAC", frame->src_mac), JOT_PRINT("IOS Version", px+offset+i,len-i), 0); break; case 0x0006: /* Platform*/ JOTDOWN(ferret, JOT_MACADDR("ID-MAC", frame->src_mac), JOT_PRINT("Cisco Platform", px+offset,len), 0); break; case 0x0008: /* Hello: cluster mgmt */ break; case 0x0009: /* VTP mgmnt domain */ JOTDOWN(ferret, JOT_MACADDR("ID-MAC", frame->src_mac), JOT_PRINT("VTP Mgmt Domain", px+offset,len), 0); break; case 0x000a: /* Native VLAN */ break; case 0x000b: /* Duplex */ break; case 0x0012: /* Trust Bitmap */ break; case 0x0013: /* Untrusted Port CoS */ break; case 0x0016: /* Management Addresses */ /* TODO: decode the management addresses */ break; default: FRAMERR(frame, "%s: unknown value: 0x%x\n", "cdp", tag); } offset += len; } }
void process_udp(struct Seaper *seap, struct NetFrame *frame, const unsigned char *px, unsigned length) { unsigned offset=0; struct { unsigned src_port; unsigned dst_port; unsigned length; unsigned checksum; } udp; if (length == 0) { FRAMERR(frame, "udp: frame empty\n"); return; } if (length < 8) { FRAMERR(frame, "udp: frame too short\n"); return; } udp.src_port = ex16be(px+0); udp.dst_port = ex16be(px+2); udp.length = ex16be(px+4); udp.checksum = ex16be(px+6); frame->src_port = udp.src_port; frame->dst_port = udp.dst_port; if (udp.length < 8) { FRAMERR_TRUNCATED(frame, "udp"); return; } if (length > udp.length) length = udp.length; offset += 8; switch (frame->dst_ipv4) { case 0xe0000123: /* 224.0.1.35 - SLP */ if (udp.dst_port == 427) SAMPLE("SLP", "packet", REC_SZ, "test",-1); else FRAMERR(frame, "unknown port %d\n", udp.dst_port); return; } SAMPLE("UDP", "src", REC_UNSIGNED, &udp.src_port, sizeof(udp.src_port)); SAMPLE("UDP", "dst", REC_UNSIGNED, &udp.dst_port, sizeof(udp.dst_port)); switch (udp.src_port) { case 68: case 67: process_dhcp(seap, frame, px+offset, length-offset); break; case 53: process_dns(seap, frame, px+offset, length-offset); break; case 137: process_dns(seap, frame, px+offset, length-offset); break; case 138: process_netbios_dgm(seap, frame, px+offset, length-offset); break; case 389: process_ldap(seap, frame, px+offset, length-offset); break; case 631: if (udp.dst_port == 631) { process_cups(seap, frame, px+offset, length-offset); } break; case 1900: if (length-offset > 9 && memicmp(px+offset, "HTTP/1.1 ", 9) == 0) { process_upnp_response(seap, frame, px+offset, length-offset); } break; case 14906: /* ??? */ break; case 4500: break; default: switch (udp.dst_port) { case 0: break; case 68: case 67: process_dhcp(seap, frame, px+offset, length-offset); break; case 53: case 5353: process_dns(seap, frame, px+offset, length-offset); break; case 137: process_dns(seap, frame, px+offset, length-offset); break; case 138: process_netbios_dgm(seap, frame, px+offset, length-offset); break; case 1900: if (frame->dst_ipv4 == 0xeffffffa) process_ssdp(seap, frame, px+offset, length-offset); break; case 5369: break; case 29301: break; case 123: break; case 5499: break; case 2233: /*intel/shiva vpn*/ break; case 27900: /* GameSpy*/ break; case 9283: process_callwave_iam(seap, frame, px+offset, length-offset); break; case 161: process_snmp(seap, frame, px+offset, length-offset); break; case 192: /* ??? */ break; case 389: process_ldap(seap, frame, px+offset, length-offset); break; case 427: /* SRVLOC */ process_srvloc(seap, frame, px+offset, length-offset); break; case 14906: /* ??? */ break; case 500: process_isakmp(seap, frame, px+offset, length-offset); break; case 2222: break; default: if (frame->dst_ipv4 == 0xc0a8a89b || frame->src_ipv4 == 0xc0a8a89b) ; else FRAMERR(frame, "udp: unknown, [%d.%d.%d.%d]->[%d.%d.%d.%d] src=%d, dst=%d\n", (frame->src_ipv4>>24)&0xFF,(frame->src_ipv4>>16)&0xFF,(frame->src_ipv4>>8)&0xFF,(frame->src_ipv4>>0)&0xFF, (frame->dst_ipv4>>24)&0xFF,(frame->dst_ipv4>>16)&0xFF,(frame->dst_ipv4>>8)&0xFF,(frame->dst_ipv4>>0)&0xFF, frame->src_port, frame->dst_port); } } }