1/* 2 Linux DNS client library implementation 3 Copyright (C) 2006 Gerald Carter <jerry@samba.org> 4 5 ** NOTE! The following LGPL license applies to the libaddns 6 ** library. This does NOT imply that all of Samba is released 7 ** under the LGPL 8 9 This library is free software; you can redistribute it and/or 10 modify it under the terms of the GNU Lesser General Public 11 License as published by the Free Software Foundation; either 12 version 2.1 of the License, or (at your option) any later version. 13 14 This library is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 Lesser General Public License for more details. 18 19 You should have received a copy of the GNU Lesser General Public 20 License along with this library; if not, see <http://www.gnu.org/licenses/>. 21*/ 22 23#include "dns.h" 24#include "assert.h" 25 26struct dns_buffer *dns_create_buffer(TALLOC_CTX *mem_ctx) 27{ 28 struct dns_buffer *result; 29 30 if (!(result = talloc(mem_ctx, struct dns_buffer))) { 31 return NULL; 32 } 33 34 result->offset = 0; 35 result->error = ERROR_DNS_SUCCESS; 36 37 /* 38 * Small inital size to excercise the realloc code 39 */ 40 result->size = 2; 41 42 if (!(result->data = TALLOC_ARRAY(result, uint8, result->size))) { 43 TALLOC_FREE(result); 44 return NULL; 45 } 46 47 return result; 48} 49 50void dns_marshall_buffer(struct dns_buffer *buf, const uint8 *data, 51 size_t len) 52{ 53 if (!ERR_DNS_IS_OK(buf->error)) return; 54 55 if (buf->offset + len < buf->offset) { 56 /* 57 * Wraparound! 58 */ 59 buf->error = ERROR_DNS_INVALID_PARAMETER; 60 return; 61 } 62 63 if ((buf->offset + len) > 0xffff) { 64 /* 65 * Only 64k possible 66 */ 67 buf->error = ERROR_DNS_INVALID_PARAMETER; 68 return; 69 } 70 71 if (buf->offset + len > buf->size) { 72 size_t new_size = buf->offset + len; 73 uint8 *new_data; 74 75 /* 76 * Don't do too many reallocs, round up to some multiple 77 */ 78 79 new_size += (64 - (new_size % 64)); 80 81 if (!(new_data = TALLOC_REALLOC_ARRAY(buf, buf->data, uint8, 82 new_size))) { 83 buf->error = ERROR_DNS_NO_MEMORY; 84 return; 85 } 86 87 buf->size = new_size; 88 buf->data = new_data; 89 } 90 91 memcpy(buf->data + buf->offset, data, len); 92 buf->offset += len; 93 return; 94} 95 96void dns_marshall_uint16(struct dns_buffer *buf, uint16 val) 97{ 98 uint16 n_val = htons(val); 99 dns_marshall_buffer(buf, (uint8 *)&n_val, sizeof(n_val)); 100} 101 102void dns_marshall_uint32(struct dns_buffer *buf, uint32 val) 103{ 104 uint32 n_val = htonl(val); 105 dns_marshall_buffer(buf, (uint8 *)&n_val, sizeof(n_val)); 106} 107 108void dns_unmarshall_buffer(struct dns_buffer *buf, uint8 *data, 109 size_t len) 110{ 111 if (!(ERR_DNS_IS_OK(buf->error))) return; 112 113 if ((len > buf->size) || (buf->offset + len > buf->size)) { 114 buf->error = ERROR_DNS_INVALID_MESSAGE; 115 return; 116 } 117 118 memcpy((void *)data, (const void *)(buf->data + buf->offset), len); 119 buf->offset += len; 120 121 return; 122} 123 124void dns_unmarshall_uint16(struct dns_buffer *buf, uint16 *val) 125{ 126 uint16 n_val; 127 128 dns_unmarshall_buffer(buf, (uint8 *)&n_val, sizeof(n_val)); 129 if (!(ERR_DNS_IS_OK(buf->error))) return; 130 131 *val = ntohs(n_val); 132} 133 134void dns_unmarshall_uint32(struct dns_buffer *buf, uint32 *val) 135{ 136 uint32 n_val; 137 138 dns_unmarshall_buffer(buf, (uint8 *)&n_val, sizeof(n_val)); 139 if (!(ERR_DNS_IS_OK(buf->error))) return; 140 141 *val = ntohl(n_val); 142} 143 144void dns_marshall_domain_name(struct dns_buffer *buf, 145 const struct dns_domain_name *name) 146{ 147 struct dns_domain_label *label; 148 char end_char = '\0'; 149 150 /* 151 * TODO: Implement DNS compression 152 */ 153 154 for (label = name->pLabelList; label != NULL; label = label->next) { 155 uint8 len = label->len; 156 157 dns_marshall_buffer(buf, (uint8 *)&len, sizeof(len)); 158 if (!ERR_DNS_IS_OK(buf->error)) return; 159 160 dns_marshall_buffer(buf, (uint8 *)label->label, len); 161 if (!ERR_DNS_IS_OK(buf->error)) return; 162 } 163 164 dns_marshall_buffer(buf, (uint8 *)&end_char, 1); 165} 166 167static void dns_unmarshall_label(TALLOC_CTX *mem_ctx, 168 int level, 169 struct dns_buffer *buf, 170 struct dns_domain_label **plabel) 171{ 172 struct dns_domain_label *label; 173 uint8 len; 174 175 if (!ERR_DNS_IS_OK(buf->error)) return; 176 177 if (level > 128) { 178 /* 179 * Protect against recursion 180 */ 181 buf->error = ERROR_DNS_INVALID_MESSAGE; 182 return; 183 } 184 185 dns_unmarshall_buffer(buf, &len, sizeof(len)); 186 if (!ERR_DNS_IS_OK(buf->error)) return; 187 188 if (len == 0) { 189 *plabel = NULL; 190 return; 191 } 192 193 if ((len & 0xc0) == 0xc0) { 194 /* 195 * We've got a compressed name. Build up a new "fake" buffer 196 * and using the calculated offset. 197 */ 198 struct dns_buffer new_buf; 199 uint8 low; 200 201 dns_unmarshall_buffer(buf, &low, sizeof(low)); 202 if (!ERR_DNS_IS_OK(buf->error)) return; 203 204 new_buf = *buf; 205 new_buf.offset = len & 0x3f; 206 new_buf.offset <<= 8; 207 new_buf.offset |= low; 208 209 dns_unmarshall_label(mem_ctx, level+1, &new_buf, plabel); 210 buf->error = new_buf.error; 211 return; 212 } 213 214 if ((len & 0xc0) != 0) { 215 buf->error = ERROR_DNS_INVALID_NAME; 216 return; 217 } 218 219 if (!(label = talloc(mem_ctx, struct dns_domain_label))) { 220 buf->error = ERROR_DNS_NO_MEMORY; 221 return; 222 } 223 224 label->len = len; 225 226 if (!(label->label = TALLOC_ARRAY(label, char, len+1))) { 227 buf->error = ERROR_DNS_NO_MEMORY; 228 goto error; 229 } 230 231 dns_unmarshall_buffer(buf, (uint8 *)label->label, len); 232 if (!ERR_DNS_IS_OK(buf->error)) goto error; 233 234 dns_unmarshall_label(label, level+1, buf, &label->next); 235 if (!ERR_DNS_IS_OK(buf->error)) goto error; 236 237 *plabel = label; 238 return; 239 240 error: 241 TALLOC_FREE(label); 242 return; 243} 244 245void dns_unmarshall_domain_name(TALLOC_CTX *mem_ctx, 246 struct dns_buffer *buf, 247 struct dns_domain_name **pname) 248{ 249 struct dns_domain_name *name; 250 251 if (!ERR_DNS_IS_OK(buf->error)) return; 252 253 if (!(name = talloc(mem_ctx, struct dns_domain_name))) { 254 buf->error = ERROR_DNS_NO_MEMORY; 255 return; 256 } 257 258 dns_unmarshall_label(name, 0, buf, &name->pLabelList); 259 260 if (!ERR_DNS_IS_OK(buf->error)) { 261 return; 262 } 263 264 *pname = name; 265 return; 266} 267 268static void dns_marshall_question(struct dns_buffer *buf, 269 const struct dns_question *q) 270{ 271 dns_marshall_domain_name(buf, q->name); 272 dns_marshall_uint16(buf, q->q_type); 273 dns_marshall_uint16(buf, q->q_class); 274} 275 276static void dns_unmarshall_question(TALLOC_CTX *mem_ctx, 277 struct dns_buffer *buf, 278 struct dns_question **pq) 279{ 280 struct dns_question *q; 281 282 if (!(ERR_DNS_IS_OK(buf->error))) return; 283 284 if (!(q = talloc(mem_ctx, struct dns_question))) { 285 buf->error = ERROR_DNS_NO_MEMORY; 286 return; 287 } 288 289 dns_unmarshall_domain_name(q, buf, &q->name); 290 dns_unmarshall_uint16(buf, &q->q_type); 291 dns_unmarshall_uint16(buf, &q->q_class); 292 293 if (!(ERR_DNS_IS_OK(buf->error))) return; 294 295 *pq = q; 296} 297 298static void dns_marshall_rr(struct dns_buffer *buf, 299 const struct dns_rrec *r) 300{ 301 dns_marshall_domain_name(buf, r->name); 302 dns_marshall_uint16(buf, r->type); 303 dns_marshall_uint16(buf, r->r_class); 304 dns_marshall_uint32(buf, r->ttl); 305 dns_marshall_uint16(buf, r->data_length); 306 dns_marshall_buffer(buf, r->data, r->data_length); 307} 308 309static void dns_unmarshall_rr(TALLOC_CTX *mem_ctx, 310 struct dns_buffer *buf, 311 struct dns_rrec **pr) 312{ 313 struct dns_rrec *r; 314 315 if (!(ERR_DNS_IS_OK(buf->error))) return; 316 317 if (!(r = talloc(mem_ctx, struct dns_rrec))) { 318 buf->error = ERROR_DNS_NO_MEMORY; 319 return; 320 } 321 322 dns_unmarshall_domain_name(r, buf, &r->name); 323 dns_unmarshall_uint16(buf, &r->type); 324 dns_unmarshall_uint16(buf, &r->r_class); 325 dns_unmarshall_uint32(buf, &r->ttl); 326 dns_unmarshall_uint16(buf, &r->data_length); 327 r->data = NULL; 328 329 if (!(ERR_DNS_IS_OK(buf->error))) return; 330 331 if (r->data_length != 0) { 332 if (!(r->data = TALLOC_ARRAY(r, uint8, r->data_length))) { 333 buf->error = ERROR_DNS_NO_MEMORY; 334 return; 335 } 336 dns_unmarshall_buffer(buf, r->data, r->data_length); 337 } 338 339 if (!(ERR_DNS_IS_OK(buf->error))) return; 340 341 *pr = r; 342} 343 344DNS_ERROR dns_marshall_request(TALLOC_CTX *mem_ctx, 345 const struct dns_request *req, 346 struct dns_buffer **pbuf) 347{ 348 struct dns_buffer *buf; 349 uint16 i; 350 351 if (!(buf = dns_create_buffer(mem_ctx))) { 352 return ERROR_DNS_NO_MEMORY; 353 } 354 355 dns_marshall_uint16(buf, req->id); 356 dns_marshall_uint16(buf, req->flags); 357 dns_marshall_uint16(buf, req->num_questions); 358 dns_marshall_uint16(buf, req->num_answers); 359 dns_marshall_uint16(buf, req->num_auths); 360 dns_marshall_uint16(buf, req->num_additionals); 361 362 for (i=0; i<req->num_questions; i++) { 363 dns_marshall_question(buf, req->questions[i]); 364 } 365 for (i=0; i<req->num_answers; i++) { 366 dns_marshall_rr(buf, req->answers[i]); 367 } 368 for (i=0; i<req->num_auths; i++) { 369 dns_marshall_rr(buf, req->auths[i]); 370 } 371 for (i=0; i<req->num_additionals; i++) { 372 dns_marshall_rr(buf, req->additionals[i]); 373 } 374 375 if (!ERR_DNS_IS_OK(buf->error)) { 376 DNS_ERROR err = buf->error; 377 TALLOC_FREE(buf); 378 return err; 379 } 380 381 *pbuf = buf; 382 return ERROR_DNS_SUCCESS; 383} 384 385DNS_ERROR dns_unmarshall_request(TALLOC_CTX *mem_ctx, 386 struct dns_buffer *buf, 387 struct dns_request **preq) 388{ 389 struct dns_request *req; 390 uint16 i; 391 DNS_ERROR err; 392 393 if (!(req = TALLOC_ZERO_P(mem_ctx, struct dns_request))) { 394 return ERROR_DNS_NO_MEMORY; 395 } 396 397 dns_unmarshall_uint16(buf, &req->id); 398 dns_unmarshall_uint16(buf, &req->flags); 399 dns_unmarshall_uint16(buf, &req->num_questions); 400 dns_unmarshall_uint16(buf, &req->num_answers); 401 dns_unmarshall_uint16(buf, &req->num_auths); 402 dns_unmarshall_uint16(buf, &req->num_additionals); 403 404 if (!ERR_DNS_IS_OK(buf->error)) goto error; 405 406 err = ERROR_DNS_NO_MEMORY; 407 408 if ((req->num_questions != 0) && 409 !(req->questions = TALLOC_ARRAY(req, struct dns_question *, 410 req->num_questions))) { 411 goto error; 412 } 413 if ((req->num_answers != 0) && 414 !(req->answers = TALLOC_ARRAY(req, struct dns_rrec *, 415 req->num_answers))) { 416 goto error; 417 } 418 if ((req->num_auths != 0) && 419 !(req->auths = TALLOC_ARRAY(req, struct dns_rrec *, 420 req->num_auths))) { 421 goto error; 422 } 423 if ((req->num_additionals != 0) && 424 !(req->additionals = TALLOC_ARRAY(req, struct dns_rrec *, 425 req->num_additionals))) { 426 goto error; 427 } 428 429 for (i=0; i<req->num_questions; i++) { 430 dns_unmarshall_question(req->questions, buf, 431 &req->questions[i]); 432 } 433 for (i=0; i<req->num_answers; i++) { 434 dns_unmarshall_rr(req->answers, buf, 435 &req->answers[i]); 436 } 437 for (i=0; i<req->num_auths; i++) { 438 dns_unmarshall_rr(req->auths, buf, 439 &req->auths[i]); 440 } 441 for (i=0; i<req->num_additionals; i++) { 442 dns_unmarshall_rr(req->additionals, buf, 443 &req->additionals[i]); 444 } 445 446 if (!ERR_DNS_IS_OK(buf->error)) { 447 err = buf->error; 448 goto error; 449 } 450 451 *preq = req; 452 return ERROR_DNS_SUCCESS; 453 454 error: 455 err = buf->error; 456 TALLOC_FREE(req); 457 return err; 458} 459 460struct dns_request *dns_update2request(struct dns_update_request *update) 461{ 462 struct dns_request *req; 463 464 /* 465 * This is a non-specified construct that happens to work on Linux/gcc 466 * and I would expect it to work everywhere else. dns_request and 467 * dns_update_request are essentially the same structures with 468 * different names, so any difference would mean that the compiler 469 * applied two different variations of padding given the same types in 470 * the structures. 471 */ 472 473 req = (struct dns_request *)(void *)update; 474 475 /* 476 * The assert statement here looks like we could do the equivalent 477 * assignments to get portable, but it would mean that we have to 478 * allocate the dns_question record for the dns_zone records. We 479 * assume that if this assert works then the same holds true for 480 * dns_zone<>dns_question as well. 481 */ 482 483#ifdef DEVELOPER 484 assert((req->id == update->id) && (req->flags == update->flags) && 485 (req->num_questions == update->num_zones) && 486 (req->num_answers == update->num_preqs) && 487 (req->num_auths == update->num_updates) && 488 (req->num_additionals == update->num_additionals) && 489 (req->questions == 490 (struct dns_question **)(void *)update->zones) && 491 (req->answers == update->preqs) && 492 (req->auths == update->updates) && 493 (req->additionals == update->additionals)); 494#endif 495 496 return req; 497} 498 499struct dns_update_request *dns_request2update(struct dns_request *request) 500{ 501 /* 502 * For portability concerns see dns_update2request; 503 */ 504 return (struct dns_update_request *)(void *)request; 505} 506 507DNS_ERROR dns_marshall_update_request(TALLOC_CTX *mem_ctx, 508 struct dns_update_request *update, 509 struct dns_buffer **pbuf) 510{ 511 return dns_marshall_request(mem_ctx, dns_update2request(update), pbuf); 512} 513 514DNS_ERROR dns_unmarshall_update_request(TALLOC_CTX *mem_ctx, 515 struct dns_buffer *buf, 516 struct dns_update_request **pupreq) 517{ 518 /* 519 * See comments above about portability. If the above works, this will 520 * as well. 521 */ 522 523 return dns_unmarshall_request(mem_ctx, buf, 524 (struct dns_request **)(void *)pupreq); 525} 526 527uint16 dns_response_code(uint16 flags) 528{ 529 return flags & 0xF; 530} 531