1 | /* |
---|
2 | * Copyright (c) 1996 by Internet Software Consortium. |
---|
3 | * |
---|
4 | * Permission to use, copy, modify, and 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 INTERNET SOFTWARE CONSORTIUM DISCLAIMS |
---|
9 | * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES |
---|
10 | * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE |
---|
11 | * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
---|
12 | * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
---|
13 | * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS |
---|
14 | * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
---|
15 | * SOFTWARE. |
---|
16 | */ |
---|
17 | |
---|
18 | /* |
---|
19 | * Based on the Dynamic DNS reference implementation by Viraj Bais |
---|
20 | * <viraj_bais@ccm.fm.intel.com> |
---|
21 | */ |
---|
22 | |
---|
23 | #if !defined(__rtems__) |
---|
24 | #if !defined(lint) && !defined(SABER) |
---|
25 | static char rcsid[] = "$Id$"; |
---|
26 | #endif /* not lint */ |
---|
27 | #endif /* not rtems */ |
---|
28 | |
---|
29 | /* Since we compile with strict ANSI we need to undef it to get |
---|
30 | * prototypes for extensions |
---|
31 | */ |
---|
32 | #undef __STRICT_ANSI__ |
---|
33 | |
---|
34 | #include <sys/types.h> |
---|
35 | #include <sys/param.h> |
---|
36 | |
---|
37 | #include <netinet/in.h> |
---|
38 | #include <arpa/nameser.h> |
---|
39 | #include <arpa/inet.h> |
---|
40 | |
---|
41 | #include <errno.h> |
---|
42 | #include <limits.h> |
---|
43 | #include <netdb.h> |
---|
44 | #include <resolv.h> |
---|
45 | #include <stdio.h> |
---|
46 | #include <stdlib.h> |
---|
47 | #include <string.h> |
---|
48 | #include <unistd.h> |
---|
49 | #include <ctype.h> |
---|
50 | |
---|
51 | #include "res_config.h" |
---|
52 | |
---|
53 | static int getnum_str(u_char **, u_char *); |
---|
54 | static int getword_str(char *, int, u_char **, u_char *); |
---|
55 | |
---|
56 | #define ShrinkBuffer(x) if ((buflen -= x) < 0) return (-2); |
---|
57 | |
---|
58 | /* |
---|
59 | * Form update packets. |
---|
60 | * Returns the size of the resulting packet if no error |
---|
61 | * On error, |
---|
62 | * returns -1 if error in reading a word/number in rdata |
---|
63 | * portion for update packets |
---|
64 | * -2 if length of buffer passed is insufficient |
---|
65 | * -3 if zone section is not the first section in |
---|
66 | * the linked list, or section order has a problem |
---|
67 | * -4 on a number overflow |
---|
68 | * -5 unknown operation or no records |
---|
69 | */ |
---|
70 | int |
---|
71 | res_mkupdate(ns_updrec *rrecp_in, u_char *buf, int buflen) { |
---|
72 | ns_updrec *rrecp_start = rrecp_in; |
---|
73 | HEADER *hp; |
---|
74 | u_char *cp, *sp1, *sp2, *startp, *endp; |
---|
75 | int n, i, soanum, multiline; |
---|
76 | ns_updrec *rrecp; |
---|
77 | struct in_addr ina; |
---|
78 | char buf2[MAXDNAME]; |
---|
79 | int section, numrrs = 0, counts[ns_s_max]; |
---|
80 | u_int16_t rtype, rclass; |
---|
81 | u_int32_t n1, rttl; |
---|
82 | u_char *dnptrs[20], **dpp, **lastdnptr; |
---|
83 | |
---|
84 | if ((_res.options & RES_INIT) == 0 && res_init() == -1) { |
---|
85 | h_errno = NETDB_INTERNAL; |
---|
86 | return (-1); |
---|
87 | } |
---|
88 | |
---|
89 | /* |
---|
90 | * Initialize header fields. |
---|
91 | */ |
---|
92 | if ((buf == NULL) || (buflen < HFIXEDSZ)) |
---|
93 | return (-1); |
---|
94 | memset(buf, 0, HFIXEDSZ); |
---|
95 | hp = (HEADER *) buf; |
---|
96 | hp->id = htons(++_res.id); |
---|
97 | hp->opcode = ns_o_update; |
---|
98 | hp->rcode = NOERROR; |
---|
99 | sp1 = buf + 2*INT16SZ; /* save pointer to zocount */ |
---|
100 | cp = buf + HFIXEDSZ; |
---|
101 | buflen -= HFIXEDSZ; |
---|
102 | dpp = dnptrs; |
---|
103 | *dpp++ = buf; |
---|
104 | *dpp++ = NULL; |
---|
105 | lastdnptr = dnptrs + sizeof dnptrs / sizeof dnptrs[0]; |
---|
106 | |
---|
107 | if (rrecp_start == NULL) |
---|
108 | return (-5); |
---|
109 | else if (rrecp_start->r_section != S_ZONE) |
---|
110 | return (-3); |
---|
111 | |
---|
112 | memset(counts, 0, sizeof counts); |
---|
113 | for (rrecp = rrecp_start; rrecp; rrecp = rrecp->r_grpnext) { |
---|
114 | numrrs++; |
---|
115 | section = rrecp->r_section; |
---|
116 | if (section < 0 || section >= ns_s_max) |
---|
117 | return (-1); |
---|
118 | counts[section]++; |
---|
119 | for (i = section + 1; i < ns_s_max; i++) |
---|
120 | if (counts[i]) |
---|
121 | return (-3); |
---|
122 | rtype = rrecp->r_type; |
---|
123 | rclass = rrecp->r_class; |
---|
124 | rttl = rrecp->r_ttl; |
---|
125 | /* overload class and type */ |
---|
126 | if (section == S_PREREQ) { |
---|
127 | rttl = 0; |
---|
128 | switch (rrecp->r_opcode) { |
---|
129 | case YXDOMAIN: |
---|
130 | rclass = C_ANY; |
---|
131 | rtype = T_ANY; |
---|
132 | rrecp->r_size = 0; |
---|
133 | break; |
---|
134 | case NXDOMAIN: |
---|
135 | rclass = C_NONE; |
---|
136 | rtype = T_ANY; |
---|
137 | rrecp->r_size = 0; |
---|
138 | break; |
---|
139 | case NXRRSET: |
---|
140 | rclass = C_NONE; |
---|
141 | rrecp->r_size = 0; |
---|
142 | break; |
---|
143 | case YXRRSET: |
---|
144 | if (rrecp->r_size == 0) |
---|
145 | rclass = C_ANY; |
---|
146 | break; |
---|
147 | default: |
---|
148 | fprintf(stderr, |
---|
149 | "res_mkupdate: incorrect opcode: %d\n", |
---|
150 | rrecp->r_opcode); |
---|
151 | fflush(stderr); |
---|
152 | return (-1); |
---|
153 | } |
---|
154 | } else if (section == S_UPDATE) { |
---|
155 | switch (rrecp->r_opcode) { |
---|
156 | case DELETE: |
---|
157 | rclass = rrecp->r_size == 0 ? C_ANY : C_NONE; |
---|
158 | break; |
---|
159 | case ADD: |
---|
160 | break; |
---|
161 | default: |
---|
162 | fprintf(stderr, |
---|
163 | "res_mkupdate: incorrect opcode: %d\n", |
---|
164 | rrecp->r_opcode); |
---|
165 | fflush(stderr); |
---|
166 | return (-1); |
---|
167 | } |
---|
168 | } |
---|
169 | |
---|
170 | /* |
---|
171 | * XXX appending default domain to owner name is omitted, |
---|
172 | * fqdn must be provided |
---|
173 | */ |
---|
174 | if ((n = dn_comp(rrecp->r_dname, cp, buflen, dnptrs, |
---|
175 | lastdnptr)) < 0) |
---|
176 | return (-1); |
---|
177 | cp += n; |
---|
178 | ShrinkBuffer(n + 2*INT16SZ); |
---|
179 | PUTSHORT(rtype, cp); |
---|
180 | PUTSHORT(rclass, cp); |
---|
181 | if (section == S_ZONE) { |
---|
182 | if (numrrs != 1 || rrecp->r_type != T_SOA) |
---|
183 | return (-3); |
---|
184 | continue; |
---|
185 | } |
---|
186 | ShrinkBuffer(INT32SZ + INT16SZ); |
---|
187 | PUTLONG(rttl, cp); |
---|
188 | sp2 = cp; /* save pointer to length byte */ |
---|
189 | cp += INT16SZ; |
---|
190 | if (rrecp->r_size == 0) { |
---|
191 | if (section == S_UPDATE && rclass != C_ANY) |
---|
192 | return (-1); |
---|
193 | else { |
---|
194 | PUTSHORT(0, sp2); |
---|
195 | continue; |
---|
196 | } |
---|
197 | } |
---|
198 | startp = rrecp->r_data; |
---|
199 | endp = startp + rrecp->r_size - 1; |
---|
200 | /* XXX this should be done centrally. */ |
---|
201 | switch (rrecp->r_type) { |
---|
202 | case T_A: |
---|
203 | if (!getword_str(buf2, sizeof buf2, &startp, endp)) |
---|
204 | return (-1); |
---|
205 | if (!inet_aton(buf2, &ina)) |
---|
206 | return (-1); |
---|
207 | n1 = ntohl(ina.s_addr); |
---|
208 | ShrinkBuffer(INT32SZ); |
---|
209 | PUTLONG(n1, cp); |
---|
210 | break; |
---|
211 | case T_CNAME: |
---|
212 | case T_MB: |
---|
213 | case T_MG: |
---|
214 | case T_MR: |
---|
215 | case T_NS: |
---|
216 | case T_PTR: |
---|
217 | if (!getword_str(buf2, sizeof buf2, &startp, endp)) |
---|
218 | return (-1); |
---|
219 | n = dn_comp(buf2, cp, buflen, dnptrs, lastdnptr); |
---|
220 | if (n < 0) |
---|
221 | return (-1); |
---|
222 | cp += n; |
---|
223 | ShrinkBuffer(n); |
---|
224 | break; |
---|
225 | case T_MINFO: |
---|
226 | case T_SOA: |
---|
227 | case T_RP: |
---|
228 | for (i = 0; i < 2; i++) { |
---|
229 | if (!getword_str(buf2, sizeof buf2, &startp, |
---|
230 | endp)) |
---|
231 | return (-1); |
---|
232 | n = dn_comp(buf2, cp, buflen, |
---|
233 | dnptrs, lastdnptr); |
---|
234 | if (n < 0) |
---|
235 | return (-1); |
---|
236 | cp += n; |
---|
237 | ShrinkBuffer(n); |
---|
238 | } |
---|
239 | if (rrecp->r_type == T_SOA) { |
---|
240 | ShrinkBuffer(5 * INT32SZ); |
---|
241 | while (isspace(*startp) || !*startp) |
---|
242 | startp++; |
---|
243 | if (*startp == '(') { |
---|
244 | multiline = 1; |
---|
245 | startp++; |
---|
246 | } else |
---|
247 | multiline = 0; |
---|
248 | /* serial, refresh, retry, expire, minimum */ |
---|
249 | for (i = 0; i < 5; i++) { |
---|
250 | soanum = getnum_str(&startp, endp); |
---|
251 | if (soanum < 0) |
---|
252 | return (-1); |
---|
253 | PUTLONG(soanum, cp); |
---|
254 | } |
---|
255 | if (multiline) { |
---|
256 | while (isspace(*startp) || !*startp) |
---|
257 | startp++; |
---|
258 | if (*startp != ')') |
---|
259 | return (-1); |
---|
260 | } |
---|
261 | } |
---|
262 | break; |
---|
263 | case T_MX: |
---|
264 | case T_AFSDB: |
---|
265 | case T_RT: |
---|
266 | n = getnum_str(&startp, endp); |
---|
267 | if (n < 0) |
---|
268 | return (-1); |
---|
269 | PUTSHORT(n, cp); |
---|
270 | ShrinkBuffer(INT16SZ); |
---|
271 | if (!getword_str(buf2, sizeof buf2, &startp, endp)) |
---|
272 | return (-1); |
---|
273 | n = dn_comp(buf2, cp, buflen, dnptrs, lastdnptr); |
---|
274 | if (n < 0) |
---|
275 | return (-1); |
---|
276 | cp += n; |
---|
277 | ShrinkBuffer(n); |
---|
278 | break; |
---|
279 | case T_PX: |
---|
280 | n = getnum_str(&startp, endp); |
---|
281 | if (n < 0) |
---|
282 | return (-1); |
---|
283 | PUTSHORT(n, cp); |
---|
284 | ShrinkBuffer(INT16SZ); |
---|
285 | for (i = 0; i < 2; i++) { |
---|
286 | if (!getword_str(buf2, sizeof buf2, &startp, |
---|
287 | endp)) |
---|
288 | return (-1); |
---|
289 | n = dn_comp(buf2, cp, buflen, dnptrs, |
---|
290 | lastdnptr); |
---|
291 | if (n < 0) |
---|
292 | return (-1); |
---|
293 | cp += n; |
---|
294 | ShrinkBuffer(n); |
---|
295 | } |
---|
296 | break; |
---|
297 | case T_WKS: |
---|
298 | case T_HINFO: |
---|
299 | case T_TXT: |
---|
300 | case T_X25: |
---|
301 | case T_ISDN: |
---|
302 | case T_NSAP: |
---|
303 | case T_LOC: |
---|
304 | /* XXX - more fine tuning needed here */ |
---|
305 | ShrinkBuffer(rrecp->r_size); |
---|
306 | memcpy(cp, rrecp->r_data, rrecp->r_size); |
---|
307 | cp += rrecp->r_size; |
---|
308 | break; |
---|
309 | default: |
---|
310 | return (-1); |
---|
311 | } /*switch*/ |
---|
312 | n = (u_int16_t)((cp - sp2) - INT16SZ); |
---|
313 | PUTSHORT(n, sp2); |
---|
314 | } /*for*/ |
---|
315 | |
---|
316 | hp->qdcount = htons(counts[0]); |
---|
317 | hp->ancount = htons(counts[1]); |
---|
318 | hp->nscount = htons(counts[2]); |
---|
319 | hp->arcount = htons(counts[3]); |
---|
320 | return (cp - buf); |
---|
321 | } |
---|
322 | |
---|
323 | /* |
---|
324 | * Get a whitespace delimited word from a string (not file) |
---|
325 | * into buf. modify the start pointer to point after the |
---|
326 | * word in the string. |
---|
327 | */ |
---|
328 | static int |
---|
329 | getword_str(char *buf, int size, u_char **startpp, u_char *endp) { |
---|
330 | char *cp; |
---|
331 | int c; |
---|
332 | |
---|
333 | for (cp = buf; *startpp <= endp; ) { |
---|
334 | c = **startpp; |
---|
335 | if (isspace(c) || c == '\0') { |
---|
336 | if (cp != buf) /* trailing whitespace */ |
---|
337 | break; |
---|
338 | else { /* leading whitespace */ |
---|
339 | (*startpp)++; |
---|
340 | continue; |
---|
341 | } |
---|
342 | } |
---|
343 | (*startpp)++; |
---|
344 | if (cp >= buf+size-1) |
---|
345 | break; |
---|
346 | *cp++ = (u_char)c; |
---|
347 | } |
---|
348 | *cp = '\0'; |
---|
349 | return (cp != buf); |
---|
350 | } |
---|
351 | |
---|
352 | /* |
---|
353 | * Get a whitespace delimited number from a string (not file) into buf |
---|
354 | * update the start pointer to point after the number in the string. |
---|
355 | */ |
---|
356 | static int |
---|
357 | getnum_str(u_char **startpp, u_char *endp) { |
---|
358 | int c, n; |
---|
359 | int seendigit = 0; |
---|
360 | int m = 0; |
---|
361 | |
---|
362 | for (n = 0; *startpp <= endp; ) { |
---|
363 | c = **startpp; |
---|
364 | if (isspace(c) || c == '\0') { |
---|
365 | if (seendigit) /* trailing whitespace */ |
---|
366 | break; |
---|
367 | else { /* leading whitespace */ |
---|
368 | (*startpp)++; |
---|
369 | continue; |
---|
370 | } |
---|
371 | } |
---|
372 | if (c == ';') { |
---|
373 | while ((*startpp <= endp) && |
---|
374 | ((c = **startpp) != '\n')) |
---|
375 | (*startpp)++; |
---|
376 | if (seendigit) |
---|
377 | break; |
---|
378 | continue; |
---|
379 | } |
---|
380 | if (!isdigit(c)) { |
---|
381 | if (c == ')' && seendigit) { |
---|
382 | (*startpp)--; |
---|
383 | break; |
---|
384 | } |
---|
385 | return (-1); |
---|
386 | } |
---|
387 | (*startpp)++; |
---|
388 | n = n * 10 + (c - '0'); |
---|
389 | seendigit = 1; |
---|
390 | } |
---|
391 | return (n + m); |
---|
392 | } |
---|
393 | |
---|
394 | /* |
---|
395 | * Allocate a resource record buffer & save rr info. |
---|
396 | */ |
---|
397 | ns_updrec * |
---|
398 | res_mkupdrec(int section, const char *dname, |
---|
399 | u_int class, u_int type, u_long ttl) { |
---|
400 | ns_updrec *rrecp = (ns_updrec *)calloc(1, sizeof(ns_updrec)); |
---|
401 | |
---|
402 | if (!rrecp || !(rrecp->r_dname = strdup(dname))) |
---|
403 | return (NULL); |
---|
404 | rrecp->r_class = class; |
---|
405 | rrecp->r_type = type; |
---|
406 | rrecp->r_ttl = ttl; |
---|
407 | rrecp->r_section = section; |
---|
408 | return (rrecp); |
---|
409 | } |
---|
410 | |
---|
411 | /* |
---|
412 | * Free a resource record buffer created by res_mkupdrec. |
---|
413 | */ |
---|
414 | void |
---|
415 | res_freeupdrec(ns_updrec *rrecp) { |
---|
416 | /* Note: freeing r_dp is the caller's responsibility. */ |
---|
417 | if (rrecp->r_dname != NULL) |
---|
418 | free(rrecp->r_dname); |
---|
419 | free(rrecp); |
---|
420 | } |
---|