1 
/* $Id: udns_dn.c,v 1.7 2006/11/28 22:45:20 mjt Exp $ 

2 
domain names manipulation routines 

3 


4 
Copyright (C) 2005 Michael Tokarev <mjt@corpit.ru> 

5 
This file is part of UDNS library, an async DNS stub resolver. 

6 


7 
This library is free software; you can redistribute it and/or 

8 
modify it under the terms of the GNU Lesser General Public 

9 
License as published by the Free Software Foundation; either 

10 
version 2.1 of the License, or (at your option) any later version. 

11 


12 
This library is distributed in the hope that it will be useful, 

13 
but WITHOUT ANY WARRANTY; without even the implied warranty of 

14 
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 

15 
Lesser General Public License for more details. 

16 


17 
You should have received a copy of the GNU Lesser General Public 

18 
License along with this library, in file named COPYING.LGPL; if not, 

19 
write to the Free Software Foundation, Inc., 59 Temple Place, 

20 
Suite 330, Boston, MA 021111307 USA 

21 


22 
*/ 

23 


24 
#include <string.h> 

25 
#include "udns.h" 

26 


27 
unsigned dns_dnlen(dnscc_t *dn) { 

28 
register dnscc_t *d = dn; 

29 
while(*d) 

30 
d += 1 + *d; 

31 
return (unsigned)(d  dn) + 1; 

32 
} 

33 


34 
unsigned dns_dnlabels(register dnscc_t *dn) { 

35 
register unsigned l = 0; 

36 
while(*dn) 

37 
++l, dn += 1 + *dn; 

38 
return l; 

39 
} 

40 


41 
unsigned dns_dnequal(register dnscc_t *dn1, register dnscc_t *dn2) { 

42 
register unsigned c; 

43 
dnscc_t *dn = dn1; 

44 
for(;;) { 

45 
if ((c = *dn1++) != *dn2++) 

46 
return 0; 

47 
if (!c) 

48 
return (unsigned)(dn1  dn); 

49 
while(c) { 

50 
if (DNS_DNLC(*dn1) != DNS_DNLC(*dn2)) 

51 
return 0; 

52 
++dn1; ++dn2; 

53 
} 

54 
} 

55 
} 

56 


57 
unsigned 

58 
dns_dntodn(dnscc_t *sdn, dnsc_t *ddn, unsigned ddnsiz) { 

59 
unsigned sdnlen = dns_dnlen(sdn); 

60 
if (ddnsiz < sdnlen) 

61 
return 0; 

62 
memcpy(ddn, sdn, sdnlen); 

63 
return sdnlen; 

64 
} 

65 


66 
int 

67 
dns_ptodn(const char *name, unsigned namelen, 

68 
dnsc_t *dn, unsigned dnsiz, int *isabs) 

