static int dnsReplyId(char *buf, int offset, int n, int *id_return) { if(n - offset < 12) return -1; DO_NTOHS(*id_return, &buf[offset]); return 1; }
static int dnsDecodeReply(char *buf, int offset, int n, int *id_return, AtomPtr *name_return, AtomPtr *value_return, int *af_return, unsigned *ttl_return) { int i = offset, j, m; int id = -1, b23, qdcount, ancount, nscount, arcount, rdlength; int class, type; unsigned int ttl; char b[2048]; int af = -1; AtomPtr name = NULL, value; char addresses[1024]; int addr_index = 0; int error = EDNS_NO_ADDRESS; unsigned final_ttl = 7 * 24 * 3600; int dnserror; if(n - i < 12) { error = EDNS_INVALID; goto fail; } DO_NTOHS(id, &buf[i]); i += 2; DO_NTOHS(b23, &buf[i]); i += 2; DO_NTOHS(qdcount, &buf[i]); i += 2; DO_NTOHS(ancount, &buf[i]); i += 2; DO_NTOHS(nscount, &buf[i]); i += 2; DO_NTOHS(arcount, &buf[i]); i += 2; do_log(D_DNS, "DNS id %d, b23 0x%x, qdcount %d, ancount %d, " "nscount %d, arcount %d\n", id, b23, qdcount, ancount, nscount, arcount); if((b23 & (0xF870)) != 0x8000) { do_log(L_ERROR, "Incorrect DNS reply (b23 = 0x%x).\n", b23); error = EDNS_INVALID; goto fail; } dnserror = b23 & 0xF; if(b23 & 0x200) { do_log(L_WARN, "Truncated DNS reply (b23 = 0x%x).\n", b23); } if(dnserror || qdcount != 1) { if(!dnserror) do_log(L_ERROR, "Unexpected number %d of DNS questions.\n", qdcount); if(dnserror == 1) error = EDNS_FORMAT; else if(dnserror == 2) error = EDNS_NO_RECOVERY; else if(dnserror == 3) error = EDNS_HOST_NOT_FOUND; else if(dnserror == 4 || dnserror == 5) error = EDNS_REFUSED; else if(dnserror == 0) error = EDNS_INVALID; else error = EUNKNOWN; goto fail; } /* We do this early, so that we can return the address family to the caller in case of error. */ i = labelsToString(buf, i, n, b, 2048, &m); if(i < 0) { error = EDNS_FORMAT; goto fail; } DO_NTOHS(type, &buf[i]); i += 2; DO_NTOHS(class, &buf[i]); i += 2; if(type == 1) af = 4; else if(type == 28) af = 6; else { error = EDNS_FORMAT; goto fail; } do_log(D_DNS, "DNS q: "); do_log_n(D_DNS, b, m); do_log(D_DNS, " (%d, %d)\n", type, class); name = internAtomLowerN(b, m); if(name == NULL) { error = ENOMEM; goto fail; } if(class != 1) { error = EDNS_FORMAT; goto fail; } #define PARSE_ANSWER(kind, label) \ do { \ i = labelsToString(buf, i, 1024, b, 2048, &m); \ if(i < 0) goto label; \ DO_NTOHS(type, &buf[i]); i += 2; if(i > 1024) goto label; \ DO_NTOHS(class, &buf[i]); i += 2; if(i > 1024) goto label; \ DO_NTOHL(ttl, &buf[i]); i += 4; if(i > 1024) goto label; \ DO_NTOHS(rdlength, &buf[i]); i += 2; if(i > 1024) goto label; \ do_log(D_DNS, "DNS " kind ": "); \ do_log_n(D_DNS, b, m); \ do_log(D_DNS, " (%d, %d): %d bytes, ttl %u\n", \ type, class, rdlength, ttl); \ } while(0) for(j = 0; j < ancount; j++) { PARSE_ANSWER("an", fail); if(strcasecmp_n(name->string, b, m) == 0) { if(class != 1) { do_log(D_DNS, "DNS: %s: unknown class %d.\n", name->string, class); error = EDNS_UNSUPPORTED; goto cont; } if(type == 1 || type == 28) { if((type == 1 && rdlength != 4) || (type == 28 && rdlength != 16)) { do_log(L_ERROR, "DNS: %s: unexpected length %d of %s record.\n", scrub(name->string), rdlength, type == 1 ? "A" : "AAAA"); error = EDNS_INVALID; if(rdlength <= 0 || rdlength >= 32) goto fail; goto cont; } if(af == 0) { do_log(L_WARN, "DNS: %s: host has both A and CNAME -- " "ignoring CNAME.\n", scrub(name->string)); addr_index = 0; af = -1; } if(type == 1) { if(af < 0) af = 4; else if(af == 6) { do_log(L_WARN, "Unexpected AAAA reply.\n"); goto cont; } } else { if(af < 0) af = 6; else if(af == 4) { do_log(L_WARN, "Unexpected A reply.\n"); goto cont; } } if(addr_index == 0) { addresses[0] = DNS_A; addr_index++; } else { if(addr_index > 1000) { error = EDNS_INVALID; goto fail; } } assert(addresses[0] == DNS_A); if(final_ttl > ttl) final_ttl = ttl; memset(&addresses[addr_index], 0, sizeof(HostAddressRec)); if(type == 1) { addresses[addr_index] = 4; memcpy(addresses + addr_index + 1, buf + i, 4); } else { addresses[addr_index] = 6; memcpy(addresses + addr_index + 1, buf + i, 16); } addr_index += sizeof(HostAddressRec); } else if(type == 5) { int j, k; if(af != 0 && addr_index > 0) { do_log(L_WARN, "DNS: host has both CNAME and A -- " "ignoring CNAME.\n"); goto cont; } af = 0; if(addr_index != 0) { /* Only warn if the CNAMEs are not identical */ char tmp[512]; int jj, kk; assert(addresses[0] == DNS_CNAME); jj = labelsToString(buf, i, n, tmp, 512, &kk); if(jj < 0 || kk != strlen(addresses + 1) || memcmp(addresses + 1, tmp, kk) != 0) { do_log(L_WARN, "DNS: " "%s: host has multiple CNAMEs -- " "ignoring subsequent.\n", scrub(name->string)); } goto cont; } addresses[0] = DNS_CNAME; addr_index++; j = labelsToString(buf, i, n, addresses + 1, 1020, &k); if(j < 0) { addr_index = 0; error = ENAMETOOLONG; continue; } addr_index = k + 1; } else { error = EDNS_NO_ADDRESS; i += rdlength; continue; } } cont: i += rdlength; } #if (LOGGING_MAX & D_DNS) for(j = 0; j < nscount; j++) { PARSE_ANSWER("ns", nofail); i += rdlength; } for(j = 0; j < arcount; j++) { PARSE_ANSWER("ar", nofail); i += rdlength; } nofail: #endif #undef PARSE_ANSWER do_log(D_DNS, "DNS: %d bytes\n", addr_index); if(af < 0) goto fail; value = internAtomN(addresses, addr_index); if(value == NULL) { error = ENOMEM; goto fail; } assert(af >= 0); *id_return = id; *name_return = name; *value_return = value; *af_return = af; *ttl_return = final_ttl; return 1; fail: *id_return = id; *name_return = name; *value_return = NULL; *af_return = af; return -error; }
int parse_dhcpv6(const unsigned char *buf, int buflen, struct prefix_list **dns, struct prefix_list **ntp) { int i = 0; while(i < buflen) { unsigned const char *tlv = buf + i; int type, bodylen; if(buflen - i < 4) { fprintf(stderr, "Received truncated TLV.\n"); goto fail; } DO_NTOHS(type, tlv); DO_NTOHS(bodylen, tlv + 2); if(buflen - i < 4 + bodylen) { fprintf(stderr, "Received truncated TLV.\n"); goto fail; } switch(type) { case 23: case 31: { int j; struct in6_addr addr; struct prefix_list *pl; debugf(type == 23 ? " OPTION_DNS_SERVERS " : " OPTION_SNTP_SERVERS "); for(j = 0; j < bodylen / 16; j++) { memcpy(&addr, tlv + i + 2 + j * 16, 16); debug_address(&addr); debugf(" "); if(type == 23) { pl = prefix_list_cons(*dns, &addr, 128, NULL, 0, 0); if(pl != NULL) *dns = pl; } else { pl = prefix_list_cons(*ntp, &addr, 128, NULL, 0, 0); if(pl != NULL) *ntp = pl; } } debugf("\n"); break; } default: if(debug_level >= 3) debugf(" %d: %d\n", type, bodylen); break; } i += 4 + bodylen; i += -i & 3; } return 1; fail: return -1; }
struct external * parse_external(struct node *node, const unsigned char *buf, int buflen) { int i = 0; struct external *ext = calloc(1, sizeof(struct external)); if(ext == NULL) return NULL; while(i < buflen) { unsigned const char *tlv = buf + i; int type, bodylen; if(buflen - i < 4) { fprintf(stderr, "Received truncated TLV.\n"); goto fail; } DO_NTOHS(type, tlv); DO_NTOHS(bodylen, tlv + 2); if(buflen - i < 4 + bodylen) { fprintf(stderr, "Received truncated TLV.\n"); goto fail; } switch(type) { case 34: { unsigned char plen; struct in6_addr addr; struct prefix_list *pl; if(bodylen < 9) { fprintf(stderr, "Truncated DELEGATED-PREFIX.\n"); break; } plen = *(tlv + 12); if(bodylen < 9 + (plen + 7) / 8) { fprintf(stderr, "Truncated DELEGATED-PREFIX.\n"); break; } parse_pref(&addr, tlv + 13, plen); debugf(" DELEGATED-PREFIX "); debug_prefix_raw(&addr, plen); debugf("\n"); /* XXX parse embedded TLVs. */ pl = prefix_list_cons(ext->delegated, &addr, plen, node->id, 0, 0); if(pl != NULL) ext->delegated = pl; break; } case 37: { debugf(" DHCPV4-DATA\n"); parse_dhcpv4(tlv + 4, bodylen, &ext->dns, &ext->ntp); data_change_time = now; break; } case 38: { debugf(" DHCPV6-DATA\n"); parse_dhcpv6(tlv + 4, bodylen, &ext->dns, &ext->ntp); data_change_time = now; break; } default: if(debug_level >= 3) debugf(" %d: %d\n", type, bodylen); break; } i += 4 + bodylen; i += -i & 3; } return ext; fail: destroy_external(ext); return NULL; }
int parse_packet(const unsigned char *buf, int buflen, struct sockaddr_in6 *from, int unicast, struct interface *interface) { unsigned char id[4]; unsigned int eid = 42; /* silence gcc */ int have_id = 0; int i = 0, rc; int recompute = 0; if(debug_level >= 3) debugf("Received %d bytes.\n", buflen); while(i < buflen) { unsigned const char *tlv = buf + i; int type, bodylen; if(buflen - i < 4) { fprintf(stderr, "Received truncated TLV.\n"); goto fail; } DO_NTOHS(type, tlv); DO_NTOHS(bodylen, tlv + 2); if(buflen - i < 4 + bodylen) { fprintf(stderr, "Received truncated TLV.\n"); goto fail; } switch(type) { case 1: { debugf(" REQ-NETWORK-STATE\n"); int i; buffer_network_state(from, NULL); for(i = 0; i < numnodes; i++) buffer_node_state(&nodes[i], 0, from, NULL); break; } case 2: { struct node *node; if(bodylen < 4) { fprintf(stderr, "Truncated REQ-NODE-STATE\n"); goto fail; } debugf(" REQ-NODE-STATE %s\n", format_32(tlv + 4)); node = find_node(tlv + 4, 0); if(node) buffer_node_state(node, 1, from, NULL); break; } case 3: { struct neighbour *neigh; if(bodylen < 8) { fprintf(stderr, "Truncated NODE-ENDPOINT.\n"); break; } if(have_id) { fprintf(stderr, "Duplicate NODE-ENDPOINT.\n"); break; } memcpy(id, tlv + 4, 4); DO_NTOHL(eid, tlv + 8); debugf(" NODE-ENDPOINT %s %u\n", format_32(id), eid); if(id_eq(id, myid)) { fprintf(stderr, "Node id collision.\n"); goto fail; } have_id = 1; if(unicast) { neigh = find_neighbour(interface, id, eid, from); if(neigh && unicast) neigh->last_contact = now; } break; } case 4: { int rc; unsigned char h[8]; struct neighbour *neigh = NULL; struct timespec t; if(have_id) neigh = find_neighbour(interface, id, eid, NULL); if(bodylen < 8) { fprintf(stderr, "Truncated NETWORK-STATE.\n"); goto fail; } rc = network_hash(h); if(rc < 0) { fprintf(stderr, "Eek!\n"); goto fail; } if(memcmp(h, tlv + 4, 8) == 0) { debugf(" NETWORK-STATE %s (consistent)\n", format_64(tlv + 4)); if(neigh) neigh->last_contact = now; trickle_reset(&interface->trickle, 1); break; } debugf(" NETWORK-STATE %s (inconsistent, %s)\n", format_64(tlv + 4), format_64(h)); /* But don't reset Trickle. */ ts_add_msec(&t, &interface->last_request_sent, HNCP_I_min); if(ts_compare(&now, &t) >= 0) { interface->last_request_sent = now; debugf("-> REQ-NETWORK-STATE\n"); buffer_tlv(1, NULL, 0, from, NULL); } break; } case 5: { struct node *node; unsigned int seqno; int msecs, mine; int datalen = bodylen - 20; if(bodylen < 20) { fprintf(stderr, "Truncated NODE-STATE.\n"); goto fail; } if(!have_id) { fprintf(stderr, "NODE-STATE with no NODE-ENDPOINT.\n"); } DO_NTOHL(seqno, tlv + 8); DO_NTOHL(msecs, tlv + 12); debugf(" NODE-STATE %s %d %d", format_32(tlv + 4), seqno, msecs); mine = id_eq(tlv + 4, myid); if(mine) debugf(" (mine)"); node = find_node(tlv + 4, 0); if(node && (seqno - node->seqno) & 0x80000000) { debugf(" (older)\n"); } else if(!node || seqno != node->seqno || memcmp(tlv + 16, node->datahash, 8) != 0) { debugf(" (newer%s, %s)\n", datalen ? ", data" : "", format_64(tlv + 16)); if(mine) { fprintf(stderr, "Duplicate node identifier -- reclaiming.\n"); node->seqno = seqno + 42; break; } if(datalen) { unsigned char *new_data; unsigned char h[8]; int rc; node_hash(h, tlv + 24, datalen); if(memcmp(h, tlv + 16, 8) != 0) { fprintf(stderr, "Corrupt hash.\n"); goto fail; } if(!node) node = find_node(tlv + 4, 1); if(!node) { fprintf(stderr, "Couldn't create node.\n"); goto fail; } new_data = realloc(node->data, datalen); if(new_data == NULL) { fprintf(stderr, "Eek!\n"); goto fail; } node->interface = interface; node->seqno = seqno; ts_add_msec(&node->orig_time, &now, msecs + 1); node->data = new_data; memcpy(node->data, tlv + 24, datalen); node->datalen = datalen; memcpy(node->datahash, tlv + 16, 8); rc = parse_node_state(node); if(rc < 0) fprintf(stderr, "Couldn't parse node state.\n"); else trickle_reset_all(); recompute = 1; } else { struct neighbour *neigh = NULL; if(have_id) neigh = find_neighbour(interface, id, eid, from); if(neigh) { debugf("-> REQ-NODE-STATE %s\n", format_32(tlv + 4)); buffer_tlv(2, tlv + 4, 4, from, NULL); } else fprintf(stderr, "No neighbour to send request to.\n"); } } else { debugf(" (consistent)\n"); } break; } default: if(debug_level >= 3) debugf(" %d: %d\n", type, bodylen); break; break; } i += 4 + bodylen; i += -i & 3; } rc = 1; goto done; fail: rc = -1; done: if(recompute) { int r; silly_walk(find_node(myid, 0)); r = prefix_assignment(1); if(r > 0) republish(0, 1); } return rc; }
int parse_node_state(struct node *node) { const unsigned char *buf = node->data; int buflen = node->datalen; int i = 0, j, rc; struct node_neighbour *nn = NULL; int numnn = 0, maxnn = 0; struct external **exts = NULL; int numexts = 0, maxexts = 0; for(j = 0; j < numneighs; j++) { if(id_eq(neighs[j].id, node->id)) neighs[j].keepalive_interval = 0; } while(i < buflen) { unsigned const char *tlv = buf + i; int type, bodylen; if(buflen - i < 4) { fprintf(stderr, "Received truncated TLV.\n"); goto fail; } DO_NTOHS(type, tlv); DO_NTOHS(bodylen, tlv + 2); if(buflen - i < 4 + bodylen) { fprintf(stderr, "Received truncated TLV.\n"); goto fail; } switch(type) { case 7: fprintf(stderr, "Received fragmented nonsense.\n"); goto fail; case 8: { if(bodylen < 12) { fprintf(stderr, "Truncated neighbour TLV.\n"); break; } if(numnn >= maxnn) { struct node_neighbour *nnn = realloc(nn, (2 * maxnn + 2) * sizeof(struct node_neighbour)); if(nnn == NULL) { fprintf(stderr, "Eek.\n"); /* Oh, well. */ break; } nn = nnn; maxnn = 2 * maxnn + 2; } memcpy(nn[numnn].neigh, tlv + 4, 4); DO_NTOHL(nn[numnn].nei, tlv + 8); DO_NTOHL(nn[numnn].lei, tlv + 12); debugf(" NEIGHBOR %s (nei=%u, lei=%u)\n", format_32(nn[numnn].neigh), nn[numnn].nei, nn[numnn].lei); numnn++; break; } case 9: { unsigned eid, interval; if(bodylen < 8) { fprintf(stderr, "Truncated KEEP-ALIVE-INTERVAL.\n"); break; } DO_NTOHL(eid, tlv + 4); DO_NTOHL(interval, tlv + 8); debugf(" KEEP-ALIVE-INTERVAL %u (eid=%u)\n", interval, eid); for(j = 0; j < numneighs; j++) { if(id_eq(neighs[j].id, node->id) && neighs[j].eid == eid) neighs[j].keepalive_interval = interval ? interval : 0xFFFFFFFF; } break; } case 32: { if(bodylen < 4) { debugf("Truncated VERSION\n"); goto fail; } debugf(" VERSION\n"); memcpy(node->capabilities, tlv + 6, 2); break; } case 33: { struct external *ext; debugf(" EXTERNAL-CONNECTION\n"); ext = parse_external(node, tlv + 4, bodylen); if(ext) { if(numexts >= maxexts) { struct external **e = realloc(exts, (2 * maxexts + 2) * sizeof(struct external *)); if(e == NULL) { fprintf(stderr, "Eek.\n"); break; } maxexts = 2 * maxexts + 2; exts = e; } exts[numexts++] = ext; } break; } case 35: { unsigned char plen; struct in6_addr addr; int prio; unsigned int eid; struct prefix_list *pl; if(bodylen < 6) { fprintf(stderr, "Truncated ASSIGNED-PREFIX.\n"); break; } DO_NTOHL(eid, tlv + 4); plen = *(tlv + 9); if(bodylen < 6 + (plen + 7) / 8) { fprintf(stderr, "Truncated ASSIGNED-PREFIX.\n"); break; } prio = tlv[8] & 0x0F; parse_pref(&addr, tlv + 10, plen); debugf(" ASSIGNED-PREFIX "); debug_prefix_raw(&addr, plen); debugf(" (%d, prio=%d)\n", eid, prio); pl = prefix_list_cons(node->assigned, &addr, plen, node->id, eid, prio); if(pl) node->assigned = pl; break; } case 36: { unsigned int eid; struct prefix_list *pl; struct in6_addr addr; if(bodylen < 20) { fprintf(stderr, "Truncated NODE-ADDRESS.\n"); break; } DO_NTOHL(eid, tlv + 4); memcpy(&addr, tlv + 8, 16); debugf(" NODE-ADDRESS "); debug_address(&addr); debugf(" %d\n",eid); pl = prefix_list_cons(node->addresses, &addr, 128, node->id, eid, 0); if(pl) node->addresses = pl; break; } default: if(debug_level >= 3) debugf(" %d: %d\n", type, bodylen); break; } i += 4 + bodylen; i += -i & 3; } rc = 1; goto done; fail: rc = -1; done: if(node->neighs) free(node->neighs); node->neighs = nn; node->numneighs = numnn; if(node->exts) { for(i = 0; i < node->numexts; i++) destroy_external(node->exts[i]); free(node->exts); } node->exts = exts; node->numexts = numexts; return rc; }
/* The main function -- deal with a packet. */ int handle_packet(int sock, unsigned char *packet, int packetlen, struct interface *interface, struct in6_addr *from) { int bodylen, length, i; struct in6_addr nexthop; int have_nexthop = 0; if(!linklocal(from)) { fprintf(stderr, "Received non-link-local packet.\n"); return -1; } if(packetlen < 4 || packet[0] != 42 || packet[1] != 2) goto fail; DO_NTOHS(bodylen, packet + 2); if(bodylen + 4 > packetlen) goto fail; i = 0; while(i < bodylen) { unsigned char *tlv = packet + 4 + i; unsigned int type; type = tlv[0]; if(type == MESSAGE_PAD1) { i++; continue; } if(i + 1 > bodylen) goto fail; length = tlv[1]; if(i + length > bodylen) goto fail; #define CHECK(l) do { if(length + 2 < l) goto fail; } while(0) switch(type) { case MESSAGE_ACK_REQ: CHECK(8); send_ack(sock, interface, from, tlv + 4); break; case MESSAGE_HELLO: { unsigned short interval; CHECK(8); DO_NTOHS(interval, tlv + 6); update_neighbour(from, interface, 0, interval); break; } case MESSAGE_IHU: CHECK(8); if(tlv[2] == AE_WILDCARD || (tlv[2] == AE_LL && length + 2 >= 16 && address_match(tlv + 8, interface))) { unsigned short rxcost; DO_NTOHS(rxcost, tlv + 4); update_neighbour(from, interface, 1, rxcost); } break; case MESSAGE_NH: CHECK(4); if(tlv[2] == AE_LL) { unsigned char ll[8] = {0xfe, 0x80, 0, 0, 0, 0, 0, 0}; CHECK(12); memcpy((unsigned char*)&nexthop, ll, 8); memcpy(((unsigned char*)&nexthop) + 8, tlv + 4, 8); have_nexthop = 1; } break; case MESSAGE_UPDATE: CHECK(12); /* We're only interested in IPv6 default routes. */ if(tlv[2] == AE_IPV6 && tlv[4] == 0) { unsigned short interval, metric; DO_NTOHS(interval, tlv + 6); DO_NTOHS(metric, tlv + 10); update_selected_route(interface, have_nexthop ? &nexthop : from, interval, metric); } break; case MESSAGE_REQUEST: CHECK(4); if(tlv[2] == AE_WILDCARD) { /* Request for a full table dump. */ send_update(sock, interface, 0); } else if(tlv[2] == AE_IPV6 && tlv[3] == 64) { /* Request for a specific /64. Is it ours? */ CHECK(12); if(have_prefix && memcmp(myprefix, tlv + 4, 8) == 0) send_update(sock, interface, 0); } break; case MESSAGE_MH_REQUEST: CHECK(16); /* There's no such thing as a wildcard multi-hop request. */ if(tlv[2] == AE_IPV6 && tlv[3] == 64) { unsigned int seqno; DO_NTOHS(seqno, tlv + 4); if(have_prefix && tlv[3] == 6 && memcmp(myprefix, tlv + 8, 8) == 0 && memcmp(tlv + 8, my_router_id, 8) == 0) { increment_myseqno(seqno); send_update(sock, interface, 0); } } break; default: /* We're ignoring all other TLVs. */ break; } i += length + 2; } return 1; fail: fprintf(stderr, "Received malformed packet.\n"); return -1; }