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 
unsigned l = 0; 

30 
while(*d && l < DNS_MAXDN) { 

31 
l += 1 + *d; 

32 
d += 1 + *d; 

33 
} 

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

35 
} 

36 


37 
unsigned dns_dnlabels(register dnscc_t *dn) { 

38 
register unsigned l = 0; 

39 
while(*dn) 

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

41 
return l; 

42 
} 

43 


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

45 
register unsigned c; 

46 
dnscc_t *dn = dn1; 

47 
for(;;) { 

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

49 
return 0; 

50 
if (!c) 

51 
return (unsigned)(dn1  dn); 

52 
while(c) { 

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

54 
return 0; 

55 
++dn1; ++dn2; 

56 
} 

57 
} 

58 
} 

59 


60 
unsigned 

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

62 
unsigned sdnlen = dns_dnlen(sdn); 

63 
if (ddnsiz < sdnlen) 

64 
return 0; 

65 
memcpy(ddn, sdn, sdnlen); 

66 
return sdnlen; 

67 
} 

68 


69 
int 

70 
dns_ptodn(const char *name, unsigned namelen, 

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

72 
{ 

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

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

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

76 
dnscc_t *np = (dnscc_t *)name; 

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

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

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

80 


81 
if (!dnsiz) 

82 
return 0; 

83 
dp = llab = dn + 1; 

84 


85 
while(np < ne) { 

86 


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

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

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

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

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

92 
/* ++np; 

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

94 
break; 

95 
} 

96 
return 1; /* zero label */ 

97 
} 

98 
if (c > DNS_MAXLABEL) 

99 
return 1; /* label too long */ 

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

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

102 
++np; 

103 
continue; 

104 
} 

105 


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

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

108 
return dnsiz >= DNS_MAXDN ? 1 : 0; 

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

110 
*dp++ = *np++; 

111 
continue; 

112 
} 

113 
/* handle \style escape */ 

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

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

116 
* we're following this tradition here. 

117 
*/ 

118 
if (++np == ne) 

119 
return 1; /* bad escape */ 

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

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

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

123 
c = *np++  '0'; 

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

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

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

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

128 
if (c > 255) 

129 
return 1; /* bad escape */ 

130 
} 

131 
} 

132 
} 

133 
else 

134 
c = *np++; 

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

136 
} 

137 


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

139 
return 1; /* label too long */ 

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

141 
if (dp >= de) 

142 
return 1; 

143 
*dp++ = 0; 

144 
if (isabs) 

145 
*isabs = 0; 

146 
} 

147 
else if (isabs) 

148 
*isabs = 1; 

149 


150 
return dp  dn; 

151 
} 

152 


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

154 


155 
dnsc_t * 

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

157 
dnsc_t *p; 

158 
unsigned n; 

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

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

161 
n = *s; 

162 
p = dn + 1; 

163 
if (n > 99) { 

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

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

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

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

168 
} 

169 
else if (n > 9) { 

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

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

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

173 
} 

174 
else { 

175 
if (p > dne) return 0; 

176 
*p = n + '0'; 

177 
} 

178 
*dn = p  dn; 

179 
dn = p + 1; 

180 
} 

181 
return dn; 

182 
} 

183 


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

185 
dnsc_t *dn, unsigned dnsiz) { 

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

187 
dnsc_t *p; 

188 
unsigned l; 

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

190 
if (!p) return 0; 

191 
if (!tdn) 

192 
tdn = dns_inaddr_arpa_dn; 

193 
l = dns_dnlen(tdn); 

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

195 
memcpy(p, tdn, l); 

196 
return (p + l)  dn; 

197 
} 

198 


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

200 
dnsc_t *dn, unsigned dnsiz) { 

201 
dnsc_t *p; 

202 
int r; 

203 
if (!tname) 

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

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

206 
if (!p) return 0; 

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

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

209 
} 

210 


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

212 


213 
dnsc_t * 

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

215 
unsigned n; 

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

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

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

