1/** @file
2  Copyright (c) 1999 - 2014, Intel Corporation. All rights reserved.<BR>
3  This program and the accompanying materials are licensed and made available
4  under the terms and conditions of the BSD License which accompanies this
5  distribution.  The full text of the license may be found at
6  http://opensource.org/licenses/bsd-license.php.
7
8  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
9  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
10**/
11/*
12 * Copyright (c) 1996 by Internet Software Consortium.
13 *
14 * Permission to use, copy, modify, and distribute this software for any
15 * purpose with or without fee is hereby granted, provided that the above
16 * copyright notice and this permission notice appear in all copies.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
19 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
21 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
22 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
23 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
24 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
25 * SOFTWARE.
26 */
27
28/*
29 * Portions copyright (c) 1999, 2000
30 * Intel Corporation.
31 * All rights reserved.
32 *
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
35 * are met:
36 *
37 * 1. Redistributions of source code must retain the above copyright
38 *    notice, this list of conditions and the following disclaimer.
39 *
40 * 2. Redistributions in binary form must reproduce the above copyright
41 *    notice, this list of conditions and the following disclaimer in the
42 *    documentation and/or other materials provided with the distribution.
43 *
44 * 3. All advertising materials mentioning features or use of this software
45 *    must display the following acknowledgement:
46 *
47 *    This product includes software developed by Intel Corporation and
48 *    its contributors.
49 *
50 * 4. Neither the name of Intel Corporation or its contributors may be
51 *    used to endorse or promote products derived from this software
52 *    without specific prior written permission.
53 *
54 * THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION AND CONTRIBUTORS ``AS IS''
55 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
56 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
57 * ARE DISCLAIMED.  IN NO EVENT SHALL INTEL CORPORATION OR CONTRIBUTORS BE
58 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
59 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
60 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
61 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
62 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
63 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
64 * THE POSSIBILITY OF SUCH DAMAGE.
65 *
66 */
67
68/*
69 * Based on the Dynamic DNS reference implementation by Viraj Bais
70 * <viraj_bais@ccm.fm.intel.com>
71 */
72
73#include <sys/types.h>
74#include <sys/param.h>
75
76#include <netinet/in.h>
77#include <arpa/nameser.h>
78#include <arpa/inet.h>
79
80#include <errno.h>
81#include <limits.h>
82#include <netdb.h>
83#include <resolv.h>
84#include <stdio.h>
85#include <stdlib.h>
86#include <string.h>
87#include <unistd.h>
88#include <ctype.h>
89
90#include "res_config.h"
91
92static int getnum_str(u_char **, u_char *);
93static int getword_str(char *, int, u_char **, u_char *);
94
95#define ShrinkBuffer(x)  if ((buflen -= x) < 0) return (-2);
96
97/*
98 * Form update packets.
99 * Returns the size of the resulting packet if no error
100 * On error,
101 *  returns -1 if error in reading a word/number in rdata
102 *         portion for update packets
103 *      -2 if length of buffer passed is insufficient
104 *      -3 if zone section is not the first section in
105 *         the linked list, or section order has a problem
106 *      -4 on a number overflow
107 *      -5 unknown operation or no records
108 */
109int
110res_mkupdate(ns_updrec *rrecp_in, u_char *buf, int buflen) {
111    ns_updrec *rrecp_start = rrecp_in;
112    HEADER *hp;
113    u_char *cp, *sp2, *startp, *endp;
114    int n, i, soanum, multiline;
115    ns_updrec *rrecp;
116    struct in_addr ina;
117        char buf2[MAXDNAME];
118    int section, numrrs = 0, counts[ns_s_max];
119    u_int16_t rtype, rclass;
120    u_int32_t n1, rttl;
121    u_char *dnptrs[20], **dpp, **lastdnptr;
122
123    if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
124        h_errno = NETDB_INTERNAL;
125        return (-1);
126    }
127
128    /*
129     * Initialize header fields.
130     */
131    if ((buf == NULL) || (buflen < HFIXEDSZ))
132        return (-1);
133    memset(buf, 0, HFIXEDSZ);
134    hp = (HEADER *) buf;
135    hp->id = htons(++_res.id);
136    hp->opcode = ns_o_update;
137    hp->rcode = NOERROR;
138    cp = buf + HFIXEDSZ;
139    buflen -= HFIXEDSZ;
140    dpp = dnptrs;
141    *dpp++ = buf;
142    *dpp++ = NULL;
143    lastdnptr = dnptrs + sizeof dnptrs / sizeof dnptrs[0];
144
145    if (rrecp_start == NULL)
146        return (-5);
147    else if (rrecp_start->r_section != S_ZONE)
148        return (-3);
149
150    memset(counts, 0, sizeof counts);
151    for (rrecp = rrecp_start; rrecp; rrecp = rrecp->r_grpnext) {
152        numrrs++;
153                section = rrecp->r_section;
154        if (section < 0 || section >= ns_s_max)
155            return (-1);
156        counts[section]++;
157        for (i = section + 1; i < ns_s_max; i++)
158            if (counts[i])
159                return (-3);
160        rtype = rrecp->r_type;
161        rclass = rrecp->r_class;
162        rttl = rrecp->r_ttl;
163        /* overload class and type */
164        if (section == S_PREREQ) {
165            rttl = 0;
166            switch (rrecp->r_opcode) {
167            case YXDOMAIN:
168                rclass = C_ANY;
169                rtype = T_ANY;
170                rrecp->r_size = 0;
171                break;
172            case NXDOMAIN:
173                rclass = C_NONE;
174                rtype = T_ANY;
175                rrecp->r_size = 0;
176                break;
177            case NXRRSET:
178                rclass = C_NONE;
179                rrecp->r_size = 0;
180                break;
181            case YXRRSET:
182                if (rrecp->r_size == 0)
183                    rclass = C_ANY;
184                break;
185            default:
186                fprintf(stderr,
187                    "res_mkupdate: incorrect opcode: %d\n",
188                    rrecp->r_opcode);
189                fflush(stderr);
190                return (-1);
191            }
192        } else if (section == S_UPDATE) {
193            switch (rrecp->r_opcode) {
194            case DELETE:
195                rclass = rrecp->r_size == 0 ? C_ANY : C_NONE;
196                break;
197            case ADD:
198                break;
199            default:
200                fprintf(stderr,
201                    "res_mkupdate: incorrect opcode: %d\n",
202                    rrecp->r_opcode);
203                fflush(stderr);
204                return (-1);
205            }
206        }
207
208        /*
209         * XXX  appending default domain to owner name is omitted,
210         *  fqdn must be provided
211         */
212        if ((n = dn_comp(rrecp->r_dname, cp, buflen, dnptrs,
213                 lastdnptr)) < 0)
214            return (-1);
215        cp += n;
216        ShrinkBuffer(n + 2*INT16SZ);
217        PUTSHORT(rtype, cp);
218        PUTSHORT(rclass, cp);
219        if (section == S_ZONE) {
220            if (numrrs != 1 || rrecp->r_type != T_SOA)
221                return (-3);
222            continue;
223        }
224        ShrinkBuffer(INT32SZ + INT16SZ);
225        PUTLONG(rttl, cp);
226        sp2 = cp;  /* save pointer to length byte */
227        cp += INT16SZ;
228        if (rrecp->r_size == 0) {
229            if (section == S_UPDATE && rclass != C_ANY)
230                return (-1);
231            else {
232                PUTSHORT(0, sp2);
233                continue;
234            }
235        }
236        startp = rrecp->r_data;
237        endp = startp + rrecp->r_size - 1;
238        /* XXX this should be done centrally. */
239        switch (rrecp->r_type) {
240        case T_A:
241            if (!getword_str(buf2, sizeof buf2, &startp, endp))
242                return (-1);
243            if (!inet_aton(buf2, &ina))
244                return (-1);
245            n1 = ntohl(ina.s_addr);
246            ShrinkBuffer(INT32SZ);
247            PUTLONG(n1, cp);
248            break;
249        case T_CNAME:
250        case T_MB:
251        case T_MG:
252        case T_MR:
253        case T_NS:
254        case T_PTR:
255            if (!getword_str(buf2, sizeof buf2, &startp, endp))
256                return (-1);
257            n = dn_comp(buf2, cp, buflen, dnptrs, lastdnptr);
258            if (n < 0)
259                return (-1);
260            cp += n;
261            ShrinkBuffer(n);
262            break;
263        case T_MINFO:
264        case T_SOA:
265        case T_RP:
266            for (i = 0; i < 2; i++) {
267                if (!getword_str(buf2, sizeof buf2, &startp,
268                         endp))
269                return (-1);
270                n = dn_comp(buf2, cp, buflen,
271                        dnptrs, lastdnptr);
272                if (n < 0)
273                    return (-1);
274                cp += n;
275                ShrinkBuffer(n);
276            }
277            if (rrecp->r_type == T_SOA) {
278                ShrinkBuffer(5 * INT32SZ);
279                while (isspace(*startp) || !*startp)
280                    startp++;
281                if (*startp == '(') {
282                    multiline = 1;
283                    startp++;
284                } else
285                    multiline = 0;
286                /* serial, refresh, retry, expire, minimum */
287                for (i = 0; i < 5; i++) {
288                    soanum = getnum_str(&startp, endp);
289                    if (soanum < 0)
290                        return (-1);
291                    PUTLONG(soanum, cp);
292                }
293                if (multiline) {
294                    while (isspace(*startp) || !*startp)
295                        startp++;
296                    if (*startp != ')')
297                        return (-1);
298                }
299            }
300            break;
301        case T_MX:
302        case T_AFSDB:
303        case T_RT:
304            n = getnum_str(&startp, endp);
305            if (n < 0)
306                return (-1);
307            PUTSHORT(n, cp);
308            ShrinkBuffer(INT16SZ);
309            if (!getword_str(buf2, sizeof buf2, &startp, endp))
310                return (-1);
311            n = dn_comp(buf2, cp, buflen, dnptrs, lastdnptr);
312            if (n < 0)
313                return (-1);
314            cp += n;
315            ShrinkBuffer(n);
316            break;
317        case T_PX:
318            n = getnum_str(&startp, endp);
319            if (n < 0)
320                return (-1);
321            PUTSHORT(n, cp);
322            ShrinkBuffer(INT16SZ);
323            for (i = 0; i < 2; i++) {
324                if (!getword_str(buf2, sizeof buf2, &startp,
325                         endp))
326                    return (-1);
327                n = dn_comp(buf2, cp, buflen, dnptrs,
328                        lastdnptr);
329                if (n < 0)
330                    return (-1);
331                cp += n;
332                ShrinkBuffer(n);
333            }
334            break;
335        case T_WKS:
336        case T_HINFO:
337        case T_TXT:
338        case T_X25:
339        case T_ISDN:
340        case T_NSAP:
341        case T_LOC:
342            /* XXX - more fine tuning needed here */
343            ShrinkBuffer(rrecp->r_size);
344            memcpy(cp, rrecp->r_data, rrecp->r_size);
345            cp += rrecp->r_size;
346            break;
347        default:
348            return (-1);
349        } /*switch*/
350        n = (u_int16_t)((cp - sp2) - INT16SZ);
351        PUTSHORT(n, sp2);
352    } /*for*/
353
354    hp->qdcount = htons(counts[0]);
355    hp->ancount = htons(counts[1]);
356    hp->nscount = htons(counts[2]);
357    hp->arcount = htons(counts[3]);
358    return ((int)(cp - buf));
359}
360
361/*
362 * Get a whitespace delimited word from a string (not file)
363 * into buf. modify the start pointer to point after the
364 * word in the string.
365 */
366static int
367getword_str(char *buf, int size, u_char **startpp, u_char *endp) {
368        char *cp;
369        int c;
370
371        for (cp = buf; *startpp <= endp; ) {
372                c = **startpp;
373                if (isspace(c) || c == '\0') {
374                        if (cp != buf) /* trailing whitespace */
375                                break;
376                        else { /* leading whitespace */
377                                (*startpp)++;
378                                continue;
379                        }
380                }
381                (*startpp)++;
382                if (cp >= buf+size-1)
383                        break;
384                *cp++ = (u_char)c;
385        }
386        *cp = '\0';
387        return (cp != buf);
388}
389
390/*
391 * Get a whitespace delimited number from a string (not file) into buf
392 * update the start pointer to point after the number in the string.
393 */
394static int
395getnum_str(u_char **startpp, u_char *endp) {
396        int c;
397        int n;
398        int seendigit = 0;
399        int m = 0;
400
401        for (n = 0; *startpp <= endp; ) {
402                c = **startpp;
403                if (isspace(c) || c == '\0') {
404                        if (seendigit) /* trailing whitespace */
405                                break;
406                        else { /* leading whitespace */
407                                (*startpp)++;
408                                continue;
409                        }
410                }
411                if (c == ';') {
412                        while ((*startpp <= endp) &&
413                   ((c = **startpp) != '\n'))
414                    (*startpp)++;
415                        if (seendigit)
416                                break;
417                        continue;
418                }
419                if (!isdigit(c)) {
420                        if (c == ')' && seendigit) {
421                                (*startpp)--;
422                                break;
423                        }
424            return (-1);
425                }
426                (*startpp)++;
427                n = n * 10 + (c - '0');
428                seendigit = 1;
429        }
430        return (n + m);
431}
432
433/*
434 * Allocate a resource record buffer & save rr info.
435 */
436ns_updrec *
437res_mkupdrec(int section, const char *dname,
438         u_int class, u_int type, u_long ttl) {
439    ns_updrec *rrecp = (ns_updrec *)calloc(1, sizeof(ns_updrec));
440
441    if (!rrecp || !(rrecp->r_dname = strdup(dname)))
442        return (NULL);
443    rrecp->r_class = (u_int16_t)class;
444    rrecp->r_type = (u_int16_t)type;
445    rrecp->r_ttl = (u_int32_t)ttl;
446    rrecp->r_section = (u_int8_t)section;
447    return (rrecp);
448}
449
450/*
451 * Free a resource record buffer created by res_mkupdrec.
452 */
453void
454res_freeupdrec(ns_updrec *rrecp) {
455    /* Note: freeing r_dp is the caller's responsibility. */
456    if (rrecp->r_dname != NULL)
457        free(rrecp->r_dname);
458    free(rrecp);
459}
460