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