219 
*dn++ = 1; 

220 
n = *s & 0x0f; 

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

222 
*dn++ = 1; 

223 
n = *s >> 4; 

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

225 
} 

226 
return dn; 

227 
} 

228 


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

230 
dnsc_t *dn, unsigned dnsiz) { 

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

232 
dnsc_t *p; 

233 
unsigned l; 

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

235 
if (!p) return 0; 

236 
if (!tdn) 

237 
tdn = dns_ip6_arpa_dn; 

238 
l = dns_dnlen(tdn); 

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

240 
memcpy(p, tdn, l); 

241 
return (p + l)  dn; 

242 
} 

243 


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

245 
dnsc_t *dn, unsigned dnsiz) { 

246 
dnsc_t *p; 

247 
int r; 

248 
if (!tname) 

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

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

251 
if (!p) return 0; 

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

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

254 
} 

255 


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

257 
* Keep in sync with dns_dntop() below. 

258 
*/ 

259 
unsigned dns_dntop_size(dnscc_t *dn) { 

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

261 
dnscc_t *le; /* label end */ 

262 


263 
while(*dn) { 

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

265 
if (size) 

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

267 
le = dn + *dn + 1; 

268 
++dn; 

269 
do { 

270 
switch(*dn) { 

271 
case '.': 

272 
case '\\': 

273 
/* Special modifiers in zone files. */ 

274 
case '"': 

275 
case ';': 

276 
case '@': 

277 
case '$': 

278 
size += 2; 

279 
break; 

280 
default: 

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

282 
/* \ddd decimal notation */ 

283 
size += 4; 

284 
else 

285 
size += 1; 

286 
} 

287 
} while(++dn < le); 

288 
} 

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

290 
return size > DNS_MAXNAME ? 0 : size; 

291 
} 

292 


293 
/* Convert the dn into asciiz string. 

294 
* Keep in sync with dns_dntop_size() above. 

295 
*/ 

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

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

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

299 
dnscc_t *le; /* label end */ 

300 


301 
while(*dn) { 

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

303 
if (np != name) { 

304 
if (np >= ne) goto toolong; 

305 
*np++ = '.'; 

306 
} 

307 
le = dn + *dn + 1; 

308 
++dn; 

309 
do { 

310 
switch(*dn) { 

311 
case '.': 

312 
case '\\': 

313 
/* Special modifiers in zone files. */ 

314 
case '"': 

315 
case ';': 

316 
case '@': 

317 
case '$': 

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

319 
*np++ = '\\'; 

320 
*np++ = *dn; 

321 
break; 

322 
default: 

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

324 
/* \ddd decimal notation */ 

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

326 
*np++ = '\\'; 

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

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

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

330 
} 

331 
else { 

332 
if (np >= ne) goto toolong; 

333 
*np++ = *dn; 

334 
} 

335 
} 

336 
} while(++dn < le); 

337 
} 

338 
if (np >= ne) goto toolong; 

339 
*np++ = '\0'; 

340 
return np  name; 

341 
toolong: 

342 
return namesiz >= DNS_MAXNAME ? 1 : 0; 

343 
} 

344 


345 
#ifdef TEST 

346 
#include <stdio.h> 

347 
#include <stdlib.h> 

348 


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

350 
int i; 

351 
int sz; 

352 
dnsc_t dn[DNS_MAXDN+10]; 

353 
dnsc_t *dl, *dp; 

354 
int isabs; 

355 


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

357 


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

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

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

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

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

363 
else { 

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

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

366 
dl = dn; 

367 
while(*dl) { 

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

369 
dp = dl + 1; 

370 
dl = dp + *dl; 

371 
while(dp < dl) { 

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

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

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

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

376 
else 

377 
putchar(*dp); 

378 
++dp; 

379 
} 

380 
} 

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

382 
putchar('\n'); 

383 
} 

384 
} 

385 
return 0; 

386 
} 

387 


388 
#endif /* TEST */ 