69 
{ 

70 
dnsc_t *dp; /* current position in dn (len byte first) */ 

71 
dnsc_t *const de /* end of dn: last byte that can be filled up */ 

72 
= dn + (dnsiz >= DNS_MAXDN ? DNS_MAXDN : dnsiz)  1; 

73 
dnscc_t *np = (dnscc_t *)name; 

74 
dnscc_t *ne = np + (namelen ? namelen : strlen((char*)np)); 

75 
dnsc_t *llab; /* start of last label (llab[1] will be length) */ 

76 
unsigned c; /* next input character, or length of last label */ 

77 


78 
if (!dnsiz) 

79 
return 0; 

80 
dp = llab = dn + 1; 

81 


82 
while(np < ne) { 

83 


84 
if (*np == '.') { /* label delimiter */ 

85 
c = dp  llab; /* length of the label */ 

86 
if (!c) { /* empty label */ 

87 
if (np == (dnscc_t *)name && np + 1 == ne) { 

88 
/* special case for root dn, aka `.' */ 

89 
/* ++np; 

90 
... incrementing a variable that isn't subsequently looked at */ 

91 
break; 

92 
} 

93 
return 1; /* zero label */ 

94 
} 

95 
if (c > DNS_MAXLABEL) 

96 
return 1; /* label too long */ 

97 
llab[1] = (dnsc_t)c; /* update len of last label */ 

98 
llab = ++dp; /* start new label, llab[1] will be len of it */ 

99 
++np; 

100 
continue; 

101 
} 

102 


103 
/* check whenever we may put out one more byte */ 

104 
if (dp >= de) /* too long? */ 

105 
return dnsiz >= DNS_MAXDN ? 1 : 0; 

106 
if (*np != '\\') { /* nonescape, simple case */ 

107 
*dp++ = *np++; 

108 
continue; 

109 
} 

110 
/* handle \style escape */ 

111 
/* note that traditionally, domain names (gethostbyname etc) 

112 
* used decimal \dd notation, not octal \ooo (RFC1035), so 

113 
* we're following this tradition here. 

114 
*/ 

115 
if (++np == ne) 

116 
return 1; /* bad escape */ 

117 
else if (*np >= '0' && *np <= '9') { /* decimal number */ 

118 
/* we allow not only exactly 3 digits as per RFC1035, 

119 
* but also 2 or 1, for better usability. */ 

120 
c = *np++  '0'; 

121 
if (np < ne && *np >= '0' && *np <= '9') { /* 2digits */ 

122 
c = c * 10 + *np++  '0'; 

123 
if (np < ne && *np >= '0' && *np <= '9') { 

124 
c = c * 10 + *np++  '0'; 

125 
if (c > 255) 

126 
return 1; /* bad escape */ 

127 
} 

128 
} 

129 
} 

130 
else 

131 
c = *np++; 

132 
*dp++ = (dnsc_t)c; /* place next out byte */ 

133 
} 

134 


135 
if ((c = dp  llab) > DNS_MAXLABEL) 

136 
return 1; /* label too long */ 

137 
if ((llab[1] = (dnsc_t)c) != 0) { 

138 
*dp++ = 0; 

139 
if (isabs) 

140 
*isabs = 0; 

141 
} 

142 
else if (isabs) 

143 
*isabs = 1; 

144 


145 
return dp  dn; 

146 
} 

147 


148 
dnscc_t dns_inaddr_arpa_dn[14] = "\07inaddr\04arpa"; 

149 


150 
dnsc_t * 

151 
dns_a4todn_(const struct in_addr *addr, dnsc_t *dn, dnsc_t *dne) { 

152 
dnsc_t *p; 

153 
unsigned n; 

154 
dnscc_t *s = ((dnscc_t *)addr) + 4; 

155 
while(s >= (dnscc_t *)addr) { 

156 
n = *s; 

157 
p = dn + 1; 

158 
if (n > 99) { 

159 
if (p + 2 > dne) return 0; 

160 
*p++ = n / 100 + '0'; 

161 
*p++ = (n % 100 / 10) + '0'; 

162 
*p = n % 10 + '0'; 

163 
} 

164 
else if (n > 9) { 

165 
if (p + 1 > dne) return 0; 

166 
*p++ = n / 10 + '0'; 

167 
*p = n % 10 + '0'; 

168 
} 

169 
else { 

170 
if (p > dne) return 0; 

171 
*p = n + '0'; 

172 
} 

173 
*dn = p  dn; 

174 
dn = p + 1; 

175 
} 

176 
return dn; 

177 
} 

178 


179 
int dns_a4todn(const struct in_addr *addr, dnscc_t *tdn, 

180 
dnsc_t *dn, unsigned dnsiz) { 

181 
dnsc_t *dne = dn + (dnsiz > DNS_MAXDN ? DNS_MAXDN : dnsiz); 

182 
dnsc_t *p; 

183 
unsigned l; 

184 
p = dns_a4todn_(addr, dn, dne); 

185 
if (!p) return 0; 

186 
if (!tdn) 

187 
tdn = dns_inaddr_arpa_dn; 

188 
l = dns_dnlen(tdn); 

189 
if (p + l > dne) return dnsiz >= DNS_MAXDN ? 1 : 0; 

190 
memcpy(p, tdn, l); 

191 
return (p + l)  dn; 

192 
} 

