You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

379 lines
9.7 KiB

  1. /* udns_dn.c
  2. domain names manipulation routines
  3. Copyright (C) 2005 Michael Tokarev <mjt@corpit.ru>
  4. This file is part of UDNS library, an async DNS stub resolver.
  5. This library is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU Lesser General Public
  7. License as published by the Free Software Foundation; either
  8. version 2.1 of the License, or (at your option) any later version.
  9. This library is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. Lesser General Public License for more details.
  13. You should have received a copy of the GNU Lesser General Public
  14. License along with this library, in file named COPYING.LGPL; if not,
  15. write to the Free Software Foundation, Inc., 59 Temple Place,
  16. Suite 330, Boston, MA 02111-1307 USA
  17. */
  18. #include <string.h>
  19. #include "udns.h"
  20. unsigned dns_dnlen(dnscc_t *dn) {
  21. register dnscc_t *d = dn;
  22. while(*d)
  23. d += 1 + *d;
  24. return (unsigned)(d - dn) + 1;
  25. }
  26. unsigned dns_dnlabels(register dnscc_t *dn) {
  27. register unsigned l = 0;
  28. while(*dn)
  29. ++l, dn += 1 + *dn;
  30. return l;
  31. }
  32. unsigned dns_dnequal(register dnscc_t *dn1, register dnscc_t *dn2) {
  33. register unsigned c;
  34. dnscc_t *dn = dn1;
  35. for(;;) {
  36. if ((c = *dn1++) != *dn2++)
  37. return 0;
  38. if (!c)
  39. return (unsigned)(dn1 - dn);
  40. while(c--) {
  41. if (DNS_DNLC(*dn1) != DNS_DNLC(*dn2))
  42. return 0;
  43. ++dn1; ++dn2;
  44. }
  45. }
  46. }
  47. unsigned
  48. dns_dntodn(dnscc_t *sdn, dnsc_t *ddn, unsigned ddnsiz) {
  49. unsigned sdnlen = dns_dnlen(sdn);
  50. if (ddnsiz < sdnlen)
  51. return 0;
  52. memcpy(ddn, sdn, sdnlen);
  53. return sdnlen;
  54. }
  55. int
  56. dns_ptodn(const char *name, unsigned namelen,
  57. dnsc_t *dn, unsigned dnsiz, int *isabs)
  58. {
  59. dnsc_t *dp; /* current position in dn (len byte first) */
  60. dnsc_t *const de /* end of dn: last byte that can be filled up */
  61. = dn + (dnsiz >= DNS_MAXDN ? DNS_MAXDN : dnsiz) - 1;
  62. dnscc_t *np = (dnscc_t *)name;
  63. dnscc_t *ne = np + (namelen ? namelen : strlen((char*)np));
  64. dnsc_t *llab; /* start of last label (llab[-1] will be length) */
  65. unsigned c; /* next input character, or length of last label */
  66. if (!dnsiz)
  67. return 0;
  68. dp = llab = dn + 1;
  69. while(np < ne) {
  70. if (*np == '.') { /* label delimiter */
  71. c = dp - llab; /* length of the label */
  72. if (!c) { /* empty label */
  73. if (np == (dnscc_t *)name && np + 1 == ne) {
  74. /* special case for root dn, aka `.' */
  75. ++np;
  76. break;
  77. }
  78. return -1; /* zero label */
  79. }
  80. if (c > DNS_MAXLABEL)
  81. return -1; /* label too long */
  82. llab[-1] = (dnsc_t)c; /* update len of last label */
  83. llab = ++dp; /* start new label, llab[-1] will be len of it */
  84. ++np;
  85. continue;
  86. }
  87. /* check whenever we may put out one more byte */
  88. if (dp >= de) /* too long? */
  89. return dnsiz >= DNS_MAXDN ? -1 : 0;
  90. if (*np != '\\') { /* non-escape, simple case */
  91. *dp++ = *np++;
  92. continue;
  93. }
  94. /* handle \-style escape */
  95. /* note that traditionally, domain names (gethostbyname etc)
  96. * used decimal \dd notation, not octal \ooo (RFC1035), so
  97. * we're following this tradition here.
  98. */
  99. if (++np == ne)
  100. return -1; /* bad escape */
  101. else if (*np >= '0' && *np <= '9') { /* decimal number */
  102. /* we allow not only exactly 3 digits as per RFC1035,
  103. * but also 2 or 1, for better usability. */
  104. c = *np++ - '0';
  105. if (np < ne && *np >= '0' && *np <= '9') { /* 2digits */
  106. c = c * 10 + *np++ - '0';
  107. if (np < ne && *np >= '0' && *np <= '9') {
  108. c = c * 10 + *np++ - '0';
  109. if (c > 255)
  110. return -1; /* bad escape */
  111. }
  112. }
  113. }
  114. else
  115. c = *np++;
  116. *dp++ = (dnsc_t)c; /* place next out byte */
  117. }
  118. if ((c = dp - llab) > DNS_MAXLABEL)
  119. return -1; /* label too long */
  120. if ((llab[-1] = (dnsc_t)c) != 0) {
  121. *dp++ = 0;
  122. if (isabs)
  123. *isabs = 0;
  124. }
  125. else if (isabs)
  126. *isabs = 1;
  127. return dp - dn;
  128. }
  129. dnscc_t dns_inaddr_arpa_dn[14] = "\07in-addr\04arpa";
  130. dnsc_t *
  131. dns_a4todn_(const struct in_addr *addr, dnsc_t *dn, dnsc_t *dne) {
  132. const unsigned char *s = ((const unsigned char *)addr) + 4;
  133. while(s > (const unsigned char *)addr) {
  134. unsigned n = *--s;
  135. dnsc_t *p = dn + 1;
  136. if (n > 99) {
  137. if (p + 2 > dne) return 0;
  138. *p++ = n / 100 + '0';
  139. *p++ = (n % 100 / 10) + '0';
  140. *p = n % 10 + '0';
  141. }
  142. else if (n > 9) {
  143. if (p + 1 > dne) return 0;
  144. *p++ = n / 10 + '0';
  145. *p = n % 10 + '0';
  146. }
  147. else {
  148. if (p > dne) return 0;
  149. *p = n + '0';
  150. }
  151. *dn = p - dn;
  152. dn = p + 1;
  153. }
  154. return dn;
  155. }
  156. int dns_a4todn(const struct in_addr *addr, dnscc_t *tdn,
  157. dnsc_t *dn, unsigned dnsiz) {
  158. dnsc_t *dne = dn + (dnsiz > DNS_MAXDN ? DNS_MAXDN : dnsiz);
  159. dnsc_t *p;
  160. unsigned l;
  161. p = dns_a4todn_(addr, dn, dne);
  162. if (!p) return 0;
  163. if (!tdn)
  164. tdn = dns_inaddr_arpa_dn;
  165. l = dns_dnlen(tdn);
  166. if (p + l > dne) return dnsiz >= DNS_MAXDN ? -1 : 0;
  167. memcpy(p, tdn, l);
  168. return (p + l) - dn;
  169. }
  170. int dns_a4ptodn(const struct in_addr *addr, const char *tname,
  171. dnsc_t *dn, unsigned dnsiz) {
  172. dnsc_t *p;
  173. int r;
  174. if (!tname)
  175. return dns_a4todn(addr, NULL, dn, dnsiz);
  176. p = dns_a4todn_(addr, dn, dn + dnsiz);
  177. if (!p) return 0;
  178. r = dns_sptodn(tname, p, dnsiz - (p - dn));
  179. return r != 0 ? r : dnsiz >= DNS_MAXDN ? -1 : 0;
  180. }
  181. dnscc_t dns_ip6_arpa_dn[10] = "\03ip6\04arpa";
  182. dnsc_t *
  183. dns_a6todn_(const struct in6_addr *addr, dnsc_t *dn, dnsc_t *dne) {
  184. const unsigned char *s = ((const unsigned char *)addr) + 16;
  185. if (dn + 64 > dne) return 0;
  186. while(s > (const unsigned char *)addr) {
  187. unsigned n = *--s & 0x0f;
  188. *dn++ = 1;
  189. *dn++ = n > 9 ? n + 'a' - 10 : n + '0';
  190. *dn++ = 1;
  191. n = *s >> 4;
  192. *dn++ = n > 9 ? n + 'a' - 10 : n + '0';
  193. }
  194. return dn;
  195. }
  196. int dns_a6todn(const struct in6_addr *addr, dnscc_t *tdn,
  197. dnsc_t *dn, unsigned dnsiz) {
  198. dnsc_t *dne = dn + (dnsiz > DNS_MAXDN ? DNS_MAXDN : dnsiz);
  199. dnsc_t *p;
  200. unsigned l;
  201. p = dns_a6todn_(addr, dn, dne);
  202. if (!p) return 0;
  203. if (!tdn)
  204. tdn = dns_ip6_arpa_dn;
  205. l = dns_dnlen(tdn);
  206. if (p + l > dne) return dnsiz >= DNS_MAXDN ? -1 : 0;
  207. memcpy(p, tdn, l);
  208. return (p + l) - dn;
  209. }
  210. int dns_a6ptodn(const struct in6_addr *addr, const char *tname,
  211. dnsc_t *dn, unsigned dnsiz) {
  212. dnsc_t *p;
  213. int r;
  214. if (!tname)
  215. return dns_a6todn(addr, NULL, dn, dnsiz);
  216. p = dns_a6todn_(addr, dn, dn + dnsiz);
  217. if (!p) return 0;
  218. r = dns_sptodn(tname, p, dnsiz - (p - dn));
  219. return r != 0 ? r : dnsiz >= DNS_MAXDN ? -1 : 0;
  220. }
  221. /* return size of buffer required to convert the dn into asciiz string.
  222. * Keep in sync with dns_dntop() below.
  223. */
  224. unsigned dns_dntop_size(dnscc_t *dn) {
  225. unsigned size = 0; /* the size reqd */
  226. dnscc_t *le; /* label end */
  227. while(*dn) {
  228. /* *dn is the length of the next label, non-zero */
  229. if (size)
  230. ++size; /* for the dot */
  231. le = dn + *dn + 1;
  232. ++dn;
  233. do {
  234. switch(*dn) {
  235. case '.':
  236. case '\\':
  237. /* Special modifiers in zone files. */
  238. case '"':
  239. case ';':
  240. case '@':
  241. case '$':
  242. size += 2;
  243. break;
  244. default:
  245. if (*dn <= 0x20 || *dn >= 0x7f)
  246. /* \ddd decimal notation */
  247. size += 4;
  248. else
  249. size += 1;
  250. }
  251. } while(++dn < le);
  252. }
  253. size += 1; /* zero byte at the end - string terminator */
  254. return size > DNS_MAXNAME ? 0 : size;
  255. }
  256. /* Convert the dn into asciiz string.
  257. * Keep in sync with dns_dntop_size() above.
  258. */
  259. int dns_dntop(dnscc_t *dn, char *name, unsigned namesiz) {
  260. char *np = name; /* current name ptr */
  261. char *const ne = name + namesiz; /* end of name */
  262. dnscc_t *le; /* label end */
  263. while(*dn) {
  264. /* *dn is the length of the next label, non-zero */
  265. if (np != name) {
  266. if (np >= ne) goto toolong;
  267. *np++ = '.';
  268. }
  269. le = dn + *dn + 1;
  270. ++dn;
  271. do {
  272. switch(*dn) {
  273. case '.':
  274. case '\\':
  275. /* Special modifiers in zone files. */
  276. case '"':
  277. case ';':
  278. case '@':
  279. case '$':
  280. if (np + 2 > ne) goto toolong;
  281. *np++ = '\\';
  282. *np++ = *dn;
  283. break;
  284. default:
  285. if (*dn <= 0x20 || *dn >= 0x7f) {
  286. /* \ddd decimal notation */
  287. if (np + 4 >= ne) goto toolong;
  288. *np++ = '\\';
  289. *np++ = '0' + (*dn / 100);
  290. *np++ = '0' + ((*dn % 100) / 10);
  291. *np++ = '0' + (*dn % 10);
  292. }
  293. else {
  294. if (np >= ne) goto toolong;
  295. *np++ = *dn;
  296. }
  297. }
  298. } while(++dn < le);
  299. }
  300. if (np >= ne) goto toolong;
  301. *np++ = '\0';
  302. return np - name;
  303. toolong:
  304. return namesiz >= DNS_MAXNAME ? -1 : 0;
  305. }
  306. #if 0
  307. #include <stdio.h>
  308. #include <stdlib.h>
  309. int main(int argc, char **argv) {
  310. int i;
  311. int sz;
  312. dnsc_t dn[DNS_MAXDN+10];
  313. dnsc_t *dl, *dp;
  314. int isabs;
  315. sz = (argc > 1) ? atoi(argv[1]) : 0;
  316. for(i = 2; i < argc; ++i) {
  317. int r = dns_ptodn(argv[i], 0, dn, sz, &isabs);
  318. printf("%s: ", argv[i]);
  319. if (r < 0) printf("error\n");
  320. else if (!r) printf("buffer too small\n");
  321. else {
  322. printf("len=%d dnlen=%d size=%d name:",
  323. r, dns_dnlen(dn), dns_dntop_size(dn));
  324. dl = dn;
  325. while(*dl) {
  326. printf(" %d=", *dl);
  327. dp = dl + 1;
  328. dl = dp + *dl;
  329. while(dp < dl) {
  330. if (*dp <= ' ' || *dp >= 0x7f)
  331. printf("\\%03d", *dp);
  332. else if (*dp == '.' || *dp == '\\')
  333. printf("\\%c", *dp);
  334. else
  335. putchar(*dp);
  336. ++dp;
  337. }
  338. }
  339. if (isabs) putchar('.');
  340. putchar('\n');
  341. }
  342. }
  343. return 0;
  344. }
  345. #endif /* TEST */