001/* 002 * Copyright 2015-2018 the original author or authors 003 * 004 * This software is licensed under the Apache License, Version 2.0, 005 * the GNU Lesser General Public License version 2 or later ("LGPL") 006 * and the WTFPL. 007 * You may choose either license to govern your use of this software only 008 * upon the condition that you accept all of the terms of either 009 * the Apache License 2.0, the LGPL 2.1+ or the WTFPL. 010 */ 011package org.minidns.dnsmessage; 012 013import org.minidns.edns.Edns; 014import org.minidns.record.Data; 015import org.minidns.record.OPT; 016import org.minidns.record.Record; 017import org.minidns.record.Record.TYPE; 018 019import java.io.ByteArrayInputStream; 020import java.io.ByteArrayOutputStream; 021import java.io.DataInputStream; 022import java.io.DataOutputStream; 023import java.io.IOException; 024import java.net.DatagramPacket; 025import java.net.InetAddress; 026import java.nio.ByteBuffer; 027import java.util.ArrayList; 028import java.util.Arrays; 029import java.util.Collection; 030import java.util.Collections; 031import java.util.Date; 032import java.util.HashMap; 033import java.util.HashSet; 034import java.util.List; 035import java.util.Map; 036import java.util.Set; 037import java.util.logging.Level; 038import java.util.logging.Logger; 039 040/** 041 * A DNS message as defined by RFC 1035. The message consists of a header and 042 * 4 sections: question, answer, nameserver and addition resource record 043 * section. 044 * A message can either be parsed ({@link #DnsMessage(byte[])}) or serialized 045 * ({@link DnsMessage#toArray()}). 046 * 047 * @see <a href="https://www.ietf.org/rfc/rfc1035.txt">RFC 1035</a> 048 */ 049public class DnsMessage { 050 051 private static final Logger LOGGER = Logger.getLogger(DnsMessage.class.getName()); 052 053 /** 054 * Possible DNS response codes. 055 * 056 * @see <a href= 057 * "http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6"> 058 * IANA Domain Name System (DNS) Paramters - DNS RCODEs</a> 059 * @see <a href="http://tools.ietf.org/html/rfc6895#section-2.3">RFC 6895 § 2.3</a> 060 */ 061 public static enum RESPONSE_CODE { 062 NO_ERROR(0), 063 FORMAT_ERR(1), 064 SERVER_FAIL(2), 065 NX_DOMAIN(3), 066 NO_IMP(4), 067 REFUSED(5), 068 YXDOMAIN(6), 069 YXRRSET(7), 070 NXRRSET(8), 071 NOT_AUTH(9), 072 NOT_ZONE(10), 073 BADVERS_BADSIG(16), 074 BADKEY(17), 075 BADTIME(18), 076 BADMODE(19), 077 BADNAME(20), 078 BADALG(21), 079 BADTRUNC(22), 080 BADCOOKIE(23), 081 ; 082 083 /** 084 * Reverse lookup table for response codes. 085 */ 086 private final static Map<Integer, RESPONSE_CODE> INVERSE_LUT = new HashMap<>(RESPONSE_CODE.values().length); 087 088 static { 089 for (RESPONSE_CODE responseCode : RESPONSE_CODE.values()) { 090 INVERSE_LUT.put((int) responseCode.value, responseCode); 091 } 092 } 093 094 /** 095 * The response code value. 096 */ 097 private final byte value; 098 099 /** 100 * Create a new response code. 101 * 102 * @param value The response code value. 103 */ 104 private RESPONSE_CODE(int value) { 105 this.value = (byte) value; 106 } 107 108 /** 109 * Retrieve the byte value of the response code. 110 * 111 * @return the response code. 112 */ 113 public byte getValue() { 114 return value; 115 } 116 117 /** 118 * Retrieve the response code for a byte value. 119 * 120 * @param value The byte value. 121 * @return The symbolic response code or null. 122 * @throws IllegalArgumentException if the value is not in the range of 0..15. 123 */ 124 public static RESPONSE_CODE getResponseCode(int value) throws IllegalArgumentException { 125 if (value < 0 || value > 65535) { 126 throw new IllegalArgumentException(); 127 } 128 return INVERSE_LUT.get(value); 129 } 130 131 } 132 133 /** 134 * Symbolic DNS Opcode values. 135 * 136 * @see <a href= 137 * "http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-5"> 138 * IANA Domain Name System (DNS) Paramters - DNS OpCodes</a> 139 */ 140 public static enum OPCODE { 141 QUERY, 142 INVERSE_QUERY, 143 STATUS, 144 UNASSIGNED3, 145 NOTIFY, 146 UPDATE, 147 ; 148 149 /** 150 * Lookup table for for opcode resolution. 151 */ 152 private final static OPCODE INVERSE_LUT[] = new OPCODE[OPCODE.values().length]; 153 154 static { 155 for (OPCODE opcode : OPCODE.values()) { 156 if (INVERSE_LUT[opcode.getValue()] != null) { 157 throw new IllegalStateException(); 158 } 159 INVERSE_LUT[opcode.getValue()] = opcode; 160 } 161 } 162 163 /** 164 * The value of this opcode. 165 */ 166 private final byte value; 167 168 /** 169 * Create a new opcode for a given byte value. 170 * 171 */ 172 private OPCODE() { 173 this.value = (byte) this.ordinal(); 174 } 175 176 /** 177 * Retrieve the byte value of this opcode. 178 * 179 * @return The byte value of this opcode. 180 */ 181 public byte getValue() { 182 return value; 183 } 184 185 /** 186 * Retrieve the symbolic name of an opcode byte. 187 * 188 * @param value The byte value of the opcode. 189 * @return The symbolic opcode or null. 190 * @throws IllegalArgumentException If the byte value is not in the 191 * range 0..15. 192 */ 193 public static OPCODE getOpcode(int value) throws IllegalArgumentException { 194 if (value < 0 || value > 15) { 195 throw new IllegalArgumentException(); 196 } 197 if (value >= INVERSE_LUT.length) { 198 return null; 199 } 200 return INVERSE_LUT[value]; 201 } 202 203 } 204 205 /** 206 * The DNS message id. 207 */ 208 public final int id; 209 210 /** 211 * The DNS message opcode. 212 */ 213 public final OPCODE opcode; 214 215 /** 216 * The response code of this dns message. 217 */ 218 public final RESPONSE_CODE responseCode; 219 220 /** 221 * The QR flag of the DNS message header. Note that this will be <code>true</code> if the message is a 222 * <b>response</b> and <code>false</code> if it is a <b>query</b>. 223 * 224 * @see <a href="https://www.ietf.org/rfc/rfc1035.txt">RFC 1035 § 4.1.1</a> 225 */ 226 public final boolean qr; 227 228 /** 229 * True if this is a authorative response. 230 */ 231 public final boolean authoritativeAnswer; 232 233 /** 234 * True if message is truncated. Then TCP should be used. 235 */ 236 public final boolean truncated; 237 238 /** 239 * True if the server should recurse. 240 */ 241 public final boolean recursionDesired; 242 243 /** 244 * True if recursion is possible. 245 */ 246 public final boolean recursionAvailable; 247 248 /** 249 * True if the server regarded the response as authentic. 250 */ 251 public final boolean authenticData; 252 253 /** 254 * True if the server should not perform DNSSEC validation before returning the result. 255 */ 256 public final boolean checkingDisabled; 257 258 /** 259 * The question section content. Usually there will be only one question. 260 * <p> 261 * This list is unmodifiable. 262 * </p> 263 */ 264 public final List<Question> questions; 265 266 /** 267 * The answers section records. Note that it is not guaranteed that all records found in this section will be direct 268 * answers to the question in the query. If DNSSEC is used, then this section also contains the RRSIG record. 269 * <p> 270 * This list is unmodifiable. 271 * </p> 272 */ 273 public final List<Record<? extends Data>> answerSection; 274 275 /** 276 * The Authority Section. Note that it is not guaranteed that this section only contains nameserver records. If DNSSEC is used, then this section could also contain a NSEC(3) record. 277 * <p> 278 * This list is unmodifiable. 279 * </p> 280 */ 281 public final List<Record<? extends Data>> authoritySection; 282 283 /** 284 * The additional section. It eventually contains RRs which relate to the query. 285 * <p> 286 * This list is unmodifiable. 287 * </p> 288 */ 289 public final List<Record<? extends Data>> additionalSection; 290 291 public final int optRrPosition; 292 293 /** 294 * The optional but very common EDNS information. Note that this field is lazily populated. 295 * 296 */ 297 private Edns edns; 298 299 /** 300 * The receive timestamp. Set only if this message was created via parse. 301 * This should be used to evaluate TTLs. 302 */ 303 public final long receiveTimestamp; 304 305 protected DnsMessage(Builder builder) { 306 this.id = builder.id; 307 this.opcode = builder.opcode; 308 this.responseCode = builder.responseCode; 309 this.receiveTimestamp = builder.receiveTimestamp; 310 this.qr = builder.query; 311 this.authoritativeAnswer = builder.authoritativeAnswer; 312 this.truncated = builder.truncated; 313 this.recursionDesired = builder.recursionDesired; 314 this.recursionAvailable = builder.recursionAvailable; 315 this.authenticData = builder.authenticData; 316 this.checkingDisabled = builder.checkingDisabled; 317 318 if (builder.questions == null) { 319 this.questions = Collections.emptyList(); 320 } else { 321 List<Question> q = new ArrayList<>(builder.questions.size()); 322 q.addAll(builder.questions); 323 this.questions = Collections.unmodifiableList(q); 324 } 325 326 if (builder.answers == null) { 327 this.answerSection = Collections.emptyList(); 328 } else { 329 List<Record<? extends Data>> a = new ArrayList<>(builder.answers.size()); 330 a.addAll(builder.answers); 331 this.answerSection = Collections.unmodifiableList(a); 332 } 333 334 if (builder.nameserverRecords == null) { 335 this.authoritySection = Collections.emptyList(); 336 } else { 337 List<Record<? extends Data>> n = new ArrayList<>(builder.nameserverRecords.size()); 338 n.addAll(builder.nameserverRecords); 339 this.authoritySection = Collections.unmodifiableList(n); 340 } 341 342 if (builder.additionalResourceRecords == null && builder.ednsBuilder == null) { 343 this.additionalSection = Collections.emptyList(); 344 } else { 345 int size = 0; 346 if (builder.additionalResourceRecords != null) { 347 size += builder.additionalResourceRecords.size(); 348 } 349 if (builder.ednsBuilder != null) { 350 size++; 351 } 352 List<Record<? extends Data>> a = new ArrayList<>(size); 353 if (builder.additionalResourceRecords != null) { 354 a.addAll(builder.additionalResourceRecords); 355 } 356 if (builder.ednsBuilder != null) { 357 Edns edns = builder.ednsBuilder.build(); 358 this.edns = edns; 359 a.add(edns.asRecord()); 360 } 361 this.additionalSection = Collections.unmodifiableList(a); 362 } 363 364 optRrPosition = getOptRrPosition(this.additionalSection); 365 366 if (optRrPosition != -1) { 367 // Verify that there are no further OPT records but the one we already found. 368 for (int i = optRrPosition + 1; i < this.additionalSection.size(); i++) { 369 if (this.additionalSection.get(i).type == TYPE.OPT) { 370 throw new IllegalArgumentException("There must be only one OPT pseudo RR in the additional section"); 371 } 372 } 373 } 374 375 // TODO Add verification of dns message state here 376 } 377 378 /** 379 * Build a DNS Message based on a binary DNS message. 380 * 381 * @param data The DNS message data. 382 * @throws IOException On read errors. 383 */ 384 public DnsMessage(byte data[]) throws IOException { 385 ByteArrayInputStream bis = new ByteArrayInputStream(data); 386 DataInputStream dis = new DataInputStream(bis); 387 id = dis.readUnsignedShort(); 388 int header = dis.readUnsignedShort(); 389 qr = ((header >> 15) & 1) == 1; 390 opcode = OPCODE.getOpcode((header >> 11) & 0xf); 391 authoritativeAnswer = ((header >> 10) & 1) == 1; 392 truncated = ((header >> 9) & 1) == 1; 393 recursionDesired = ((header >> 8) & 1) == 1; 394 recursionAvailable = ((header >> 7) & 1) == 1; 395 authenticData = ((header >> 5) & 1) == 1; 396 checkingDisabled = ((header >> 4) & 1) == 1; 397 responseCode = RESPONSE_CODE.getResponseCode(header & 0xf); 398 receiveTimestamp = System.currentTimeMillis(); 399 int questionCount = dis.readUnsignedShort(); 400 int answerCount = dis.readUnsignedShort(); 401 int nameserverCount = dis.readUnsignedShort(); 402 int additionalResourceRecordCount = dis.readUnsignedShort(); 403 questions = new ArrayList<>(questionCount); 404 for (int i = 0; i < questionCount; i++) { 405 questions.add(new Question(dis, data)); 406 } 407 answerSection = new ArrayList<>(answerCount); 408 for (int i = 0; i < answerCount; i++) { 409 answerSection.add(Record.parse(dis, data)); 410 } 411 authoritySection = new ArrayList<>(nameserverCount); 412 for (int i = 0; i < nameserverCount; i++) { 413 authoritySection.add(Record.parse(dis, data)); 414 } 415 additionalSection = new ArrayList<>(additionalResourceRecordCount); 416 for (int i = 0; i < additionalResourceRecordCount; i++) { 417 additionalSection.add(Record.parse(dis, data)); 418 } 419 optRrPosition = getOptRrPosition(additionalSection); 420 } 421 422 /** 423 * Constructs an normalized version of the given DnsMessage by setting the id to '0'. 424 * 425 * @param message the message of which normalized version should be constructed. 426 */ 427 private DnsMessage(DnsMessage message) { 428 id = 0; 429 qr = message.qr; 430 opcode = message.opcode; 431 authoritativeAnswer = message.authoritativeAnswer; 432 truncated = message.truncated; 433 recursionDesired = message.recursionDesired; 434 recursionAvailable = message.recursionAvailable; 435 authenticData = message.authenticData; 436 checkingDisabled = message.checkingDisabled; 437 responseCode = message.responseCode; 438 receiveTimestamp = message.receiveTimestamp; 439 questions = message.questions; 440 answerSection = message.answerSection; 441 authoritySection = message.authoritySection; 442 additionalSection = message.additionalSection; 443 optRrPosition = message.optRrPosition; 444 } 445 446 private static int getOptRrPosition(List<Record<? extends Data>> additionalSection) { 447 int optRrPosition = -1; 448 for (int i = 0; i < additionalSection.size(); i++) { 449 Record<? extends Data> record = additionalSection.get(i); 450 if (record.type == Record.TYPE.OPT) { 451 optRrPosition = i; 452 break; 453 } 454 } 455 return optRrPosition; 456 } 457 458 /** 459 * Generate a binary dns packet out of this message. 460 * 461 * @return byte[] the binary representation. 462 */ 463 public byte[] toArray() { 464 return serialize().clone(); 465 } 466 467 public DatagramPacket asDatagram(InetAddress address, int port) { 468 byte[] bytes = serialize(); 469 return new DatagramPacket(bytes, bytes.length, address, port); 470 } 471 472 public void writeTo(DataOutputStream dataOutputStream) throws IOException { 473 byte[] bytes = serialize(); 474 dataOutputStream.writeShort(bytes.length); 475 dataOutputStream.write(bytes); 476 } 477 478 public ByteBuffer getInByteBuffer() { 479 byte[] bytes = serialize().clone(); 480 return ByteBuffer.wrap(bytes); 481 } 482 483 private byte[] byteCache; 484 485 private byte[] serialize() { 486 if (byteCache != null) { 487 return byteCache; 488 } 489 490 ByteArrayOutputStream baos = new ByteArrayOutputStream(512); 491 DataOutputStream dos = new DataOutputStream(baos); 492 int header = calculateHeaderBitmap(); 493 try { 494 dos.writeShort((short) id); 495 dos.writeShort((short) header); 496 if (questions == null) { 497 dos.writeShort(0); 498 } else { 499 dos.writeShort((short) questions.size()); 500 } 501 if (answerSection == null) { 502 dos.writeShort(0); 503 } else { 504 dos.writeShort((short) answerSection.size()); 505 } 506 if (authoritySection == null) { 507 dos.writeShort(0); 508 } else { 509 dos.writeShort((short) authoritySection.size()); 510 } 511 if (additionalSection == null) { 512 dos.writeShort(0); 513 } else { 514 dos.writeShort((short) additionalSection.size()); 515 } 516 if (questions != null) { 517 for (Question question : questions) { 518 dos.write(question.toByteArray()); 519 } 520 } 521 if (answerSection != null) { 522 for (Record<? extends Data> answer : answerSection) { 523 dos.write(answer.toByteArray()); 524 } 525 } 526 if (authoritySection != null) { 527 for (Record<? extends Data> nameserverRecord : authoritySection) { 528 dos.write(nameserverRecord.toByteArray()); 529 } 530 } 531 if (additionalSection != null) { 532 for (Record<? extends Data> additionalResourceRecord : additionalSection) { 533 dos.write(additionalResourceRecord.toByteArray()); 534 } 535 } 536 dos.flush(); 537 } catch (IOException e) { 538 // Should never happen. 539 throw new AssertionError(e); 540 } 541 byteCache = baos.toByteArray(); 542 return byteCache; 543 } 544 545 int calculateHeaderBitmap() { 546 int header = 0; 547 if (qr) { 548 header += 1 << 15; 549 } 550 if (opcode != null) { 551 header += opcode.getValue() << 11; 552 } 553 if (authoritativeAnswer) { 554 header += 1 << 10; 555 } 556 if (truncated) { 557 header += 1 << 9; 558 } 559 if (recursionDesired) { 560 header += 1 << 8; 561 } 562 if (recursionAvailable) { 563 header += 1 << 7; 564 } 565 if (authenticData) { 566 header += 1 << 5; 567 } 568 if (checkingDisabled) { 569 header += 1 << 4; 570 } 571 if (responseCode != null) { 572 header += responseCode.getValue(); 573 } 574 return header; 575 } 576 577 public Question getQuestion() { 578 return questions.get(0); 579 } 580 581 /** 582 * Copy the questions found in the question section. 583 * 584 * @return a copy of the question section questions. 585 * @see #questions 586 */ 587 public List<Question> copyQuestions() { 588 List<Question> copy = new ArrayList<>(questions.size()); 589 copy.addAll(questions); 590 return copy; 591 } 592 593 /** 594 * Copy the records found in the answer section into a new list. 595 * 596 * @return a copy of the answer section records. 597 * @see #answerSection 598 */ 599 public List<Record<? extends Data>> copyAnswers() { 600 List<Record<? extends Data>> res = new ArrayList<>(answerSection.size()); 601 res.addAll(answerSection); 602 return res; 603 } 604 605 /** 606 * Copy the records found in the authority section into a new list. 607 * 608 * @return a copy of the authority section records. 609 * @see #authoritySection 610 */ 611 public List<Record<? extends Data>> copyAuthority() { 612 List<Record<? extends Data>> res = new ArrayList<>(authoritySection.size()); 613 res.addAll(authoritySection); 614 return res; 615 } 616 617 public Edns getEdns() { 618 if (edns != null) return edns; 619 620 Record<OPT> optRecord = getOptPseudoRecord(); 621 if (optRecord == null) return null; 622 edns = new Edns(optRecord); 623 return edns; 624 } 625 626 @SuppressWarnings("unchecked") 627 public Record<OPT> getOptPseudoRecord() { 628 if (optRrPosition == -1) return null; 629 return (Record<OPT>) additionalSection.get(optRrPosition); 630 } 631 632 /** 633 * Check if the EDNS DO (DNSSEC OK) flag is set. 634 * 635 * @return true if the DO flag is set. 636 */ 637 public boolean isDnssecOk() { 638 Edns edns = getEdns(); 639 if (edns == null) 640 return false; 641 642 return edns.dnssecOk; 643 } 644 645 private String toStringCache; 646 647 @Override 648 public String toString() { 649 if (toStringCache != null) return toStringCache; 650 651 StringBuilder sb = new StringBuilder("DnsMessage") 652 .append('(').append(id).append(' ') 653 .append(opcode).append(' ') 654 .append(responseCode).append(' '); 655 if (qr) { 656 sb.append("resp[qr=1]"); 657 } else { 658 sb.append("query[qr=0]"); 659 } 660 if (authoritativeAnswer) sb.append(" aa"); 661 if (truncated) sb.append(" tr"); 662 if (recursionDesired) sb.append(" rd"); 663 if (recursionAvailable) sb.append(" ra"); 664 if (authenticData) sb.append(" ad"); 665 if (checkingDisabled) sb.append(" cd"); 666 sb.append(")\n"); 667 if (questions != null) { 668 for (Question question : questions) { 669 sb.append("[Q: ").append(question).append("]\n"); 670 } 671 } 672 if (answerSection != null) { 673 for (Record<? extends Data> record : answerSection) { 674 sb.append("[A: ").append(record).append("]\n"); 675 } 676 } 677 if (authoritySection != null) { 678 for (Record<? extends Data> record : authoritySection) { 679 sb.append("[N: ").append(record).append("]\n"); 680 } 681 } 682 if (additionalSection != null) { 683 for (Record<? extends Data> record : additionalSection) { 684 sb.append("[X: "); 685 Edns edns = Edns.fromRecord(record); 686 if (edns != null) { 687 sb.append(edns.toString()); 688 } else { 689 sb.append(record); 690 } 691 sb.append("]\n"); 692 } 693 } 694 695 // Strip trailing newline. 696 if (sb.charAt(sb.length() - 1) == '\n') { 697 sb.setLength(sb.length() - 1); 698 } 699 700 toStringCache = sb.toString(); 701 return toStringCache; 702 } 703 704 private String terminalOutputCache; 705 706 /** 707 * Format the DnsMessage object in a way suitable for terminal output. 708 * The format is loosely based on the output provided by {@code dig}. 709 * 710 * @return This message as a String suitable for terminal output. 711 */ 712 public String asTerminalOutput() { 713 if (terminalOutputCache != null) return terminalOutputCache; 714 715 StringBuilder sb = new StringBuilder(";; ->>HEADER<<-") 716 .append(" opcode: ").append(opcode) 717 .append(", status: ").append(responseCode) 718 .append(", id: ").append(id).append("\n") 719 .append(";; flags:"); 720 if (!qr) sb.append(" qr"); 721 if (authoritativeAnswer) sb.append(" aa"); 722 if (truncated) sb.append(" tr"); 723 if (recursionDesired) sb.append(" rd"); 724 if (recursionAvailable) sb.append(" ra"); 725 if (authenticData) sb.append(" ad"); 726 if (checkingDisabled) sb.append(" cd"); 727 sb.append("; QUERY: ").append(questions.size()) 728 .append(", ANSWER: ").append(answerSection.size()) 729 .append(", AUTHORITY: ").append(authoritySection.size()) 730 .append(", ADDITIONAL: ").append(additionalSection.size()) 731 .append("\n\n"); 732 for (Record<? extends Data> record : additionalSection) { 733 Edns edns = Edns.fromRecord(record); 734 if (edns != null) { 735 sb.append(";; OPT PSEUDOSECTION:\n; ").append(edns.asTerminalOutput()); 736 break; 737 } 738 } 739 if (questions.size() != 0) { 740 sb.append(";; QUESTION SECTION:\n"); 741 for (Question question : questions) { 742 sb.append(';').append(question.toString()).append('\n'); 743 } 744 } 745 if (authoritySection.size() != 0) { 746 sb.append("\n;; AUTHORITY SECTION:\n"); 747 for (Record<? extends Data> record : authoritySection) { 748 sb.append(record.toString()).append('\n'); 749 } 750 } 751 if (answerSection.size() != 0) { 752 sb.append("\n;; ANSWER SECTION:\n"); 753 for (Record<? extends Data> record : answerSection) { 754 sb.append(record.toString()).append('\n'); 755 } 756 } 757 if (additionalSection.size() != 0) { 758 boolean hasNonOptArr = false; 759 for (Record<? extends Data> record : additionalSection) { 760 if (record.type != Record.TYPE.OPT) { 761 if (!hasNonOptArr) { 762 hasNonOptArr = true; 763 sb.append("\n;; ADDITIONAL SECTION:\n"); 764 } 765 sb.append(record.toString()).append('\n'); 766 } 767 } 768 } 769 if (receiveTimestamp > 0) { 770 sb.append("\n;; WHEN: ").append(new Date(receiveTimestamp).toString()); 771 } 772 terminalOutputCache = sb.toString(); 773 return terminalOutputCache; 774 } 775 776 public <D extends Data> Set<D> getAnswersFor(Question q) { 777 if (responseCode != RESPONSE_CODE.NO_ERROR) return null; 778 779 // It would be great if we could verify that D matches q.type at this 780 // point. But on the other hand, if it does not, then the cast to D 781 // below will fail. 782 Set<D> res = new HashSet<>(answerSection.size()); 783 for (Record<? extends Data> record : answerSection) { 784 if (!record.isAnswer(q)) continue; 785 786 Data data = record.getPayload(); 787 @SuppressWarnings("unchecked") 788 D d = (D) data; 789 boolean isNew = res.add(d); 790 if (!isNew) { 791 LOGGER.log(Level.WARNING, "DnsMessage contains duplicate answers. Record: " + record + "; DnsMessage: " + this); 792 } 793 } 794 return res; 795 } 796 797 public Builder asBuilder() { 798 return new Builder(this); 799 } 800 801 private DnsMessage normalizedVersionCache; 802 803 public DnsMessage asNormalizedVersion() { 804 if (normalizedVersionCache == null) { 805 normalizedVersionCache = new DnsMessage(this); 806 } 807 return normalizedVersionCache; 808 } 809 810 private transient Integer hashCodeCache; 811 812 @Override 813 public int hashCode() { 814 if (hashCodeCache == null) { 815 byte[] bytes = serialize(); 816 hashCodeCache = Arrays.hashCode(bytes); 817 } 818 return hashCodeCache; 819 } 820 821 @Override 822 public boolean equals(Object other) { 823 if (!(other instanceof DnsMessage)) { 824 return false; 825 } 826 if (other == this) { 827 return true; 828 } 829 DnsMessage otherDnsMessage = (DnsMessage) other; 830 byte[] otherBytes = otherDnsMessage.serialize(); 831 byte[] myBytes = serialize(); 832 return Arrays.equals(myBytes, otherBytes); 833 } 834 835 public static Builder builder() { 836 return new DnsMessage.Builder(); 837 } 838 839 public static class Builder { 840 841 private Builder() { 842 } 843 844 private Builder(DnsMessage message) { 845 id = message.id; 846 opcode = message.opcode; 847 responseCode = message.responseCode; 848 query = message.qr; 849 authoritativeAnswer = message.authoritativeAnswer; 850 truncated = message.truncated; 851 recursionDesired = message.recursionDesired; 852 recursionAvailable = message.recursionAvailable; 853 authenticData = message.authenticData; 854 checkingDisabled = message.checkingDisabled; 855 receiveTimestamp = message.receiveTimestamp; 856 857 // Copy the unmodifiable lists over into this new builder. 858 questions = new ArrayList<>(message.questions.size()); 859 questions.addAll(message.questions); 860 answers = new ArrayList<>(message.answerSection.size()); 861 answers.addAll(message.answerSection); 862 nameserverRecords = new ArrayList<>(message.authoritySection.size()); 863 nameserverRecords.addAll(message.authoritySection); 864 additionalResourceRecords = new ArrayList<>(message.additionalSection.size()); 865 additionalResourceRecords.addAll(message.additionalSection); 866 } 867 868 private int id; 869 private OPCODE opcode = OPCODE.QUERY; 870 private RESPONSE_CODE responseCode = RESPONSE_CODE.NO_ERROR; 871 private boolean query; 872 private boolean authoritativeAnswer; 873 private boolean truncated; 874 private boolean recursionDesired; 875 private boolean recursionAvailable; 876 private boolean authenticData; 877 private boolean checkingDisabled; 878 879 private long receiveTimestamp = -1; 880 881 private List<Question> questions; 882 private List<Record<? extends Data>> answers; 883 private List<Record<? extends Data>> nameserverRecords; 884 private List<Record<? extends Data>> additionalResourceRecords; 885 private Edns.Builder ednsBuilder; 886 887 /** 888 * Set the current DNS message id. 889 * 890 * @param id The new DNS message id. 891 * @return a reference to this builder. 892 */ 893 public Builder setId(int id) { 894 this.id = id & 0xffff; 895 return this; 896 } 897 898 public Builder setOpcode(OPCODE opcode) { 899 this.opcode = opcode; 900 return this; 901 } 902 903 public Builder setResponseCode(RESPONSE_CODE responseCode) { 904 this.responseCode = responseCode; 905 return this; 906 } 907 908 /** 909 * Set the QR flag. 910 * 911 * @param query The new QR flag status. 912 * @return a reference to this builder. 913 */ 914 public Builder setQrFlag(boolean query) { 915 this.query = query; 916 return this; 917 } 918 919 /** 920 * Set the authoritative answer flag. 921 * 922 * @param authoritativeAnswer Tge new authoritative answer value. 923 * @return a reference to this builder. 924 */ 925 public Builder setAuthoritativeAnswer(boolean authoritativeAnswer) { 926 this.authoritativeAnswer = authoritativeAnswer; 927 return this; 928 } 929 930 /** 931 * Set the truncation bit on this DNS message. 932 * 933 * @param truncated The new truncated bit status. 934 * @return a reference to this builder. 935 */ 936 public Builder setTruncated(boolean truncated) { 937 this.truncated = truncated; 938 return this; 939 } 940 941 /** 942 * Set the recursion desired flag on this message. 943 * 944 * @param recursionDesired The new recusrion setting. 945 * @return a reference to this builder. 946 */ 947 public Builder setRecursionDesired(boolean recursionDesired) { 948 this.recursionDesired = recursionDesired; 949 return this; 950 } 951 952 /** 953 * Set the recursion available flog from this DNS message. 954 * 955 * @param recursionAvailable The new recursion available status. 956 * @return a reference to this builder. 957 */ 958 public Builder setRecursionAvailable(boolean recursionAvailable) { 959 this.recursionAvailable = recursionAvailable; 960 return this; 961 } 962 963 /** 964 * Set the authentic data flag on this DNS message. 965 * 966 * @param authenticData The new authentic data flag value. 967 * @return a reference to this builder. 968 */ 969 public Builder setAuthenticData(boolean authenticData) { 970 this.authenticData = authenticData; 971 return this; 972 } 973 974 /** 975 * Change the check status of this packet. 976 * 977 * @param checkingDisabled The new check disabled value. 978 * @return a reference to this builder. 979 */ 980 @Deprecated 981 public Builder setCheckDisabled(boolean checkingDisabled) { 982 this.checkingDisabled = checkingDisabled; 983 return this; 984 } 985 986 /** 987 * Change the check status of this packet. 988 * 989 * @param checkingDisabled The new check disabled value. 990 * @return a reference to this builder. 991 */ 992 public Builder setCheckingDisabled(boolean checkingDisabled) { 993 this.checkingDisabled = checkingDisabled; 994 return this; 995 } 996 997 public void copyFlagsFrom(DnsMessage dnsMessage) { 998 this.query = dnsMessage.qr; 999 this.authoritativeAnswer = dnsMessage.authenticData; 1000 this.truncated = dnsMessage.truncated; 1001 this.recursionDesired = dnsMessage.recursionDesired; 1002 this.recursionAvailable = dnsMessage.recursionAvailable; 1003 this.authenticData = dnsMessage.authenticData; 1004 this.checkingDisabled = dnsMessage.checkingDisabled; 1005 } 1006 1007 public Builder setReceiveTimestamp(long receiveTimestamp) { 1008 this.receiveTimestamp = receiveTimestamp; 1009 return this; 1010 } 1011 1012 public Builder addQuestion(Question question) { 1013 if (questions == null) { 1014 questions = new ArrayList<>(1); 1015 } 1016 questions.add(question); 1017 return this; 1018 } 1019 1020 /** 1021 * Set the question part of this message. 1022 * 1023 * @param questions The questions. 1024 * @return a reference to this builder. 1025 */ 1026 public Builder setQuestions(List<Question> questions) { 1027 this.questions = questions; 1028 return this; 1029 } 1030 1031 /** 1032 * Set the question part of this message. 1033 * 1034 * @param question The question. 1035 * @return a reference to this builder. 1036 */ 1037 public Builder setQuestion(Question question) { 1038 this.questions = new ArrayList<>(1); 1039 this.questions.add(question); 1040 return this; 1041 } 1042 1043 public Builder addAnswer(Record<? extends Data> answer) { 1044 if (answers == null) { 1045 answers = new ArrayList<>(1); 1046 } 1047 answers.add(answer); 1048 return this; 1049 } 1050 1051 public Builder addAnswers(Collection<Record<? extends Data>> records) { 1052 if (answers == null) { 1053 answers = new ArrayList<>(records.size()); 1054 } 1055 answers.addAll(records); 1056 return this; 1057 } 1058 1059 public Builder setAnswers(Collection<Record<? extends Data>> records) { 1060 answers = new ArrayList<>(records.size()); 1061 answers.addAll(records); 1062 return this; 1063 } 1064 1065 public List<Record<? extends Data>> getAnswers() { 1066 if (answers == null) { 1067 return Collections.emptyList(); 1068 } 1069 return answers; 1070 } 1071 1072 public Builder addNameserverRecords(Record<? extends Data> record) { 1073 if (nameserverRecords == null) { 1074 nameserverRecords = new ArrayList<>(8); 1075 } 1076 nameserverRecords.add(record); 1077 return this; 1078 } 1079 1080 public Builder setNameserverRecords(Collection<Record<? extends Data>> records) { 1081 nameserverRecords = new ArrayList<>(records.size()); 1082 nameserverRecords.addAll(records); 1083 return this; 1084 } 1085 1086 public Builder setAdditionalResourceRecords(Collection<Record<? extends Data>> records) { 1087 additionalResourceRecords = new ArrayList<>(records.size()); 1088 additionalResourceRecords.addAll(records); 1089 return this; 1090 } 1091 1092 public Builder addAdditionalResourceRecord(Record<? extends Data> record) { 1093 if (additionalResourceRecords == null) { 1094 additionalResourceRecords = new ArrayList<>(); 1095 } 1096 additionalResourceRecords.add(record); 1097 return this; 1098 } 1099 1100 public Builder addAdditionalResourceRecords(List<Record<? extends Data>> records) { 1101 if (additionalResourceRecords == null) { 1102 additionalResourceRecords = new ArrayList<>(records.size()); 1103 } 1104 additionalResourceRecords.addAll(records); 1105 return this; 1106 } 1107 1108 public List<Record<? extends Data>> getAdditionalResourceRecords() { 1109 if (additionalResourceRecords == null) { 1110 return Collections.emptyList(); 1111 } 1112 return additionalResourceRecords; 1113 } 1114 1115 /** 1116 * Get the @{link EDNS} builder. If no builder has been set so far, then a new one will be created. 1117 * <p> 1118 * The EDNS record can be used to announce the supported size of UDP payload as well as additional flags. 1119 * </p> 1120 * <p> 1121 * Note that some networks and firewalls are known to block big UDP payloads. 1280 should be a reasonable value, 1122 * everything below 512 is treated as 512 and should work on all networks. 1123 * </p> 1124 * 1125 * @return a EDNS builder. 1126 */ 1127 public Edns.Builder getEdnsBuilder() { 1128 if (ednsBuilder == null) { 1129 ednsBuilder = Edns.builder(); 1130 } 1131 return ednsBuilder; 1132 } 1133 1134 public DnsMessage build() { 1135 return new DnsMessage(this); 1136 } 1137 1138 } 1139 1140}