1 /* 2 * edns.c -- EDNS definitions (RFC 2671). 3 * 4 * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. 5 * 6 * See LICENSE for the license. 7 * 8 */ 9 10 11 #include "config.h" 12 13 #include <string.h> 14 15 #include "dns.h" 16 #include "edns.h" 17 #include "nsd.h" 18 #include "query.h" 19 20 void 21 edns_init_data(edns_data_type *data, uint16_t max_length) 22 { 23 memset(data, 0, sizeof(edns_data_type)); 24 /* record type: OPT */ 25 data->ok[1] = (TYPE_OPT & 0xff00) >> 8; /* type_hi */ 26 data->ok[2] = TYPE_OPT & 0x00ff; /* type_lo */ 27 /* udp payload size */ 28 data->ok[3] = (max_length & 0xff00) >> 8; /* size_hi */ 29 data->ok[4] = max_length & 0x00ff; /* size_lo */ 30 31 data->error[1] = (TYPE_OPT & 0xff00) >> 8; /* type_hi */ 32 data->error[2] = TYPE_OPT & 0x00ff; /* type_lo */ 33 data->error[3] = (max_length & 0xff00) >> 8; /* size_hi */ 34 data->error[4] = max_length & 0x00ff; /* size_lo */ 35 data->error[5] = 1; /* XXX Extended RCODE=BAD VERS */ 36 } 37 38 void 39 edns_init_nsid(edns_data_type *data, uint16_t nsid_len) 40 { 41 /* add nsid length bytes */ 42 data->rdata_nsid[0] = ((OPT_HDR + nsid_len) & 0xff00) >> 8; /* length_hi */ 43 data->rdata_nsid[1] = ((OPT_HDR + nsid_len) & 0x00ff); /* length_lo */ 44 45 /* NSID OPT HDR */ 46 data->nsid[0] = (NSID_CODE & 0xff00) >> 8; 47 data->nsid[1] = (NSID_CODE & 0x00ff); 48 data->nsid[2] = (nsid_len & 0xff00) >> 8; 49 data->nsid[3] = (nsid_len & 0x00ff); 50 } 51 52 void 53 edns_init_record(edns_record_type *edns) 54 { 55 edns->status = EDNS_NOT_PRESENT; 56 edns->position = 0; 57 edns->maxlen = 0; 58 edns->opt_reserved_space = 0; 59 edns->dnssec_ok = 0; 60 edns->nsid = 0; 61 } 62 63 /** handle a single edns option in the query */ 64 static int 65 edns_handle_option(uint16_t optcode, uint16_t optlen, buffer_type* packet, 66 edns_record_type* edns, struct query* query, nsd_type* nsd) 67 { 68 (void) query; /* in case edns options need the query structure */ 69 /* handle opt code and read the optlen bytes from the packet */ 70 switch(optcode) { 71 case NSID_CODE: 72 /* is NSID enabled? */ 73 if(nsd->nsid_len > 0) { 74 edns->nsid = 1; 75 /* we have to check optlen, and move the buffer along */ 76 buffer_skip(packet, optlen); 77 /* in the reply we need space for optcode+optlen+nsid_bytes */ 78 edns->opt_reserved_space += OPT_HDR + nsd->nsid_len; 79 } else { 80 /* ignore option */ 81 buffer_skip(packet, optlen); 82 } 83 break; 84 default: 85 buffer_skip(packet, optlen); 86 break; 87 } 88 return 1; 89 } 90 91 int 92 edns_parse_record(edns_record_type *edns, buffer_type *packet, 93 query_type* query, nsd_type* nsd) 94 { 95 /* OPT record type... */ 96 uint8_t opt_owner; 97 uint16_t opt_type; 98 uint16_t opt_class; 99 uint8_t opt_version; 100 uint16_t opt_flags; 101 uint16_t opt_rdlen; 102 103 edns->position = buffer_position(packet); 104 105 if (!buffer_available(packet, (OPT_LEN + OPT_RDATA))) 106 return 0; 107 108 opt_owner = buffer_read_u8(packet); 109 opt_type = buffer_read_u16(packet); 110 if (opt_owner != 0 || opt_type != TYPE_OPT) { 111 /* Not EDNS. */ 112 buffer_set_position(packet, edns->position); 113 return 0; 114 } 115 116 opt_class = buffer_read_u16(packet); 117 (void)buffer_read_u8(packet); /* opt_extended_rcode */ 118 opt_version = buffer_read_u8(packet); 119 opt_flags = buffer_read_u16(packet); 120 opt_rdlen = buffer_read_u16(packet); 121 122 if (opt_version != 0) { 123 /* The only error is VERSION not implemented */ 124 edns->status = EDNS_ERROR; 125 return 1; 126 } 127 128 if (opt_rdlen > 0) { 129 if(!buffer_available(packet, opt_rdlen)) 130 return 0; 131 /* there is more to come, read opt code */ 132 while(opt_rdlen >= 4) { 133 uint16_t optcode = buffer_read_u16(packet); 134 uint16_t optlen = buffer_read_u16(packet); 135 if(opt_rdlen < 4+optlen) 136 return 0; /* opt too long, formerr */ 137 opt_rdlen -= (4+optlen); 138 if(!edns_handle_option(optcode, optlen, packet, 139 edns, query, nsd)) 140 return 0; 141 } 142 if(opt_rdlen != 0) 143 return 0; 144 } 145 146 edns->status = EDNS_OK; 147 edns->maxlen = opt_class; 148 edns->dnssec_ok = opt_flags & DNSSEC_OK_MASK; 149 return 1; 150 } 151 152 size_t 153 edns_reserved_space(edns_record_type *edns) 154 { 155 /* MIEK; when a pkt is too large?? */ 156 return edns->status == EDNS_NOT_PRESENT ? 0 : (OPT_LEN + OPT_RDATA + edns->opt_reserved_space); 157 } 158