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); }
/** * This is the main function of the DNS parser. * * This is where the each DNS 'answer' (or 'additional' or 'authoritative') * record is parsed. Mostly, we ignore the return code, though some functions * pay attention to and provide slightly different information depending * upon the opcode. */ static void dns_parse_resource_record(struct Ferret *ferret, struct NetFrame *frame, const unsigned char *px, unsigned length, struct DNSRECORD *rec, struct DNS *dns) { char name[512]; /* reserve a longer name than the max theoretical limit */ unsigned name_length; char name2[512]; /* reserve a longer name than the max theoretical limit */ unsigned name2_length; unsigned ip_address; unsigned offset = rec->rdata_offset; unsigned offset_max = MIN(rec->rdata_offset+rec->rdata_length, length); /* MULTICAST DNS (mDNS): handle the multicast DNS records differently * from normal DNS records. */ if (!dns->is_response && frame->dst_port == 5353) { bonjour_parse_resource_record(ferret, frame, px, length, rec, dns); return; } else if (dns->is_response && (frame->src_port == 5353 || (frame->dst_port == 5353 && frame->src_port != 53))) { bonjour_parse_resource_record(ferret, frame, px, length, rec, dns); return; } /* NETBIOS: handle NetBIOS records differently from normal DNS records */ if (!dns->is_response && frame->dst_port == 137) { netbios_parse_resource_record(ferret, frame, px, length, rec, dns); return; } else if (dns->is_response && frame->src_port == 137) { netbios_parse_resource_record(ferret, frame, px, length, rec, dns); return; } /* First, let's extract a pretty version of the name */ name_length = dns_extract_name(frame, px, length, rec->name_offset, name, sizeof(name)); if (rec->type == 0x8001) FRAMERR(frame, "TODO\n"); if (rec->clss == 0xfe) return; /* RFC2671 - Extension Mechanisms for DNS (EDNS0) */ if (rec->type == 41) { /* Regress: defcon2008/dump000.pca(12541) */ /* TODO: parse this */ return; } /* Haven't implemented dynamic update yet * TODO: */ if (dns->opcode == 21 || dns->opcode == 5) return; switch (rec->type<<16 | rec->clss) { case TYPECLASS(1,0x8001): /* type=A(IPv4 address), class=INTERNET(cache flush) */ bonjour_parse_resource_record(ferret, frame, px, length, rec, dns); break; case TYPECLASS(1,1): /* type=A(IPv4 address), class=INTERNET */ if (!is_valid_opcode(dns->opcode, 0x10, 5, -1)) { FRAMERR(frame, "%s: unknown opcode=%d\n", "DNS", dns->opcode); return; } ip_address = ex32be(px+rec->rdata_offset); if (rec->rdata_length != 4) 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); JOTDOWN(ferret, JOT_PRINT("ID-DNS", name, name_length), JOT_IPv4("address", ip_address), 0); break; case TYPECLASS(2,1): /* type=NS, class=INTERNET */ if (!is_valid_opcode(dns->opcode, 0x10, -1)) { FRAMERR(frame, "%s: unknown opcode=%d\n", "DNS", dns->opcode); return; } name2_length = dns_extract_name(frame, px, length, rec->rdata_offset, name2, sizeof(name2)); ip_address = dns_resolve_alias(frame, px, length, dns, name2, 0); JOTDOWN(ferret, JOT_PRINT("ID-DNS", name, name_length), JOT_PRINT("Name-Server", name2, name2_length), JOT_IPv4("address", ip_address), 0); break; case TYPECLASS(5,1): /*type=CNAME(aliased canonical name), class=INTERNET */ if (!is_valid_opcode(dns->opcode, 0x10, 5, -1)) { FRAMERR(frame, "%s: unknown opcode=%d\n", "DNS", dns->opcode); return; } name2_length = dns_extract_name(frame, px, length, rec->rdata_offset, name2, sizeof(name2)); ip_address = dns_resolve_alias(frame, px, length, dns, name2, 0); if (ip_address != 0) { JOTDOWN(ferret, JOT_PRINT("ID-DNS", name, name_length), JOT_IPv4("alias",ip_address), 0); } JOTDOWN(ferret, JOT_PRINT("ID-DNS", name, name_length), JOT_PRINT("alias", name2, name2_length), 0); break; case TYPECLASS(6,1): /*type=SOA, class=INTERNET*/ if (!is_valid_opcode(dns->opcode, 0x10, -1)) { FRAMERR(frame, "%s: unknown opcode=%d\n", "DNS", dns->opcode); return; } /* * Authoritative Name Server */ name2_length = dns_extract_name(frame, px, length, offset, name2, sizeof(name2)); JOTDOWN(ferret, JOT_PRINT("ID-DNS", name, name_length), JOT_SZ("SOA", "Start of zone authority"), JOT_PRINT("Name-Server", name2, name2_length), 0); ip_address = dns_resolve_alias(frame, px, length, dns, name2, 0); if (ip_address) JOTDOWN(ferret, JOT_PRINT("ID-DNS", name, name_length), JOT_SZ("SOA", "Start of zone authority"), JOT_PRINT("Name-Server", name2, name2_length), JOT_IPv4("address", ip_address), 0); skip_name(px, length, &offset); /* Contact */ if (offset < offset_max) { name2_length = dns_extract_name(frame, px, length, offset, name2, sizeof(name2)); JOTDOWN(ferret, JOT_PRINT("ID-DNS", name, name_length), JOT_SZ("SOA", "Start of zone authority"), JOT_PRINT("Contact", name2, name2_length), 0); skip_name(px, length, &offset); } break; case TYPECLASS(10,1): /* type=NULL, class=INTERNET*/ /* Regress: defcon2008-dns2.pcap(100803): name=Vaaaaiaqaac.tunnel.fastcoder.net */ /* I'm not sure what this is, other than passing data as Null records. * This would be a good thing for an intrusion-detection system to trigger * on. */ break; case TYPECLASS(12,0x8001): /*type=PTR, class=INTERNET */ bonjour_parse_resource_record(ferret, frame, px, length, rec, dns); break; case TYPECLASS(12,1): /*type=PTR(pointer reverse lookup), class=INTERNET */ if (!is_valid_opcode(dns->opcode, 0x10, -1)) { FRAMERR(frame, "%s: unknown opcode=%d\n", "DNS", dns->opcode); return; } if (name_length > 6 && memcmp(name+name_length-6, ".local", 6) == 0) { JOTDOWN(ferret, JOT_SRC("ID-IP", frame), JOT_PRINT("Service", name,name_length), 0); /* Extract MAC address */ { const unsigned char *p_name; unsigned name_length; const unsigned char *p_mac = find_mac(px, MIN(length, rec->rdata_offset+rec->rdata_length), rec->rdata_offset, &p_name, &name_length); if (p_mac) { JOTDOWN(ferret, JOT_SRC("ID-IP", frame), JOT_PRINT("mac", p_mac, 19), 0); JOTDOWN(ferret, JOT_SRC("ID-IP", frame), JOT_PRINT("name", p_name, name_length), 0); } } } else if (endsWith(name, ".in-addr.arpa")) { /* Extract a 4-byte IPv4 address * Example: "18.0.0.10.in-addr.arpa"*/ unsigned ipv4=0; unsigned i; unsigned j=0; for (i=0; i<4; i++) { unsigned num = 0; for (; name[j] && name[j] != '.'; j++) { if ('0' <= name[j] && name[j] <= '9') num = num * 10 + name[j]-'0'; } while (name[j] == '.') j++; ipv4 |= num<<(i*8); } /* Now get the name it points to */ name2_length = dns_extract_name(frame, px, length, offset, name2, sizeof(name2)); JOTDOWN(ferret, JOT_PRINT("ID-DNS", name2, name2_length), JOT_IPv4("ID-IP", ipv4), JOT_SRC("dnssrv", frame), 0); } else ; //FRAMERR(frame, "dns: unknown PTR record\n"); break; case TYPECLASS(13,0x8001): /*type=HINFO, class=INTERNET */ bonjour_parse_resource_record(ferret, frame, px, length, rec, dns); break; case TYPECLASS(15,1): /*type=MX, class=INTERNET */ /* Regress: defcon2008-dns2.pcap(18661) */ break; case TYPECLASS(16,0x8001): /*type=TXT, class=INTERNET(cache flush)*/ bonjour_parse_resource_record(ferret, frame, px, length, rec, dns); break; case TYPECLASS(16,1): /*type=TXT, class=INTERNET */ if (!is_valid_opcode(dns->opcode, 0x10, 5, -1)) { FRAMERR(frame, "%s: unknown opcode=%d\n", "DNS", dns->opcode); return; } if (stricmp(name, "current.cvd.clamav.net") == 0) { /* This is a single string containing a version string, like: * 0.91.1:44:3855:1186270141:1 */ break; } else if (starts_with("_DM-NOTIFICATION.", name, strlen(name))) { /* Regress: defcon2008\dump001.pcap(87082) */ /* TODO */ break; } else if (endsWith(name, "._workstation._tcp.local")) { /* Regress: defcon2008-dns2.pcap(56127): "mike-desktop [00:0c:29:f6:58:ca]._workstation._tcp.local" */ break; } else if (endsWith(name, ".asn.cymru.com")) { /* Regress: defcon2008-dns2.pcap(98958) */ /* This is a system for mapping IP to ASN numbers: * http://www.team-cymru.org/Services/ip-to-asn.html */ break; } else if (endsWith(name, ".wrs.trendmicro.com")) { /* Regress: defcon2008-dns2.pcap(184904) */ /* Appears to check whether IP addresses are trustworthy */ break; } else { FRAMERR(frame, "%s: unknown TXT record %s", "DNS", name); } break; case TYPECLASS(0x1c,1): /*type=AAAA(IPv6 address), class=INTERNET*/ case TYPECLASS(0x1c,255): /*type=AAAA(IPv6 address), class=INTERNET*/ if (!is_valid_opcode(dns->opcode, 0x10, 5, -1)) { FRAMERR(frame, "%s: unknown opcode=%d\n", "DNS", dns->opcode); return; } switch (dns->opcode) { case 0x10: { const unsigned char *ipv6_address = px+rec->rdata_offset; if (rec->rdata_length != 16) FRAMERR(frame, "dns: data not 16-bytes long, was %d-bytes instead (class=%d, type=%d, name=%s)\n", rec->rdata_length, rec->clss, rec->type, name); JOTDOWN(ferret, JOT_SZ("proto","DNS"), JOT_SZ("op","lookup"), JOT_SRC("ip.src", frame), JOT_PRINT("name", name, name_length), JOT_IPv6("address", ipv6_address, 16), 0); } case 5: /* dynamic update*/ /* Regress: defcon2008-dns2.pcap(7958) */ break; default: FRAMERR(frame, "%s: unknown opcode=%d\n", "DNS", dns->opcode); } break; case TYPECLASS(33,1): /*type=SRV, class=INTERNET */ if (!is_valid_opcode(dns->opcode, 0x10, -1)) { FRAMERR(frame, "%s: unknown opcode=%d\n", "DNS", dns->opcode); return; } if (rec->rdata_length < 7) FRAMERR(frame, "dns: unknown type=%d (class=%d, name=%s)\n", rec->type, rec->clss, name); else { unsigned port = px[rec->rdata_offset+4]<<8 | px[rec->rdata_offset+5]; name2_length = dns_extract_name(frame, px, length, rec->rdata_offset+6, name2, sizeof(name2)); ip_address = dns_resolve_alias(frame, px, length, dns, name2, 0); if (ip_address != 0) { JOTDOWN(ferret, JOT_PRINT("ID-DNS", name, name_length), JOT_PRINT("Server", name2, name2_length), JOT_NUM("Port", port), JOT_IPv4("IPv4",ip_address), 0); } else JOTDOWN(ferret, JOT_PRINT("ID-DNS", name, name_length), JOT_PRINT("Server", name2, name2_length), JOT_NUM("Port", port), 0); } break; default: FRAMERR(frame, "dns: unknown type=%d (class=%d, name=%s)\n", rec->type, rec->clss, name); } }