1 
/* udns_dn.c 

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 
break; 

91 
} 

92 
return 1; /* zero label */ 

93 
} 

94 
if (c > DNS_MAXLABEL) 

95 
return 1; /* label too long */ 

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

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

98 
++np; 

99 
continue; 

100 
} 

101 


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

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

104 
return dnsiz >= DNS_MAXDN ? 1 : 0; 

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

106 
*dp++ = *np++; 

107 
continue; 

108 
} 

109 
/* handle \style escape */ 

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

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

112 
* we're following this tradition here. 

113 
*/ 

114 
if (++np == ne) 

115 
return 1; /* bad escape */ 

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

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

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

119 
c = *np++  '0'; 

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

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

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

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

124 
if (c > 255) 

125 
return 1; /* bad escape */ 

126 
} 

127 
} 

128 
} 

129 
else 

130 
c = *np++; 

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

132 
} 

133 


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

135 
return 1; /* label too long */ 

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

137 
*dp++ = 0; 

138 
if (isabs) 

139 
*isabs = 0; 

140 
} 

141 
else if (isabs) 

142 
*isabs = 1; 

143 


144 
return dp  dn; 

145 
} 

146 


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

148 


149 
dnsc_t * 

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

151 
const unsigned char *s = ((const unsigned char *)addr) + 4; 

152 
while(s > (const unsigned char *)addr) { 

153 
unsigned n = *s; 

154 
dnsc_t *p = dn + 1; 

155 
if (n > 99) { 

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

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

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

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

160 
} 

161 
else if (n > 9) { 

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

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

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

165 
} 

166 
else { 

167 
if (p > dne) return 0; 

168 
*p = n + '0'; 

169 
} 

170 
*dn = p  dn; 

171 
dn = p + 1; 

172 
} 

173 
return dn; 

174 
} 

175 


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

177 
dnsc_t *dn, unsigned dnsiz) { 

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

179 
dnsc_t *p; 

180 
unsigned l; 

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

182 
if (!p) return 0; 

183 
if (!tdn) 

184 
tdn = dns_inaddr_arpa_dn; 

185 
l = dns_dnlen(tdn); 

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

187 
memcpy(p, tdn, l); 

188 
return (p + l)  dn; 

189 
} 

190 


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

192 
dnsc_t *dn, unsigned dnsiz) { 

193 
dnsc_t *p; 

194 
int r; 

195 
if (!tname) 

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

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

198 
if (!p) return 0; 

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

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

201 
} 

202 


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

204 


205 
dnsc_t * 

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

207 
const unsigned char *s = ((const unsigned char *)addr) + 16; 

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

209 
while(s > (const unsigned char *)addr) { 

210 
unsigned n = *s & 0x0f; 

211 
*dn++ = 1; 

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

213 
*dn++ = 1; 

214 
n = *s >> 4; 

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

216 
} 

217 
return dn; 

218 
} 

219 


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

221 
dnsc_t *dn, unsigned dnsiz) { 

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

223 
dnsc_t *p; 

224 
unsigned l; 

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

226 
if (!p) return 0; 

227 
if (!tdn) 

228 
tdn = dns_ip6_arpa_dn; 

229 
l = dns_dnlen(tdn); 

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

231 
memcpy(p, tdn, l); 

232 
return (p + l)  dn; 

233 
} 

234 


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

236 
dnsc_t *dn, unsigned dnsiz) { 

237 
dnsc_t *p; 

238 
int r; 

239 
if (!tname) 

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

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

242 
if (!p) return 0; 

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

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

245 
} 

246 


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

248 
* Keep in sync with dns_dntop() below. 

249 
*/ 

250 
unsigned dns_dntop_size(dnscc_t *dn) { 

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

252 
dnscc_t *le; /* label end */ 

253 


254 
while(*dn) { 

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

256 
if (size) 

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

258 
le = dn + *dn + 1; 

259 
++dn; 

260 
do { 

261 
switch(*dn) { 

262 
case '.': 

263 
case '\\': 

264 
/* Special modifiers in zone files. */ 

265 
case '"': 

266 
case ';': 

267 
case '@': 

268 
case '$': 

269 
size += 2; 

270 
break; 

271 
default: 

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

273 
/* \ddd decimal notation */ 

274 
size += 4; 

275 
else 

276 
size += 1; 

277 
} 

278 
} while(++dn < le); 

279 
} 

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

281 
return size > DNS_MAXNAME ? 0 : size; 

282 
} 

283 


284 
/* Convert the dn into asciiz string. 

285 
* Keep in sync with dns_dntop_size() above. 

286 
*/ 

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

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

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

290 
dnscc_t *le; /* label end */ 

291 


292 
while(*dn) { 

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

294 
if (np != name) { 

295 
if (np >= ne) goto toolong; 

296 
*np++ = '.'; 

297 
} 

298 
le = dn + *dn + 1; 

299 
++dn; 

300 
do { 

301 
switch(*dn) { 

302 
case '.': 

303 
case '\\': 

304 
/* Special modifiers in zone files. */ 

305 
case '"': 

306 
case ';': 

307 
case '@': 

308 
case '$': 

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

310 
*np++ = '\\'; 

311 
*np++ = *dn; 

312 
break; 

313 
default: 

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

315 
/* \ddd decimal notation */ 

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

317 
*np++ = '\\'; 

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

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

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

321 
} 

322 
else { 

323 
if (np >= ne) goto toolong; 

324 
*np++ = *dn; 

325 
} 

326 
} 

327 
} while(++dn < le); 

328 
} 

329 
if (np >= ne) goto toolong; 

330 
*np++ = '\0'; 

331 
return np  name; 

332 
toolong: 

333 
return namesiz >= DNS_MAXNAME ? 1 : 0; 

334 
} 

335 


336 
#ifdef TEST 

337 
#include <stdio.h> 

338 
#include <stdlib.h> 

339 


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

341 
int i; 

342 
int sz; 

343 
dnsc_t dn[DNS_MAXDN+10]; 

344 
dnsc_t *dl, *dp; 

345 
int isabs; 

346 


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

348 


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

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

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

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

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

354 
else { 

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

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

357 
dl = dn; 

358 
while(*dl) { 

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

360 
dp = dl + 1; 

361 
dl = dp + *dl; 

362 
while(dp < dl) { 

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

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

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

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

367 
else 

368 
putchar(*dp); 

369 
++dp; 

370 
} 

371 
} 

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

373 
putchar('\n'); 

374 
} 

375 
} 

376 
return 0; 

377 
} 

378 


379 
#endif /* TEST */ 