193 


194 
int dns_a4ptodn(const struct in_addr *addr, const char *tname, 

195 
dnsc_t *dn, unsigned dnsiz) { 

196 
dnsc_t *p; 

197 
int r; 

198 
if (!tname) 

199 
return dns_a4todn(addr, NULL, dn, dnsiz); 

200 
p = dns_a4todn_(addr, dn, dn + dnsiz); 

201 
if (!p) return 0; 

202 
r = dns_sptodn(tname, p, dnsiz  (p  dn)); 

203 
return r != 0 ? r : dnsiz >= DNS_MAXDN ? 1 : 0; 

204 
} 

205 


206 
dnscc_t dns_ip6_arpa_dn[10] = "\03ip6\04arpa"; 

207 


208 
dnsc_t * 

209 
dns_a6todn_(const struct in6_addr *addr, dnsc_t *dn, dnsc_t *dne) { 

210 
unsigned n; 

211 
dnscc_t *s = ((dnscc_t *)addr) + 16; 

212 
if (dn + 64 > dne) return 0; 

213 
while(s >= (dnscc_t *)addr) { 

214 
*dn++ = 1; 

215 
n = *s & 0x0f; 

216 
*dn++ = n > 9 ? n + 'a'  10 : n + '0'; 

217 
*dn++ = 1; 

218 
n = *s >> 4; 

219 
*dn++ = n > 9 ? n + 'a'  10 : n + '0'; 

220 
} 

221 
return dn; 

222 
} 

223 


224 
int dns_a6todn(const struct in6_addr *addr, dnscc_t *tdn, 

225 
dnsc_t *dn, unsigned dnsiz) { 

226 
dnsc_t *dne = dn + (dnsiz > DNS_MAXDN ? DNS_MAXDN : dnsiz); 

227 
dnsc_t *p; 

228 
unsigned l; 

229 
p = dns_a6todn_(addr, dn, dne); 

230 
if (!p) return 0; 

231 
if (!tdn) 

232 
tdn = dns_ip6_arpa_dn; 

233 
l = dns_dnlen(tdn); 

234 
if (p + l > dne) return dnsiz >= DNS_MAXDN ? 1 : 0; 

235 
memcpy(p, tdn, l); 

236 
return (p + l)  dn; 

237 
} 

238 


239 
int dns_a6ptodn(const struct in6_addr *addr, const char *tname, 

240 
dnsc_t *dn, unsigned dnsiz) { 

241 
dnsc_t *p; 

242 
int r; 

243 
if (!tname) 

244 
return dns_a6todn(addr, NULL, dn, dnsiz); 

245 
p = dns_a6todn_(addr, dn, dn + dnsiz); 

246 
if (!p) return 0; 

247 
r = dns_sptodn(tname, p, dnsiz  (p  dn)); 

248 
return r != 0 ? r : dnsiz >= DNS_MAXDN ? 1 : 0; 

249 
} 

250 


251 
/* return size of buffer required to convert the dn into asciiz string. 

252 
* Keep in sync with dns_dntop() below. 

253 
*/ 

254 
unsigned dns_dntop_size(dnscc_t *dn) { 

255 
unsigned size = 0; /* the size reqd */ 

256 
dnscc_t *le; /* label end */ 

257 


258 
while(*dn) { 

259 
/* *dn is the length of the next label, nonzero */ 

260 
if (size) 

261 
++size; /* for the dot */ 

262 
le = dn + *dn + 1; 

263 
++dn; 

264 
do { 

265 
switch(*dn) { 

266 
case '.': 

267 
case '\\': 

268 
/* Special modifiers in zone files. */ 

269 
case '"': 

270 
case ';': 

271 
case '@': 

272 
case '$': 

273 
size += 2; 

274 
break; 

275 
default: 

276 
if (*dn <= 0x20  *dn >= 0x7f) 

277 
/* \ddd decimal notation */ 

278 
size += 4; 

279 
else 

280 
size += 1; 

281 
} 

282 
} while(++dn < le); 

283 
} 

284 
size += 1; /* zero byte at the end  string terminator */ 

285 
return size > DNS_MAXNAME ? 0 : size; 

286 
} 

