wininet: Fix buffer size query for InternetQueryOption(INTERNET_OPTION_PROXY).
[wine] / dlls / dnsapi / ns_name.c
1 /*
2  * Copyright (c) 1996,1999 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 #include "config.h"
19
20 #ifdef HAVE_RESOLV
21
22 #include <sys/types.h>
23
24 #ifdef HAVE_NETINET_IN_H
25 # include <netinet/in.h>
26 #endif
27 #ifdef HAVE_ARPA_NAMESER_H
28 # include <arpa/nameser.h>
29 #endif
30
31 #include <ctype.h>
32 #include <errno.h>
33 #ifdef HAVE_RESOLV_H
34 # include <resolv.h>
35 #endif
36 #include <string.h>
37
38 /* Data. */
39
40 static const char       digits[] = "0123456789";
41
42 /* Forward. */
43
44 static int              special(int);
45 static int              printable(int);
46
47 /* Public. */
48
49 /*
50  * dns_ns_name_ntop(src, dst, dstsiz)
51  *      Convert an encoded domain name to printable ascii as per RFC1035.
52  * return:
53  *      Number of bytes written to buffer, or -1 (with errno set)
54  * notes:
55  *      The root is returned as "."
56  *      All other domains are returned in non absolute form
57  */
58 static int
59 dns_ns_name_ntop(const u_char *src, char *dst, size_t dstsiz) {
60         const u_char *cp;
61         char *dn, *eom;
62         u_char c;
63         u_int n;
64
65         cp = src;
66         dn = dst;
67         eom = dst + dstsiz;
68
69         while ((n = *cp++) != 0) {
70                 if ((n & NS_CMPRSFLGS) != 0 && n != 0x41) {
71                         /* Some kind of compression pointer. */
72                         return (-1);
73                 }
74                 if (dn != dst) {
75                         if (dn >= eom) {
76                                 return (-1);
77                         }
78                         *dn++ = '.';
79                 }
80
81                 if (n == 0x41) {
82                         n = *cp++ / 8;
83                         if (dn + n * 2 + 4 >= eom) {
84                                 return (-1);
85                         }
86                         *dn++ = '\\';
87                         *dn++ = '[';
88                         *dn++ = 'x';
89
90                         while (n-- > 0) {
91                 unsigned u;
92                                 c = *cp++;
93                                 u = c >> 4;
94                                 *dn++ = u > 9 ? 'a' + u - 10 : '0' + u;
95                                 u = c & 0xf;
96                                 *dn++ = u > 9 ? 'a' + u - 10 : '0' + u;
97                         }
98
99                         *dn++ = ']';
100                         continue;
101                 }
102
103                 if (dn + n >= eom) {
104                         return (-1);
105                 }
106                 for ((void)NULL; n > 0; n--) {
107                         c = *cp++;
108                         if (special(c)) {
109                                 if (dn + 1 >= eom) {
110                                         return (-1);
111                                 }
112                                 *dn++ = '\\';
113                                 *dn++ = (char)c;
114                         } else if (!printable(c)) {
115                                 if (dn + 3 >= eom) {
116                                         return (-1);
117                                 }
118                                 *dn++ = '\\';
119                                 *dn++ = digits[c / 100];
120                                 *dn++ = digits[(c % 100) / 10];
121                                 *dn++ = digits[c % 10];
122                         } else {
123                                 if (dn >= eom) {
124                                         return (-1);
125                                 }
126                                 *dn++ = (char)c;
127                         }
128                 }
129         }
130         if (dn == dst) {
131                 if (dn >= eom) {
132                         return (-1);
133                 }
134                 *dn++ = '.';
135         }
136         if (dn >= eom) {
137                 return (-1);
138         }
139         *dn++ = '\0';
140         return (dn - dst);
141 }
142
143 /*
144  * dns_ns_name_pton(src, dst, dstsiz)
145  *      Convert a ascii string into an encoded domain name as per RFC1035.
146  * return:
147  *      -1 if it fails
148  *      1 if string was fully qualified
149  *      0 is string was not fully qualified
150  * notes:
151  *      Enforces label and domain length limits.
152  */
153
154 int
155 dns_ns_name_pton(const char *src, u_char *dst, size_t dstsiz) {
156         u_char *label, *bp, *eom;
157         int c, n, escaped;
158         char *cp;
159
160         escaped = 0;
161         bp = dst;
162         eom = dst + dstsiz;
163         label = bp++;
164
165         while ((c = *src++) != 0) {
166                 if (escaped) {
167                         if ((cp = strchr(digits, c)) != NULL) {
168                                 n = (cp - digits) * 100;
169                                 if ((c = *src++) == 0 ||
170                                     (cp = strchr(digits, c)) == NULL) {
171                                         return (-1);
172                                 }
173                                 n += (cp - digits) * 10;
174                                 if ((c = *src++) == 0 ||
175                                     (cp = strchr(digits, c)) == NULL) {
176                                         return (-1);
177                                 }
178                                 n += (cp - digits);
179                                 if (n > 255) {
180                                         return (-1);
181                                 }
182                                 c = n;
183                         } else if (c == '[' && label == bp - 1 && *src == 'x') {
184                                 /* Theoretically we would have to handle \[o
185                                    as well but we do not since we do not need
186                                    it internally.  */
187                                 *label = 0x41;
188                                 label = bp++;
189                                 ++src;
190                                 while (isxdigit (*src)) {
191                                         n = *src > '9' ? *src - 'a' + 10 : *src - '0';
192                                         ++src;
193                                         if (! isxdigit(*src)) {
194                                                 return (-1);
195                                         }
196                                         n <<= 4;
197                                         n += *src > '9' ? *src - 'a' + 10 : *src - '0';
198                                         if (bp + 1 >= eom) {
199                                                 return (-1);
200                                         }
201                                         *bp++ = n;
202                                         ++src;
203                                 }
204                                 *label = (bp - label - 1) * 8;
205                                 if (*src++ != ']' || *src++ != '.') {
206                                         return (-1);
207                                 }
208                                 escaped = 0;
209                                 label = bp++;
210                                 if (bp >= eom) {
211                                         return (-1);
212                                 }
213                                 continue;
214                         }
215                         escaped = 0;
216                 } else if (c == '\\') {
217                         escaped = 1;
218                         continue;
219                 } else if (c == '.') {
220                         c = (bp - label - 1);
221                         if ((c & NS_CMPRSFLGS) != 0) {  /* Label too big. */
222                                 return (-1);
223                         }
224                         if (label >= eom) {
225                                 return (-1);
226                         }
227                         *label = c;
228                         /* Fully qualified ? */
229                         if (*src == '\0') {
230                                 if (c != 0) {
231                                         if (bp >= eom) {
232                                                 return (-1);
233                                         }
234                                         *bp++ = '\0';
235                                 }
236                                 if ((bp - dst) > NS_MAXCDNAME) {
237                                         return (-1);
238                                 }
239                                 return (1);
240                         }
241                         if (c == 0 || *src == '.') {
242                                 return (-1);
243                         }
244                         label = bp++;
245                         continue;
246                 }
247                 if (bp >= eom) {
248                         return (-1);
249                 }
250                 *bp++ = (u_char)c;
251         }
252         c = (bp - label - 1);
253         if ((c & NS_CMPRSFLGS) != 0) {          /* Label too big. */
254                 return (-1);
255         }
256         if (label >= eom) {
257                 return (-1);
258         }
259         *label = c;
260         if (c != 0) {
261                 if (bp >= eom) {
262                         return (-1);
263                 }
264                 *bp++ = 0;
265         }
266         if ((bp - dst) > NS_MAXCDNAME) {        /* src too big */
267                 return (-1);
268         }
269         return (0);
270 }
271
272
273 /*
274  * dns_ns_name_unpack(msg, eom, src, dst, dstsiz)
275  *      Unpack a domain name from a message, source may be compressed.
276  * return:
277  *      -1 if it fails, or consumed octets if it succeeds.
278  */
279 static int
280 dns_ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src,
281                u_char *dst, size_t dstsiz)
282 {
283         const u_char *srcp, *dstlim;
284         u_char *dstp;
285         int n, len, checked;
286
287         len = -1;
288         checked = 0;
289         dstp = dst;
290         srcp = src;
291         dstlim = dst + dstsiz;
292         if (srcp < msg || srcp >= eom) {
293                 return (-1);
294         }
295         /* Fetch next label in domain name. */
296         while ((n = *srcp++) != 0) {
297                 /* Check for indirection. */
298                 switch (n & NS_CMPRSFLGS) {
299                 case 0x40:
300                         if (n == 0x41) {
301                                 if (dstp + 1 >= dstlim) {
302                                         return (-1);
303                                 }
304                                 *dstp++ = 0x41;
305                                 n = *srcp++ / 8;
306                                 ++checked;
307                         } else {
308                                 return (-1);            /* flag error */
309                         }
310                         /* FALLTHROUGH */
311                 case 0:
312                         /* Limit checks. */
313                         if (dstp + n + 1 >= dstlim || srcp + n >= eom) {
314                                 return (-1);
315                         }
316                         checked += n + 1;
317                         dstp = memcpy(dstp, srcp - 1, n + 1);
318                         dstp += n + 1;
319                         srcp += n;
320                         break;
321
322                 case NS_CMPRSFLGS:
323                         if (srcp >= eom) {
324                                 return (-1);
325                         }
326                         if (len < 0)
327                                 len = srcp - src + 1;
328                         srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff));
329                         if (srcp < msg || srcp >= eom) {  /* Out of range. */
330                                 return (-1);
331                         }
332                         checked += 2;
333                         /*
334                          * Check for loops in the compressed name;
335                          * if we've looked at the whole message,
336                          * there must be a loop.
337                          */
338                         if (checked >= eom - msg) {
339                                 return (-1);
340                         }
341                         break;
342
343                 default:
344                         return (-1);                    /* flag error */
345                 }
346         }
347         *dstp = '\0';
348         if (len < 0)
349                 len = srcp - src;
350         return (len);
351 }
352
353
354 /*
355  * dns_ns_name_uncompress(msg, eom, src, dst, dstsiz)
356  *      Expand compressed domain name to presentation format.
357  * return:
358  *      Number of bytes read out of `src', or -1 (with errno set).
359  * note:
360  *      Root domain returns as "." not "".
361  */
362 int
363 dns_ns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src,
364                    char *dst, size_t dstsiz)
365 {
366         u_char tmp[NS_MAXCDNAME];
367         int n;
368
369         if ((n = dns_ns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1)
370                 return (-1);
371         if (dns_ns_name_ntop(tmp, dst, dstsiz) == -1)
372                 return (-1);
373         return (n);
374 }
375
376
377 /*
378  * dns_ns_name_skip(ptrptr, eom)
379  *      Advance *ptrptr to skip over the compressed name it points at.
380  * return:
381  *      0 on success, -1 (with errno set) on failure.
382  */
383 int
384 dns_ns_name_skip(const u_char **ptrptr, const u_char *eom) {
385         const u_char *cp;
386         u_int n;
387
388         cp = *ptrptr;
389         while (cp < eom && (n = *cp++) != 0) {
390                 /* Check for indirection. */
391                 switch (n & NS_CMPRSFLGS) {
392                 case 0:                 /* normal case, n == len */
393                         cp += n;
394                         continue;
395                 case NS_CMPRSFLGS:      /* indirection */
396                         cp++;
397                         break;
398                 default:                /* illegal type */
399                         return (-1);
400                 }
401                 break;
402         }
403         if (cp > eom) {
404                 return (-1);
405         }
406         *ptrptr = cp;
407         return (0);
408 }
409
410 /* Private. */
411
412 /*
413  * special(ch)
414  *      Thinking in noninternationalized USASCII (per the DNS spec),
415  *      is this character special ("in need of quoting") ?
416  * return:
417  *      boolean.
418  */
419 static int
420 special(int ch) {
421         switch (ch) {
422         case 0x22: /* '"' */
423         case 0x2E: /* '.' */
424         case 0x3B: /* ';' */
425         case 0x5C: /* '\\' */
426         /* Special modifiers in zone files. */
427         case 0x40: /* '@' */
428         case 0x24: /* '$' */
429                 return (1);
430         default:
431                 return (0);
432         }
433 }
434
435 /*
436  * printable(ch)
437  *      Thinking in noninternationalized USASCII (per the DNS spec),
438  *      is this character visible and not a space when printed ?
439  * return:
440  *      boolean.
441  */
442 static int
443 printable(int ch) {
444         return (ch > 0x20 && ch < 0x7f);
445 }
446
447 #endif  /* HAVE_RESOLV */