github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/cmd/fix/testdata/reflect.dnsmsg.go.in (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // DNS packet assembly. See RFC 1035. 6 // 7 // This is intended to support name resolution during net.Dial. 8 // It doesn't have to be blazing fast. 9 // 10 // Rather than write the usual handful of routines to pack and 11 // unpack every message that can appear on the wire, we use 12 // reflection to write a generic pack/unpack for structs and then 13 // use it. Thus, if in the future we need to define new message 14 // structs, no new pack/unpack/printing code needs to be written. 15 // 16 // The first half of this file defines the DNS message formats. 17 // The second half implements the conversion to and from wire format. 18 // A few of the structure elements have string tags to aid the 19 // generic pack/unpack routines. 20 // 21 // TODO(rsc): There are enough names defined in this file that they're all 22 // prefixed with dns. Perhaps put this in its own package later. 23 24 package net 25 26 import ( 27 "fmt" 28 "os" 29 "reflect" 30 ) 31 32 // Packet formats 33 34 // Wire constants. 35 const ( 36 // valid dnsRR_Header.Rrtype and dnsQuestion.qtype 37 dnsTypeA = 1 38 dnsTypeNS = 2 39 dnsTypeMD = 3 40 dnsTypeMF = 4 41 dnsTypeCNAME = 5 42 dnsTypeSOA = 6 43 dnsTypeMB = 7 44 dnsTypeMG = 8 45 dnsTypeMR = 9 46 dnsTypeNULL = 10 47 dnsTypeWKS = 11 48 dnsTypePTR = 12 49 dnsTypeHINFO = 13 50 dnsTypeMINFO = 14 51 dnsTypeMX = 15 52 dnsTypeTXT = 16 53 dnsTypeAAAA = 28 54 dnsTypeSRV = 33 55 56 // valid dnsQuestion.qtype only 57 dnsTypeAXFR = 252 58 dnsTypeMAILB = 253 59 dnsTypeMAILA = 254 60 dnsTypeALL = 255 61 62 // valid dnsQuestion.qclass 63 dnsClassINET = 1 64 dnsClassCSNET = 2 65 dnsClassCHAOS = 3 66 dnsClassHESIOD = 4 67 dnsClassANY = 255 68 69 // dnsMsg.rcode 70 dnsRcodeSuccess = 0 71 dnsRcodeFormatError = 1 72 dnsRcodeServerFailure = 2 73 dnsRcodeNameError = 3 74 dnsRcodeNotImplemented = 4 75 dnsRcodeRefused = 5 76 ) 77 78 // The wire format for the DNS packet header. 79 type dnsHeader struct { 80 Id uint16 81 Bits uint16 82 Qdcount, Ancount, Nscount, Arcount uint16 83 } 84 85 const ( 86 // dnsHeader.Bits 87 _QR = 1 << 15 // query/response (response=1) 88 _AA = 1 << 10 // authoritative 89 _TC = 1 << 9 // truncated 90 _RD = 1 << 8 // recursion desired 91 _RA = 1 << 7 // recursion available 92 ) 93 94 // DNS queries. 95 type dnsQuestion struct { 96 Name string "domain-name" // "domain-name" specifies encoding; see packers below 97 Qtype uint16 98 Qclass uint16 99 } 100 101 // DNS responses (resource records). 102 // There are many types of messages, 103 // but they all share the same header. 104 type dnsRR_Header struct { 105 Name string "domain-name" 106 Rrtype uint16 107 Class uint16 108 Ttl uint32 109 Rdlength uint16 // length of data after header 110 } 111 112 func (h *dnsRR_Header) Header() *dnsRR_Header { 113 return h 114 } 115 116 type dnsRR interface { 117 Header() *dnsRR_Header 118 } 119 120 // Specific DNS RR formats for each query type. 121 122 type dnsRR_CNAME struct { 123 Hdr dnsRR_Header 124 Cname string "domain-name" 125 } 126 127 func (rr *dnsRR_CNAME) Header() *dnsRR_Header { 128 return &rr.Hdr 129 } 130 131 type dnsRR_HINFO struct { 132 Hdr dnsRR_Header 133 Cpu string 134 Os string 135 } 136 137 func (rr *dnsRR_HINFO) Header() *dnsRR_Header { 138 return &rr.Hdr 139 } 140 141 type dnsRR_MB struct { 142 Hdr dnsRR_Header 143 Mb string "domain-name" 144 } 145 146 func (rr *dnsRR_MB) Header() *dnsRR_Header { 147 return &rr.Hdr 148 } 149 150 type dnsRR_MG struct { 151 Hdr dnsRR_Header 152 Mg string "domain-name" 153 } 154 155 func (rr *dnsRR_MG) Header() *dnsRR_Header { 156 return &rr.Hdr 157 } 158 159 type dnsRR_MINFO struct { 160 Hdr dnsRR_Header 161 Rmail string "domain-name" 162 Email string "domain-name" 163 } 164 165 func (rr *dnsRR_MINFO) Header() *dnsRR_Header { 166 return &rr.Hdr 167 } 168 169 type dnsRR_MR struct { 170 Hdr dnsRR_Header 171 Mr string "domain-name" 172 } 173 174 func (rr *dnsRR_MR) Header() *dnsRR_Header { 175 return &rr.Hdr 176 } 177 178 type dnsRR_MX struct { 179 Hdr dnsRR_Header 180 Pref uint16 181 Mx string "domain-name" 182 } 183 184 func (rr *dnsRR_MX) Header() *dnsRR_Header { 185 return &rr.Hdr 186 } 187 188 type dnsRR_NS struct { 189 Hdr dnsRR_Header 190 Ns string "domain-name" 191 } 192 193 func (rr *dnsRR_NS) Header() *dnsRR_Header { 194 return &rr.Hdr 195 } 196 197 type dnsRR_PTR struct { 198 Hdr dnsRR_Header 199 Ptr string "domain-name" 200 } 201 202 func (rr *dnsRR_PTR) Header() *dnsRR_Header { 203 return &rr.Hdr 204 } 205 206 type dnsRR_SOA struct { 207 Hdr dnsRR_Header 208 Ns string "domain-name" 209 Mbox string "domain-name" 210 Serial uint32 211 Refresh uint32 212 Retry uint32 213 Expire uint32 214 Minttl uint32 215 } 216 217 func (rr *dnsRR_SOA) Header() *dnsRR_Header { 218 return &rr.Hdr 219 } 220 221 type dnsRR_TXT struct { 222 Hdr dnsRR_Header 223 Txt string // not domain name 224 } 225 226 func (rr *dnsRR_TXT) Header() *dnsRR_Header { 227 return &rr.Hdr 228 } 229 230 type dnsRR_SRV struct { 231 Hdr dnsRR_Header 232 Priority uint16 233 Weight uint16 234 Port uint16 235 Target string "domain-name" 236 } 237 238 func (rr *dnsRR_SRV) Header() *dnsRR_Header { 239 return &rr.Hdr 240 } 241 242 type dnsRR_A struct { 243 Hdr dnsRR_Header 244 A uint32 "ipv4" 245 } 246 247 func (rr *dnsRR_A) Header() *dnsRR_Header { 248 return &rr.Hdr 249 } 250 251 type dnsRR_AAAA struct { 252 Hdr dnsRR_Header 253 AAAA [16]byte "ipv6" 254 } 255 256 func (rr *dnsRR_AAAA) Header() *dnsRR_Header { 257 return &rr.Hdr 258 } 259 260 // Packing and unpacking. 261 // 262 // All the packers and unpackers take a (msg []byte, off int) 263 // and return (off1 int, ok bool). If they return ok==false, they 264 // also return off1==len(msg), so that the next unpacker will 265 // also fail. This lets us avoid checks of ok until the end of a 266 // packing sequence. 267 268 // Map of constructors for each RR wire type. 269 var rr_mk = map[int]func() dnsRR{ 270 dnsTypeCNAME: func() dnsRR { return new(dnsRR_CNAME) }, 271 dnsTypeHINFO: func() dnsRR { return new(dnsRR_HINFO) }, 272 dnsTypeMB: func() dnsRR { return new(dnsRR_MB) }, 273 dnsTypeMG: func() dnsRR { return new(dnsRR_MG) }, 274 dnsTypeMINFO: func() dnsRR { return new(dnsRR_MINFO) }, 275 dnsTypeMR: func() dnsRR { return new(dnsRR_MR) }, 276 dnsTypeMX: func() dnsRR { return new(dnsRR_MX) }, 277 dnsTypeNS: func() dnsRR { return new(dnsRR_NS) }, 278 dnsTypePTR: func() dnsRR { return new(dnsRR_PTR) }, 279 dnsTypeSOA: func() dnsRR { return new(dnsRR_SOA) }, 280 dnsTypeTXT: func() dnsRR { return new(dnsRR_TXT) }, 281 dnsTypeSRV: func() dnsRR { return new(dnsRR_SRV) }, 282 dnsTypeA: func() dnsRR { return new(dnsRR_A) }, 283 dnsTypeAAAA: func() dnsRR { return new(dnsRR_AAAA) }, 284 } 285 286 // Pack a domain name s into msg[off:]. 287 // Domain names are a sequence of counted strings 288 // split at the dots. They end with a zero-length string. 289 func packDomainName(s string, msg []byte, off int) (off1 int, ok bool) { 290 // Add trailing dot to canonicalize name. 291 if n := len(s); n == 0 || s[n-1] != '.' { 292 s += "." 293 } 294 295 // Each dot ends a segment of the name. 296 // We trade each dot byte for a length byte. 297 // There is also a trailing zero. 298 // Check that we have all the space we need. 299 tot := len(s) + 1 300 if off+tot > len(msg) { 301 return len(msg), false 302 } 303 304 // Emit sequence of counted strings, chopping at dots. 305 begin := 0 306 for i := 0; i < len(s); i++ { 307 if s[i] == '.' { 308 if i-begin >= 1<<6 { // top two bits of length must be clear 309 return len(msg), false 310 } 311 msg[off] = byte(i - begin) 312 off++ 313 for j := begin; j < i; j++ { 314 msg[off] = s[j] 315 off++ 316 } 317 begin = i + 1 318 } 319 } 320 msg[off] = 0 321 off++ 322 return off, true 323 } 324 325 // Unpack a domain name. 326 // In addition to the simple sequences of counted strings above, 327 // domain names are allowed to refer to strings elsewhere in the 328 // packet, to avoid repeating common suffixes when returning 329 // many entries in a single domain. The pointers are marked 330 // by a length byte with the top two bits set. Ignoring those 331 // two bits, that byte and the next give a 14 bit offset from msg[0] 332 // where we should pick up the trail. 333 // Note that if we jump elsewhere in the packet, 334 // we return off1 == the offset after the first pointer we found, 335 // which is where the next record will start. 336 // In theory, the pointers are only allowed to jump backward. 337 // We let them jump anywhere and stop jumping after a while. 338 func unpackDomainName(msg []byte, off int) (s string, off1 int, ok bool) { 339 s = "" 340 ptr := 0 // number of pointers followed 341 Loop: 342 for { 343 if off >= len(msg) { 344 return "", len(msg), false 345 } 346 c := int(msg[off]) 347 off++ 348 switch c & 0xC0 { 349 case 0x00: 350 if c == 0x00 { 351 // end of name 352 break Loop 353 } 354 // literal string 355 if off+c > len(msg) { 356 return "", len(msg), false 357 } 358 s += string(msg[off:off+c]) + "." 359 off += c 360 case 0xC0: 361 // pointer to somewhere else in msg. 362 // remember location after first ptr, 363 // since that's how many bytes we consumed. 364 // also, don't follow too many pointers -- 365 // maybe there's a loop. 366 if off >= len(msg) { 367 return "", len(msg), false 368 } 369 c1 := msg[off] 370 off++ 371 if ptr == 0 { 372 off1 = off 373 } 374 if ptr++; ptr > 10 { 375 return "", len(msg), false 376 } 377 off = (c^0xC0)<<8 | int(c1) 378 default: 379 // 0x80 and 0x40 are reserved 380 return "", len(msg), false 381 } 382 } 383 if ptr == 0 { 384 off1 = off 385 } 386 return s, off1, true 387 } 388 389 // TODO(rsc): Move into generic library? 390 // Pack a reflect.StructValue into msg. Struct members can only be uint16, uint32, string, 391 // [n]byte, and other (often anonymous) structs. 392 func packStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, ok bool) { 393 for i := 0; i < val.NumField(); i++ { 394 f := val.Type().(*reflect.StructType).Field(i) 395 switch fv := val.Field(i).(type) { 396 default: 397 BadType: 398 fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type) 399 return len(msg), false 400 case *reflect.StructValue: 401 off, ok = packStructValue(fv, msg, off) 402 case *reflect.UintValue: 403 i := fv.Get() 404 switch fv.Type().Kind() { 405 default: 406 goto BadType 407 case reflect.Uint16: 408 if off+2 > len(msg) { 409 return len(msg), false 410 } 411 msg[off] = byte(i >> 8) 412 msg[off+1] = byte(i) 413 off += 2 414 case reflect.Uint32: 415 if off+4 > len(msg) { 416 return len(msg), false 417 } 418 msg[off] = byte(i >> 24) 419 msg[off+1] = byte(i >> 16) 420 msg[off+2] = byte(i >> 8) 421 msg[off+3] = byte(i) 422 off += 4 423 } 424 case *reflect.ArrayValue: 425 if fv.Type().(*reflect.ArrayType).Elem().Kind() != reflect.Uint8 { 426 goto BadType 427 } 428 n := fv.Len() 429 if off+n > len(msg) { 430 return len(msg), false 431 } 432 reflect.Copy(reflect.NewValue(msg[off:off+n]).(*reflect.SliceValue), fv) 433 off += n 434 case *reflect.StringValue: 435 // There are multiple string encodings. 436 // The tag distinguishes ordinary strings from domain names. 437 s := fv.Get() 438 switch f.Tag { 439 default: 440 fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag) 441 return len(msg), false 442 case "domain-name": 443 off, ok = packDomainName(s, msg, off) 444 if !ok { 445 return len(msg), false 446 } 447 case "": 448 // Counted string: 1 byte length. 449 if len(s) > 255 || off+1+len(s) > len(msg) { 450 return len(msg), false 451 } 452 msg[off] = byte(len(s)) 453 off++ 454 off += copy(msg[off:], s) 455 } 456 } 457 } 458 return off, true 459 } 460 461 func structValue(any interface{}) *reflect.StructValue { 462 return reflect.NewValue(any).(*reflect.PtrValue).Elem().(*reflect.StructValue) 463 } 464 465 func packStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) { 466 off, ok = packStructValue(structValue(any), msg, off) 467 return off, ok 468 } 469 470 // TODO(rsc): Move into generic library? 471 // Unpack a reflect.StructValue from msg. 472 // Same restrictions as packStructValue. 473 func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, ok bool) { 474 for i := 0; i < val.NumField(); i++ { 475 f := val.Type().(*reflect.StructType).Field(i) 476 switch fv := val.Field(i).(type) { 477 default: 478 BadType: 479 fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type) 480 return len(msg), false 481 case *reflect.StructValue: 482 off, ok = unpackStructValue(fv, msg, off) 483 case *reflect.UintValue: 484 switch fv.Type().Kind() { 485 default: 486 goto BadType 487 case reflect.Uint16: 488 if off+2 > len(msg) { 489 return len(msg), false 490 } 491 i := uint16(msg[off])<<8 | uint16(msg[off+1]) 492 fv.Set(uint64(i)) 493 off += 2 494 case reflect.Uint32: 495 if off+4 > len(msg) { 496 return len(msg), false 497 } 498 i := uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | uint32(msg[off+2])<<8 | uint32(msg[off+3]) 499 fv.Set(uint64(i)) 500 off += 4 501 } 502 case *reflect.ArrayValue: 503 if fv.Type().(*reflect.ArrayType).Elem().Kind() != reflect.Uint8 { 504 goto BadType 505 } 506 n := fv.Len() 507 if off+n > len(msg) { 508 return len(msg), false 509 } 510 reflect.Copy(fv, reflect.NewValue(msg[off:off+n]).(*reflect.SliceValue)) 511 off += n 512 case *reflect.StringValue: 513 var s string 514 switch f.Tag { 515 default: 516 fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag) 517 return len(msg), false 518 case "domain-name": 519 s, off, ok = unpackDomainName(msg, off) 520 if !ok { 521 return len(msg), false 522 } 523 case "": 524 if off >= len(msg) || off+1+int(msg[off]) > len(msg) { 525 return len(msg), false 526 } 527 n := int(msg[off]) 528 off++ 529 b := make([]byte, n) 530 for i := 0; i < n; i++ { 531 b[i] = msg[off+i] 532 } 533 off += n 534 s = string(b) 535 } 536 fv.Set(s) 537 } 538 } 539 return off, true 540 } 541 542 func unpackStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) { 543 off, ok = unpackStructValue(structValue(any), msg, off) 544 return off, ok 545 } 546 547 // Generic struct printer. 548 // Doesn't care about the string tag "domain-name", 549 // but does look for an "ipv4" tag on uint32 variables 550 // and the "ipv6" tag on array variables, 551 // printing them as IP addresses. 552 func printStructValue(val *reflect.StructValue) string { 553 s := "{" 554 for i := 0; i < val.NumField(); i++ { 555 if i > 0 { 556 s += ", " 557 } 558 f := val.Type().(*reflect.StructType).Field(i) 559 if !f.Anonymous { 560 s += f.Name + "=" 561 } 562 fval := val.Field(i) 563 if fv, ok := fval.(*reflect.StructValue); ok { 564 s += printStructValue(fv) 565 } else if fv, ok := fval.(*reflect.UintValue); ok && f.Tag == "ipv4" { 566 i := fv.Get() 567 s += IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String() 568 } else if fv, ok := fval.(*reflect.ArrayValue); ok && f.Tag == "ipv6" { 569 i := fv.Interface().([]byte) 570 s += IP(i).String() 571 } else { 572 s += fmt.Sprint(fval.Interface()) 573 } 574 } 575 s += "}" 576 return s 577 } 578 579 func printStruct(any interface{}) string { return printStructValue(structValue(any)) } 580 581 // Resource record packer. 582 func packRR(rr dnsRR, msg []byte, off int) (off2 int, ok bool) { 583 var off1 int 584 // pack twice, once to find end of header 585 // and again to find end of packet. 586 // a bit inefficient but this doesn't need to be fast. 587 // off1 is end of header 588 // off2 is end of rr 589 off1, ok = packStruct(rr.Header(), msg, off) 590 off2, ok = packStruct(rr, msg, off) 591 if !ok { 592 return len(msg), false 593 } 594 // pack a third time; redo header with correct data length 595 rr.Header().Rdlength = uint16(off2 - off1) 596 packStruct(rr.Header(), msg, off) 597 return off2, true 598 } 599 600 // Resource record unpacker. 601 func unpackRR(msg []byte, off int) (rr dnsRR, off1 int, ok bool) { 602 // unpack just the header, to find the rr type and length 603 var h dnsRR_Header 604 off0 := off 605 if off, ok = unpackStruct(&h, msg, off); !ok { 606 return nil, len(msg), false 607 } 608 end := off + int(h.Rdlength) 609 610 // make an rr of that type and re-unpack. 611 // again inefficient but doesn't need to be fast. 612 mk, known := rr_mk[int(h.Rrtype)] 613 if !known { 614 return &h, end, true 615 } 616 rr = mk() 617 off, ok = unpackStruct(rr, msg, off0) 618 if off != end { 619 return &h, end, true 620 } 621 return rr, off, ok 622 } 623 624 // Usable representation of a DNS packet. 625 626 // A manually-unpacked version of (id, bits). 627 // This is in its own struct for easy printing. 628 type dnsMsgHdr struct { 629 id uint16 630 response bool 631 opcode int 632 authoritative bool 633 truncated bool 634 recursion_desired bool 635 recursion_available bool 636 rcode int 637 } 638 639 type dnsMsg struct { 640 dnsMsgHdr 641 question []dnsQuestion 642 answer []dnsRR 643 ns []dnsRR 644 extra []dnsRR 645 } 646 647 func (dns *dnsMsg) Pack() (msg []byte, ok bool) { 648 var dh dnsHeader 649 650 // Convert convenient dnsMsg into wire-like dnsHeader. 651 dh.Id = dns.id 652 dh.Bits = uint16(dns.opcode)<<11 | uint16(dns.rcode) 653 if dns.recursion_available { 654 dh.Bits |= _RA 655 } 656 if dns.recursion_desired { 657 dh.Bits |= _RD 658 } 659 if dns.truncated { 660 dh.Bits |= _TC 661 } 662 if dns.authoritative { 663 dh.Bits |= _AA 664 } 665 if dns.response { 666 dh.Bits |= _QR 667 } 668 669 // Prepare variable sized arrays. 670 question := dns.question 671 answer := dns.answer 672 ns := dns.ns 673 extra := dns.extra 674 675 dh.Qdcount = uint16(len(question)) 676 dh.Ancount = uint16(len(answer)) 677 dh.Nscount = uint16(len(ns)) 678 dh.Arcount = uint16(len(extra)) 679 680 // Could work harder to calculate message size, 681 // but this is far more than we need and not 682 // big enough to hurt the allocator. 683 msg = make([]byte, 2000) 684 685 // Pack it in: header and then the pieces. 686 off := 0 687 off, ok = packStruct(&dh, msg, off) 688 for i := 0; i < len(question); i++ { 689 off, ok = packStruct(&question[i], msg, off) 690 } 691 for i := 0; i < len(answer); i++ { 692 off, ok = packRR(answer[i], msg, off) 693 } 694 for i := 0; i < len(ns); i++ { 695 off, ok = packRR(ns[i], msg, off) 696 } 697 for i := 0; i < len(extra); i++ { 698 off, ok = packRR(extra[i], msg, off) 699 } 700 if !ok { 701 return nil, false 702 } 703 return msg[0:off], true 704 } 705 706 func (dns *dnsMsg) Unpack(msg []byte) bool { 707 // Header. 708 var dh dnsHeader 709 off := 0 710 var ok bool 711 if off, ok = unpackStruct(&dh, msg, off); !ok { 712 return false 713 } 714 dns.id = dh.Id 715 dns.response = (dh.Bits & _QR) != 0 716 dns.opcode = int(dh.Bits>>11) & 0xF 717 dns.authoritative = (dh.Bits & _AA) != 0 718 dns.truncated = (dh.Bits & _TC) != 0 719 dns.recursion_desired = (dh.Bits & _RD) != 0 720 dns.recursion_available = (dh.Bits & _RA) != 0 721 dns.rcode = int(dh.Bits & 0xF) 722 723 // Arrays. 724 dns.question = make([]dnsQuestion, dh.Qdcount) 725 dns.answer = make([]dnsRR, dh.Ancount) 726 dns.ns = make([]dnsRR, dh.Nscount) 727 dns.extra = make([]dnsRR, dh.Arcount) 728 729 for i := 0; i < len(dns.question); i++ { 730 off, ok = unpackStruct(&dns.question[i], msg, off) 731 } 732 for i := 0; i < len(dns.answer); i++ { 733 dns.answer[i], off, ok = unpackRR(msg, off) 734 } 735 for i := 0; i < len(dns.ns); i++ { 736 dns.ns[i], off, ok = unpackRR(msg, off) 737 } 738 for i := 0; i < len(dns.extra); i++ { 739 dns.extra[i], off, ok = unpackRR(msg, off) 740 } 741 if !ok { 742 return false 743 } 744 // if off != len(msg) { 745 // println("extra bytes in dns packet", off, "<", len(msg)); 746 // } 747 return true 748 } 749 750 func (dns *dnsMsg) String() string { 751 s := "DNS: " + printStruct(&dns.dnsMsgHdr) + "\n" 752 if len(dns.question) > 0 { 753 s += "-- Questions\n" 754 for i := 0; i < len(dns.question); i++ { 755 s += printStruct(&dns.question[i]) + "\n" 756 } 757 } 758 if len(dns.answer) > 0 { 759 s += "-- Answers\n" 760 for i := 0; i < len(dns.answer); i++ { 761 s += printStruct(dns.answer[i]) + "\n" 762 } 763 } 764 if len(dns.ns) > 0 { 765 s += "-- Name servers\n" 766 for i := 0; i < len(dns.ns); i++ { 767 s += printStruct(dns.ns[i]) + "\n" 768 } 769 } 770 if len(dns.extra) > 0 { 771 s += "-- Extra\n" 772 for i := 0; i < len(dns.extra); i++ { 773 s += printStruct(dns.extra[i]) + "\n" 774 } 775 } 776 return s 777 }