/*----------------------------------------------------------------------------*/ _public_ int knot_edns_add_option(knot_rrset_t *opt_rr, uint16_t code, uint16_t length, const uint8_t *data, mm_ctx_t *mm) { if (opt_rr == NULL || (length != 0 && data == NULL)) { return KNOT_EINVAL; } /* We need to replace the RDATA currently in the OPT RR */ /* 1) create new RDATA by appending the new option after the current * RDATA. */ assert(opt_rr->rrs.rr_count == 1); knot_rdata_t *old_rdata = knot_rdataset_at(&opt_rr->rrs, 0); uint8_t *old_data = knot_rdata_data(old_rdata); uint16_t old_data_len = knot_rdata_rdlen(old_rdata); uint16_t new_data_len = old_data_len + KNOT_EDNS_OPTION_HDRLEN + length; uint8_t new_data[new_data_len]; memcpy(new_data, old_data, old_data_len); // write length and code in wireformat (convert endian) wire_write_u16(new_data + old_data_len, code); wire_write_u16(new_data + old_data_len + sizeof(uint16_t), length); // write the option data memcpy(new_data + old_data_len + KNOT_EDNS_OPTION_HDRLEN, data, length); /* 2) Replace the RDATA in the RRSet. */ uint32_t old_ttl = knot_rdata_ttl(old_rdata); knot_rdataset_clear(&opt_rr->rrs, mm); return knot_rrset_add_rdata(opt_rr, new_data, new_data_len, old_ttl, mm); }
static bool check_ttl(knot_rdata_t *rdata, uint8_t ext_rcode, uint8_t ver, uint16_t flags, char *msg, int *done) { /* TTL should be stored in machine byte order. We need network byte order to compare its parts. */ uint8_t ttl_wire[4] = { 0, 0, 0, 0 }; wire_write_u32(ttl_wire, knot_rdata_ttl(rdata)); /* Convert Flags from EDNS parameters to wire format for comparison. */ uint8_t flags_wire[2] = { 0, 0 }; wire_write_u16(flags_wire, flags); bool success = true; /* TTL = Ext RCODE + Version + Flags */ bool check = (ttl_wire[OFFSET_ERCODE] == ext_rcode); ok(check, "%s: extended RCODE", msg); success &= check; (*done)++; check = (ttl_wire[OFFSET_VER] == ver); ok(check, "%s: version", msg); success &= check; (*done)++; check = (memcmp(flags_wire, ttl_wire + OFFSET_FLAGS, 2) == 0); ok(check, "%s: flags", msg); success &= check; (*done)++; return success; }
_public_ int knot_pkt_put_question(knot_pkt_t *pkt, const knot_dname_t *qname, uint16_t qclass, uint16_t qtype) { if (pkt == NULL || qname == NULL) { return KNOT_EINVAL; } assert(pkt->size == KNOT_WIRE_HEADER_SIZE); assert(pkt->rrset_count == 0); /* Copy name wireformat. */ uint8_t *dst = pkt->wire + KNOT_WIRE_HEADER_SIZE; int qname_len = knot_dname_to_wire(dst, qname, pkt->max_size - pkt->size); if (qname_len < 0) { return qname_len; } /* Check size limits. */ size_t question_len = 2 * sizeof(uint16_t) + qname_len; if (qname_len < 0 || pkt->size + question_len > pkt->max_size) { return KNOT_ESPACE; } /* Copy QTYPE & QCLASS */ dst += qname_len; wire_write_u16(dst, qtype); dst += sizeof(uint16_t); wire_write_u16(dst, qclass); /* Update question count and sizes. */ knot_wire_set_qdcount(pkt->wire, 1); pkt->size += question_len; pkt->qname_size = qname_len; /* Start writing ANSWER. */ return knot_pkt_begin(pkt, KNOT_ANSWER); }
/*----------------------------------------------------------------------------*/ _public_ int knot_edns_client_subnet_create(const knot_addr_family_t family, const uint8_t *addr, const uint16_t addr_len, uint8_t src_mask, uint8_t dst_mask, uint8_t *data, uint16_t *data_len) { if (addr == NULL || data == NULL || data_len == NULL) { return KNOT_EINVAL; } uint8_t addr_prefix_len = (src_mask + 7) / 8; // Ceiling operation. uint8_t modulo = src_mask % 8; uint16_t total = sizeof(uint16_t) + 2 * sizeof(uint8_t) + addr_prefix_len; if (*data_len < total) { return KNOT_ESPACE; } if (addr_prefix_len > addr_len) { return KNOT_EINVAL; } wire_write_u16(data + EDNS_OFFSET_CLIENT_SUBNET_FAMILY, family); data[EDNS_OFFSET_CLIENT_SUBNET_SRC_MASK] = src_mask; data[EDNS_OFFSET_CLIENT_SUBNET_DST_MASK] = dst_mask; memcpy(data + EDNS_OFFSET_CLIENT_SUBNET_ADDR, addr, addr_prefix_len); // Zeroize trailing bits in the last byte. if (modulo > 0 && addr_prefix_len > 0) { data[EDNS_OFFSET_CLIENT_SUBNET_ADDR + addr_prefix_len - 1] &= 0xFF << (8 - modulo); } *data_len = total; return KNOT_EOK; }
int main(void) { plan_lazy(); wire_ctx_t wire = { 0 }; dnssec_binary_t buffer = { .size = 20, .data = (uint8_t []) { 0xc8, 0x25, 0x19, 0x3c, 0x96, 0xe6, 0x59, 0xf7, 0x2b, 0x94, 0x83, 0xb3, 0x3e, 0x6f, 0xb9, 0x01, 0xe2, 0x91, 0xa8, 0xa9, } }; wire = wire_init(buffer.data + 10, 10); ok(wire_read_u8(&wire) == 0x83, "wire_init()"); wire = wire_init_binary(&buffer); ok(wire_read_u8(&wire) == 0xc8, "wire_init_binary()"); // read operations wire_seek(&wire, 5); ok(wire_read_u8(&wire) == 0xe6, "wire_seek() forward"); wire_seek(&wire, 3); ok(wire_read_u8(&wire) == 0x3c, "wire_seek() backward"); wire_seek(&wire, 10); ok(wire_read_u8(&wire) == 0x83, "wire_read_u8()"); ok(wire_read_u16(&wire) == 45886, "wire_read_u16()"); ok(wire_tell(&wire) == 13, "wire_tell()"); ok(wire_available(&wire) == 7, "wire_available()"); dnssec_binary_t ref = { 0 }; wire_available_binary(&wire, &ref); ok(ref.data == buffer.data + 13 && ref.size == 7, "wire_available_binary()"); uint8_t in[6] = { 0 }; wire_seek(&wire, 4); wire_read(&wire, in, 6); ok(memcmp(in, buffer.data + 4, 6) == 0 && wire_tell(&wire) == 10, "wire_read()"); // write operations wire_seek(&wire, 0); wire_write_u8(&wire, 0x42); ok(buffer.data[0] == 0x42 && wire_tell(&wire) == 1, "wire_write_u8()"); wire_write_u16(&wire, 44513); ok(memcmp(buffer.data + 1, "\xad\xe1", 2) == 0 && wire_tell(&wire) == 3, "wire_write_u16()"); wire_seek(&wire, 12); const uint8_t out[7] = { 0xc0, 0x1d, 0xca, 0xfe, 0xde, 0xad, 0xbe }; wire_write(&wire, out, 7); ok(memcmp(buffer.data + 12, out, 7) == 0 && wire_tell(&wire) == 19, "wire_write()"); dnssec_binary_t bignum = { .data = (uint8_t *)out, .size = 4 }; const uint8_t expect[8] = { 0x00, 0x00, 0x00, 0x00, 0xc0, 0x1d, 0xca, 0xfe }; wire_seek(&wire, 2); wire_write_bignum(&wire, 8, &bignum); ok(memcmp(buffer.data + 2, expect, 8) == 0 && wire_tell(&wire) == 10, "wire_write_ralign()"); return 0; }