287 


288 
/* Convert the dn into asciiz string. 

289 
* Keep in sync with dns_dntop_size() above. 

290 
*/ 

291 
int dns_dntop(dnscc_t *dn, char *name, unsigned namesiz) { 

292 
char *np = name; /* current name ptr */ 

293 
char *const ne = name + namesiz; /* end of name */ 

294 
dnscc_t *le; /* label end */ 

295 


296 
while(*dn) { 

297 
/* *dn is the length of the next label, nonzero */ 

298 
if (np != name) { 

299 
if (np >= ne) goto toolong; 

300 
*np++ = '.'; 

301 
} 

302 
le = dn + *dn + 1; 

303 
++dn; 

304 
do { 

305 
switch(*dn) { 

306 
case '.': 

307 
case '\\': 

308 
/* Special modifiers in zone files. */ 

309 
case '"': 

310 
case ';': 

311 
case '@': 

312 
case '$': 

313 
if (np + 2 > ne) goto toolong; 

314 
*np++ = '\\'; 

315 
*np++ = *dn; 

316 
break; 

317 
default: 

318 
if (*dn <= 0x20  *dn >= 0x7f) { 

319 
/* \ddd decimal notation */ 

320 
if (np + 4 >= ne) goto toolong; 

321 
*np++ = '\\'; 

322 
*np++ = '0' + (*dn / 100); 

323 
*np++ = '0' + ((*dn % 100) / 10); 

324 
*np++ = '0' + (*dn % 10); 

325 
} 

326 
else { 

327 
if (np >= ne) goto toolong; 

328 
*np++ = *dn; 

329 
} 

330 
} 

331 
} while(++dn < le); 

332 
} 

333 
if (np >= ne) goto toolong; 

334 
*np++ = '\0'; 

335 
return np  name; 

336 
toolong: 

337 
return namesiz >= DNS_MAXNAME ? 1 : 0; 

338 
} 

339 


340 
#ifdef TEST 

341 
#include <stdio.h> 

342 
#include <stdlib.h> 

343 


344 
int main(int argc, char **argv) { 

345 
int i; 

346 
int sz; 

347 
dnsc_t dn[DNS_MAXDN+10]; 

348 
dnsc_t *dl, *dp; 

349 
int isabs; 

350 


351 
sz = (argc > 1) ? atoi(argv[1]) : 0; 

352 


353 
for(i = 2; i < argc; ++i) { 

354 
int r = dns_ptodn(argv[i], 0, dn, sz, &isabs); 

355 
printf("%s: ", argv[i]); 

356 
if (r < 0) printf("error\n"); 

357 
else if (!r) printf("buffer too small\n"); 

358 
else { 

359 
printf("len=%d dnlen=%d size=%d name:", 

360 
r, dns_dnlen(dn), dns_dntop_size(dn)); 

361 
dl = dn; 

362 
while(*dl) { 

363 
printf(" %d=", *dl); 

364 
dp = dl + 1; 

365 
dl = dp + *dl; 

366 
while(dp < dl) { 

367 
if (*dp <= ' '  *dp >= 0x7f) 

368 
printf("\\%03d", *dp); 

369 
else if (*dp == '.'  *dp == '\\') 

370 
printf("\\%c", *dp); 

371 
else 

372 
putchar(*dp); 

373 
++dp; 

374 
} 

375 
} 

376 
if (isabs) putchar('.'); 

377 
putchar('\n'); 

378 
} 

379 
} 

380 
return 0; 

381 
} 

382 


383 
#endif /* TEST */ 
