1 /*
2  * Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14  * PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #ifndef lint
18 static const char rcsid[] = "$Id: ns_newmsg.c,v 1.3 2009/02/26 10:48:57 marka Exp $";
19 #endif
20 
21 #include <arpa/nameser.h>
22 
23 #include <assert.h>
24 #include <errno.h>
25 #include <string.h>
26 
27 static int	rdcpy(ns_newmsg *, ns_type, const u_char *, size_t);
28 
29 /* Initialize a "newmsg" object to empty.
30  */
31 int
ns_newmsg_init(u_char * buffer,size_t bufsiz,ns_newmsg * handle)32 ns_newmsg_init(u_char *buffer, size_t bufsiz, ns_newmsg *handle) {
33 	ns_msg *msg = &handle->msg;
34 
35 	memset(handle, 0, sizeof *handle);
36 	msg->_msg = buffer;
37 	msg->_eom = buffer + bufsiz;
38 	msg->_sect = ns_s_qd;
39 	msg->_rrnum = 0;
40 	msg->_msg_ptr = buffer + NS_HFIXEDSZ;
41 	handle->dnptrs[0] = msg->_msg;
42 	handle->dnptrs[1] = NULL;
43 	handle->lastdnptr = &handle->dnptrs[sizeof handle->dnptrs /
44 					    sizeof handle->dnptrs[0] - 1];
45 	return (0);
46 }
47 
48 /* Initialize a "newmsg" object by copying an existing parsed message.
49  */
50 int
ns_newmsg_copy(ns_newmsg * handle,ns_msg * msg)51 ns_newmsg_copy(ns_newmsg *handle, ns_msg *msg) {
52 	ns_flag flag;
53 	ns_sect sect;
54 
55 	ns_newmsg_id(handle, ns_msg_id(*msg));
56 	for (flag = ns_f_qr; flag < ns_f_max; flag++)
57 		ns_newmsg_flag(handle, flag, ns_msg_getflag(*msg, flag));
58 	for (sect = ns_s_qd; sect < ns_s_max; sect++) {
59 		int i, count;
60 
61 		count = ns_msg_count(*msg, sect);
62 		for (i = 0; i < count; i++) {
63 			ns_rr2 rr;
64 			int x;
65 
66 			if (ns_parserr2(msg, sect, i, &rr) < 0)
67 				return (-1);
68 			if (sect == ns_s_qd)
69 				x = ns_newmsg_q(handle,
70 						ns_rr_nname(rr),
71 						ns_rr_type(rr),
72 						ns_rr_class(rr));
73 			else
74 				x = ns_newmsg_rr(handle, sect,
75 						 ns_rr_nname(rr),
76 						 ns_rr_type(rr),
77 						 ns_rr_class(rr),
78 						 ns_rr_ttl(rr),
79 						 ns_rr_rdlen(rr),
80 						 ns_rr_rdata(rr));
81 			if (x < 0)
82 				return (-1);
83 		}
84 	}
85 	return (0);
86 }
87 
88 /* Set the message-ID in a "newmsg" object.
89  */
90 void
ns_newmsg_id(ns_newmsg * handle,u_int16_t id)91 ns_newmsg_id(ns_newmsg *handle, u_int16_t id) {
92 	ns_msg *msg = &handle->msg;
93 
94 	msg->_id = id;
95 }
96 
97 /* Set a flag (including rcode or opcode) in a "newmsg" object.
98  */
99 void
ns_newmsg_flag(ns_newmsg * handle,ns_flag flag,u_int value)100 ns_newmsg_flag(ns_newmsg *handle, ns_flag flag, u_int value) {
101 	extern struct _ns_flagdata _ns_flagdata[16];
102 	struct _ns_flagdata *fd = &_ns_flagdata[flag];
103 	ns_msg *msg = &handle->msg;
104 
105 	assert(flag < ns_f_max);
106 	msg->_flags &= (~fd->mask);
107 	msg->_flags |= (value << fd->shift);
108 }
109 
110 /* Add a question (or zone, if it's an update) to a "newmsg" object.
111  */
112 int
ns_newmsg_q(ns_newmsg * handle,ns_nname_ct qname,ns_type qtype,ns_class qclass)113 ns_newmsg_q(ns_newmsg *handle, ns_nname_ct qname,
114 	    ns_type qtype, ns_class qclass)
115 {
116 	ns_msg *msg = &handle->msg;
117 	u_char *t;
118 	int n;
119 
120 	if (msg->_sect != ns_s_qd) {
121 		errno = ENODEV;
122 		return (-1);
123 	}
124 	t = (u_char *) (unsigned long) msg->_msg_ptr;
125 	if (msg->_rrnum == 0)
126 		msg->_sections[ns_s_qd] = t;
127 	n = ns_name_pack(qname, t, msg->_eom - t,
128 			 handle->dnptrs, handle->lastdnptr);
129 	if (n < 0)
130 		return (-1);
131 	t += n;
132 	if (t + QFIXEDSZ >= msg->_eom) {
133 		errno = EMSGSIZE;
134 		return (-1);
135 	}
136 	NS_PUT16(qtype, t);
137 	NS_PUT16(qclass, t);
138 	msg->_msg_ptr = t;
139 	msg->_counts[ns_s_qd] = ++msg->_rrnum;
140 	return (0);
141 }
142 
143 /* Add an RR to a "newmsg" object.
144  */
145 int
ns_newmsg_rr(ns_newmsg * handle,ns_sect sect,ns_nname_ct name,ns_type type,ns_class rr_class,u_int32_t ttl,u_int16_t rdlen,const u_char * rdata)146 ns_newmsg_rr(ns_newmsg *handle, ns_sect sect,
147 	     ns_nname_ct name, ns_type type,
148 	     ns_class rr_class, u_int32_t ttl,
149 	     u_int16_t rdlen, const u_char *rdata)
150 {
151 	ns_msg *msg = &handle->msg;
152 	u_char *t;
153 	int n;
154 
155 	if (sect < msg->_sect) {
156 		errno = ENODEV;
157 		return (-1);
158 	}
159 	t = (u_char *) (unsigned long) msg->_msg_ptr;
160 	if (sect > msg->_sect) {
161 		msg->_sect = sect;
162 		msg->_sections[sect] = t;
163 		msg->_rrnum = 0;
164 	}
165 	n = ns_name_pack(name, t, msg->_eom - t,
166 			 handle->dnptrs, handle->lastdnptr);
167 	if (n < 0)
168 		return (-1);
169 	t += n;
170 	if (t + RRFIXEDSZ + rdlen >= msg->_eom) {
171 		errno = EMSGSIZE;
172 		return (-1);
173 	}
174 	NS_PUT16(type, t);
175 	NS_PUT16(rr_class, t);
176 	NS_PUT32(ttl, t);
177 	msg->_msg_ptr = t;
178 	if (rdcpy(handle, type, rdata, rdlen) < 0)
179 		return (-1);
180 	msg->_counts[sect] = ++msg->_rrnum;
181 	return (0);
182 }
183 
184 /* Complete a "newmsg" object and return its size for use in write().
185  * (Note: the "newmsg" object is also made ready for ns_parserr() etc.)
186  */
187 size_t
ns_newmsg_done(ns_newmsg * handle)188 ns_newmsg_done(ns_newmsg *handle) {
189 	ns_msg *msg = &handle->msg;
190 	ns_sect sect;
191 	u_char *t;
192 
193 	t = (u_char *) (unsigned long) msg->_msg;
194 	NS_PUT16(msg->_id, t);
195 	NS_PUT16(msg->_flags, t);
196 	for (sect = 0; sect < ns_s_max; sect++)
197 		NS_PUT16(msg->_counts[sect], t);
198 	msg->_eom = msg->_msg_ptr;
199 	msg->_sect = ns_s_max;
200 	msg->_rrnum = -1;
201 	msg->_msg_ptr = NULL;
202 	return (msg->_eom - msg->_msg);
203 }
204 
205 /* Private. */
206 
207 /* Copy an RDATA, using compression pointers where RFC1035 permits.
208  */
209 static int
rdcpy(ns_newmsg * handle,ns_type type,const u_char * rdata,size_t rdlen)210 rdcpy(ns_newmsg *handle, ns_type type, const u_char *rdata, size_t rdlen) {
211 	ns_msg *msg = &handle->msg;
212 	u_char *p = (u_char *) (unsigned long) msg->_msg_ptr;
213 	u_char *t = p + NS_INT16SZ;
214 	u_char *s = t;
215 	int n;
216 
217 	switch (type) {
218 	case ns_t_soa:
219 		/* MNAME. */
220 		n = ns_name_pack(rdata, t, msg->_eom - t,
221 				 handle->dnptrs, handle->lastdnptr);
222 		if (n < 0)
223 			return (-1);
224 		t += n;
225 		if (ns_name_skip(&rdata, msg->_eom) < 0)
226 			return (-1);
227 
228 		/* ANAME. */
229 		n = ns_name_pack(rdata, t, msg->_eom - t,
230 				 handle->dnptrs, handle->lastdnptr);
231 		if (n < 0)
232 			return (-1);
233 		t += n;
234 		if (ns_name_skip(&rdata, msg->_eom) < 0)
235 			return (-1);
236 
237 		/* Serial, Refresh, Retry, Expiry, and Minimum. */
238 		if ((msg->_eom - t) < (NS_INT32SZ * 5)) {
239 			errno = EMSGSIZE;
240 			return (-1);
241 		}
242 		memcpy(t, rdata, NS_INT32SZ * 5);
243 		t += (NS_INT32SZ * 5);
244 		break;
245 	case ns_t_ptr:
246 	case ns_t_cname:
247 	case ns_t_ns:
248 		/* PTRDNAME, CNAME, or NSDNAME. */
249 		n = ns_name_pack(rdata, t, msg->_eom - t,
250 				 handle->dnptrs, handle->lastdnptr);
251 		if (n < 0)
252 			return (-1);
253 		t += n;
254 		break;
255 	default:
256 		memcpy(t, rdata, rdlen);
257 		t += rdlen;
258 	}
259 	NS_PUT16(t - s, p);
260 	msg->_msg_ptr = t;
261 	return (0);
262 }
263 
264