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