xref: /openbsd-src/usr.sbin/nsd/edns.c (revision 99fd087599a8791921855f21bd7e36130f39aadc)
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