1 /* 2 * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.jndi.dns; 27 28 import javax.naming.CommunicationException; 29 import javax.naming.InvalidNameException; 30 31 import java.io.IOException; 32 33 import java.nio.charset.StandardCharsets; 34 35 36 /** 37 * The ResourceRecord class represents a DNS resource record. 38 * The string format is based on the master file representation in 39 * RFC 1035. 40 * 41 * @author Scott Seligman 42 */ 43 44 45 public class ResourceRecord { 46 47 /* 48 * Resource record type codes 49 */ 50 static final int TYPE_A = 1; 51 static final int TYPE_NS = 2; 52 static final int TYPE_CNAME = 5; 53 static final int TYPE_SOA = 6; 54 static final int TYPE_PTR = 12; 55 static final int TYPE_HINFO = 13; 56 static final int TYPE_MX = 15; 57 static final int TYPE_TXT = 16; 58 static final int TYPE_AAAA = 28; 59 static final int TYPE_SRV = 33; 60 static final int TYPE_NAPTR = 35; 61 static final int QTYPE_AXFR = 252; // zone transfer 62 static final int QTYPE_STAR = 255; // query type "*" 63 64 /* 65 * Mapping from resource record type codes to type name strings. 66 */ 67 static final String rrTypeNames[] = { 68 null, "A", "NS", null, null, 69 "CNAME", "SOA", null, null, null, 70 null, null, "PTR", "HINFO", null, 71 "MX", "TXT", null, null, null, 72 null, null, null, null, null, 73 null, null, null, "AAAA", null, 74 null, null, null, "SRV", null, 75 "NAPTR" 76 }; 77 78 /* 79 * Resource record class codes 80 */ 81 static final int CLASS_INTERNET = 1; 82 static final int CLASS_HESIOD = 2; 83 static final int QCLASS_STAR = 255; // query class "*" 84 85 /* 86 * Mapping from resource record type codes to class name strings. 87 */ 88 static final String rrClassNames[] = { 89 null, "IN", null, null, "HS" 90 }; 91 92 /* 93 * Maximum number of compression references in labels. 94 * Used to detect compression loops. 95 */ 96 private static final int MAXIMUM_COMPRESSION_REFERENCES = 16; 97 98 byte[] msg; // DNS message 99 int msgLen; // msg size (in octets) 100 boolean qSection; // true if this RR is part of question section 101 // and therefore has no ttl or rdata 102 int offset; // offset of RR w/in msg 103 int rrlen; // number of octets in encoded RR 104 DnsName name; // name field of RR, including root label 105 int rrtype; // type field of RR 106 String rrtypeName; // name of rrtype 107 int rrclass; // class field of RR 108 String rrclassName; // name of rrclass 109 int ttl = 0; // ttl field of RR 110 int rdlen = 0; // number of octets of rdata 111 Object rdata = null; // rdata -- most are String, unknown are byte[] 112 113 114 /* 115 * Constructs a new ResourceRecord. The encoded data of the DNS 116 * message is contained in msg; data for this RR begins at msg[offset]. 117 * If qSection is true this RR is part of a question section. It's 118 * not a true resource record in that case, but is treated as if it 119 * were a shortened one (with no ttl or rdata). If decodeRdata is 120 * false, the rdata is not decoded (and getRdata() will return null) 121 * unless this is an SOA record. 122 * 123 * @throws CommunicationException if a decoded domain name isn't valid. 124 * @throws ArrayIndexOutOfBoundsException given certain other corrupt data. 125 */ ResourceRecord(byte[] msg, int msgLen, int offset, boolean qSection, boolean decodeRdata)126 ResourceRecord(byte[] msg, int msgLen, int offset, 127 boolean qSection, boolean decodeRdata) 128 throws CommunicationException { 129 130 this.msg = msg; 131 this.msgLen = msgLen; 132 this.offset = offset; 133 this.qSection = qSection; 134 decode(decodeRdata); 135 } 136 toString()137 public String toString() { 138 String text = name + " " + rrclassName + " " + rrtypeName; 139 if (!qSection) { 140 text += " " + ttl + " " + 141 ((rdata != null) ? rdata : "[n/a]"); 142 } 143 return text; 144 } 145 146 /* 147 * Returns the name field of this RR, including the root label. 148 */ getName()149 public DnsName getName() { 150 return name; 151 } 152 153 /* 154 * Returns the number of octets in the encoded RR. 155 */ size()156 public int size() { 157 return rrlen; 158 } 159 getType()160 public int getType() { 161 return rrtype; 162 } 163 getRrclass()164 public int getRrclass() { 165 return rrclass; 166 } 167 getRdata()168 public Object getRdata() { 169 return rdata; 170 } 171 172 getTypeName(int rrtype)173 public static String getTypeName(int rrtype) { 174 return valueToName(rrtype, rrTypeNames); 175 } 176 getType(String typeName)177 public static int getType(String typeName) { 178 return nameToValue(typeName, rrTypeNames); 179 } 180 getRrclassName(int rrclass)181 public static String getRrclassName(int rrclass) { 182 return valueToName(rrclass, rrClassNames); 183 } 184 getRrclass(String className)185 public static int getRrclass(String className) { 186 return nameToValue(className, rrClassNames); 187 } 188 valueToName(int val, String[] names)189 private static String valueToName(int val, String[] names) { 190 String name = null; 191 if ((val > 0) && (val < names.length)) { 192 name = names[val]; 193 } else if (val == QTYPE_STAR) { // QTYPE_STAR == QCLASS_STAR 194 name = "*"; 195 } 196 if (name == null) { 197 name = Integer.toString(val); 198 } 199 return name; 200 } 201 nameToValue(String name, String[] names)202 private static int nameToValue(String name, String[] names) { 203 if (name.isEmpty()) { 204 return -1; // invalid name 205 } else if (name.equals("*")) { 206 return QTYPE_STAR; // QTYPE_STAR == QCLASS_STAR 207 } 208 if (Character.isDigit(name.charAt(0))) { 209 try { 210 return Integer.parseInt(name); 211 } catch (NumberFormatException e) { 212 } 213 } 214 for (int i = 1; i < names.length; i++) { 215 if ((names[i] != null) && 216 name.equalsIgnoreCase(names[i])) { 217 return i; 218 } 219 } 220 return -1; // unknown name 221 } 222 223 /* 224 * Compares two SOA record serial numbers using 32-bit serial number 225 * arithmetic as defined in RFC 1982. Serial numbers are unsigned 226 * 32-bit quantities. Returns a negative, zero, or positive value 227 * as the first serial number is less than, equal to, or greater 228 * than the second. If the serial numbers are not comparable the 229 * result is undefined. Note that the relation is not transitive. 230 */ compareSerialNumbers(long s1, long s2)231 public static int compareSerialNumbers(long s1, long s2) { 232 long diff = s2 - s1; 233 if (diff == 0) { 234 return 0; 235 } else if ((diff > 0 && diff <= 0x7FFFFFFF) || 236 (diff < 0 && -diff > 0x7FFFFFFF)) { 237 return -1; 238 } else { 239 return 1; 240 } 241 } 242 243 244 /* 245 * Decodes the binary format of the RR. 246 * May throw ArrayIndexOutOfBoundsException given corrupt data. 247 */ decode(boolean decodeRdata)248 private void decode(boolean decodeRdata) throws CommunicationException { 249 int pos = offset; // index of next unread octet 250 251 name = new DnsName(); // NAME 252 pos = decodeName(pos, name); 253 254 rrtype = getUShort(pos); // TYPE 255 rrtypeName = (rrtype < rrTypeNames.length) 256 ? rrTypeNames[rrtype] 257 : null; 258 if (rrtypeName == null) { 259 rrtypeName = Integer.toString(rrtype); 260 } 261 pos += 2; 262 263 rrclass = getUShort(pos); // CLASS 264 rrclassName = (rrclass < rrClassNames.length) 265 ? rrClassNames[rrclass] 266 : null; 267 if (rrclassName == null) { 268 rrclassName = Integer.toString(rrclass); 269 } 270 pos += 2; 271 272 if (!qSection) { 273 ttl = getInt(pos); // TTL 274 pos += 4; 275 276 rdlen = getUShort(pos); // RDLENGTH 277 pos += 2; 278 279 rdata = (decodeRdata || // RDATA 280 (rrtype == TYPE_SOA)) 281 ? decodeRdata(pos) 282 : null; 283 if (rdata instanceof DnsName) { 284 rdata = rdata.toString(); 285 } 286 pos += rdlen; 287 } 288 289 rrlen = pos - offset; 290 291 msg = null; // free up for GC 292 } 293 294 /* 295 * Returns the 1-byte unsigned value at msg[pos]. 296 */ getUByte(int pos)297 private int getUByte(int pos) { 298 return (msg[pos] & 0xFF); 299 } 300 301 /* 302 * Returns the 2-byte unsigned value at msg[pos]. The high 303 * order byte comes first. 304 */ getUShort(int pos)305 private int getUShort(int pos) { 306 return (((msg[pos] & 0xFF) << 8) | 307 (msg[pos + 1] & 0xFF)); 308 } 309 310 /* 311 * Returns the 4-byte signed value at msg[pos]. The high 312 * order byte comes first. 313 */ getInt(int pos)314 private int getInt(int pos) { 315 return ((getUShort(pos) << 16) | getUShort(pos + 2)); 316 } 317 318 /* 319 * Returns the 4-byte unsigned value at msg[pos]. The high 320 * order byte comes first. 321 */ getUInt(int pos)322 private long getUInt(int pos) { 323 return (getInt(pos) & 0xffffffffL); 324 } 325 326 /* 327 * Returns the name encoded at msg[pos], including the root label. 328 */ decodeName(int pos)329 private DnsName decodeName(int pos) throws CommunicationException { 330 DnsName n = new DnsName(); 331 decodeName(pos, n); 332 return n; 333 } 334 335 /* 336 * Prepends to "n" the domain name encoded at msg[pos], including the root 337 * label. Returns the index into "msg" following the name. 338 */ decodeName(int pos, DnsName n)339 private int decodeName(int pos, DnsName n) throws CommunicationException { 340 int endPos = -1; 341 int level = 0; 342 try { 343 while (true) { 344 if (level > MAXIMUM_COMPRESSION_REFERENCES) 345 throw new IOException("Too many compression references"); 346 int typeAndLen = msg[pos] & 0xFF; 347 if (typeAndLen == 0) { // end of name 348 ++pos; 349 n.add(0, ""); 350 break; 351 } else if (typeAndLen <= 63) { // regular label 352 ++pos; 353 n.add(0, new String(msg, pos, typeAndLen, 354 StandardCharsets.ISO_8859_1)); 355 pos += typeAndLen; 356 } else if ((typeAndLen & 0xC0) == 0xC0) { // name compression 357 ++level; 358 // cater for the case where the name pointed to is itself 359 // compressed: we don't want endPos to be reset by the second 360 // compression level. 361 int ppos = pos; 362 if (endPos == -1) endPos = pos + 2; 363 pos = getUShort(pos) & 0x3FFF; 364 if (debug) { 365 dprint("decode: name compression at " + ppos 366 + " -> " + pos + " endPos=" + endPos); 367 assert endPos > 0; 368 assert pos < ppos; 369 assert pos >= Header.HEADER_SIZE; 370 } 371 } else 372 throw new IOException("Invalid label type: " + typeAndLen); 373 } 374 } catch (IOException | InvalidNameException e) { 375 CommunicationException ce =new CommunicationException( 376 "DNS error: malformed packet"); 377 ce.initCause(e); 378 throw ce; 379 } 380 if (endPos == -1) 381 endPos = pos; 382 return endPos; 383 } 384 385 /* 386 * Returns the rdata encoded at msg[pos]. The format is dependent 387 * on the rrtype and rrclass values, which have already been set. 388 * The length of the encoded data is rdlen, which has already been 389 * set. 390 * The rdata of records with unknown type/class combinations is 391 * returned in a newly-allocated byte array. 392 */ decodeRdata(int pos)393 private Object decodeRdata(int pos) throws CommunicationException { 394 if (rrclass == CLASS_INTERNET) { 395 switch (rrtype) { 396 case TYPE_A: 397 return decodeA(pos); 398 case TYPE_AAAA: 399 return decodeAAAA(pos); 400 case TYPE_CNAME: 401 case TYPE_NS: 402 case TYPE_PTR: 403 return decodeName(pos); 404 case TYPE_MX: 405 return decodeMx(pos); 406 case TYPE_SOA: 407 return decodeSoa(pos); 408 case TYPE_SRV: 409 return decodeSrv(pos); 410 case TYPE_NAPTR: 411 return decodeNaptr(pos); 412 case TYPE_TXT: 413 return decodeTxt(pos); 414 case TYPE_HINFO: 415 return decodeHinfo(pos); 416 } 417 } 418 // Unknown RR type/class 419 if (debug) { 420 dprint("Unknown RR type for RR data: " + rrtype + " rdlen=" + rdlen 421 + ", pos=" + pos +", msglen=" + msg.length + ", remaining=" 422 + (msg.length-pos)); 423 } 424 byte[] rd = new byte[rdlen]; 425 System.arraycopy(msg, pos, rd, 0, rdlen); 426 return rd; 427 } 428 429 /* 430 * Returns the rdata of an MX record that is encoded at msg[pos]. 431 */ decodeMx(int pos)432 private String decodeMx(int pos) throws CommunicationException { 433 int preference = getUShort(pos); 434 pos += 2; 435 DnsName name = decodeName(pos); 436 return (preference + " " + name); 437 } 438 439 /* 440 * Returns the rdata of an SOA record that is encoded at msg[pos]. 441 */ decodeSoa(int pos)442 private String decodeSoa(int pos) throws CommunicationException { 443 DnsName mname = new DnsName(); 444 pos = decodeName(pos, mname); 445 DnsName rname = new DnsName(); 446 pos = decodeName(pos, rname); 447 448 long serial = getUInt(pos); 449 pos += 4; 450 long refresh = getUInt(pos); 451 pos += 4; 452 long retry = getUInt(pos); 453 pos += 4; 454 long expire = getUInt(pos); 455 pos += 4; 456 long minimum = getUInt(pos); // now used as negative TTL 457 pos += 4; 458 459 return (mname + " " + rname + " " + serial + " " + 460 refresh + " " + retry + " " + expire + " " + minimum); 461 } 462 463 /* 464 * Returns the rdata of an SRV record that is encoded at msg[pos]. 465 * See RFC 2782. 466 */ decodeSrv(int pos)467 private String decodeSrv(int pos) throws CommunicationException { 468 int priority = getUShort(pos); 469 pos += 2; 470 int weight = getUShort(pos); 471 pos += 2; 472 int port = getUShort(pos); 473 pos += 2; 474 DnsName target = decodeName(pos); 475 return (priority + " " + weight + " " + port + " " + target); 476 } 477 478 /* 479 * Returns the rdata of an NAPTR record that is encoded at msg[pos]. 480 * See RFC 2915. 481 */ decodeNaptr(int pos)482 private String decodeNaptr(int pos) throws CommunicationException { 483 int order = getUShort(pos); 484 pos += 2; 485 int preference = getUShort(pos); 486 pos += 2; 487 StringBuffer flags = new StringBuffer(); 488 pos += decodeCharString(pos, flags); 489 StringBuffer services = new StringBuffer(); 490 pos += decodeCharString(pos, services); 491 StringBuffer regexp = new StringBuffer(rdlen); 492 pos += decodeCharString(pos, regexp); 493 DnsName replacement = decodeName(pos); 494 495 return (order + " " + preference + " " + flags + " " + 496 services + " " + regexp + " " + replacement); 497 } 498 499 /* 500 * Returns the rdata of a TXT record that is encoded at msg[pos]. 501 * The rdata consists of one or more <character-string>s. 502 */ decodeTxt(int pos)503 private String decodeTxt(int pos) { 504 StringBuffer buf = new StringBuffer(rdlen); 505 int end = pos + rdlen; 506 while (pos < end) { 507 pos += decodeCharString(pos, buf); 508 if (pos < end) { 509 buf.append(' '); 510 } 511 } 512 return buf.toString(); 513 } 514 515 /* 516 * Returns the rdata of an HINFO record that is encoded at msg[pos]. 517 * The rdata consists of two <character-string>s. 518 */ decodeHinfo(int pos)519 private String decodeHinfo(int pos) { 520 StringBuffer buf = new StringBuffer(rdlen); 521 pos += decodeCharString(pos, buf); 522 buf.append(' '); 523 pos += decodeCharString(pos, buf); 524 return buf.toString(); 525 } 526 527 /* 528 * Decodes the <character-string> at msg[pos] and adds it to buf. 529 * If the string contains one of the meta-characters ' ', '\\', or 530 * '"', then the result is quoted and any embedded '\\' or '"' 531 * chars are escaped with '\\'. Empty strings are also quoted. 532 * Returns the size of the encoded string, including the initial 533 * length octet. 534 */ decodeCharString(int pos, StringBuffer buf)535 private int decodeCharString(int pos, StringBuffer buf) { 536 int start = buf.length(); // starting index of this string 537 int len = getUByte(pos++); // encoded string length 538 boolean quoted = (len == 0); // quote string if empty 539 for (int i = 0; i < len; i++) { 540 int c = getUByte(pos++); 541 quoted |= (c == ' '); 542 if ((c == '\\') || (c == '"')) { 543 quoted = true; 544 buf.append('\\'); 545 } 546 buf.append((char) c); 547 } 548 if (quoted) { 549 buf.insert(start, '"'); 550 buf.append('"'); 551 } 552 return (len + 1); // size includes initial octet 553 } 554 555 /* 556 * Returns the rdata of an A record, in dotted-decimal format, 557 * that is encoded at msg[pos]. 558 */ decodeA(int pos)559 private String decodeA(int pos) { 560 return ((msg[pos] & 0xff) + "." + 561 (msg[pos + 1] & 0xff) + "." + 562 (msg[pos + 2] & 0xff) + "." + 563 (msg[pos + 3] & 0xff)); 564 } 565 566 /* 567 * Returns the rdata of an AAAA record, in colon-separated format, 568 * that is encoded at msg[pos]. For example: 4321:0:1:2:3:4:567:89ab. 569 * See RFCs 1886 and 2373. 570 */ decodeAAAA(int pos)571 private String decodeAAAA(int pos) { 572 int[] addr6 = new int[8]; // the unsigned 16-bit words of the address 573 for (int i = 0; i < 8; i++) { 574 addr6[i] = getUShort(pos); 575 pos += 2; 576 } 577 578 // Find longest sequence of two or more zeros, to compress them. 579 int curBase = -1; 580 int curLen = 0; 581 int bestBase = -1; 582 int bestLen = 0; 583 for (int i = 0; i < 8; i++) { 584 if (addr6[i] == 0) { 585 if (curBase == -1) { // new sequence 586 curBase = i; 587 curLen = 1; 588 } else { // extend sequence 589 ++curLen; 590 if ((curLen >= 2) && (curLen > bestLen)) { 591 bestBase = curBase; 592 bestLen = curLen; 593 } 594 } 595 } else { // not in sequence 596 curBase = -1; 597 } 598 } 599 600 // If addr begins with at least 6 zeros and is not :: or ::1, 601 // or with 5 zeros followed by 0xffff, use the text format for 602 // IPv4-compatible or IPv4-mapped addresses. 603 if (bestBase == 0) { 604 if ((bestLen == 6) || 605 ((bestLen == 7) && (addr6[7] > 1))) { 606 return ("::" + decodeA(pos - 4)); 607 } else if ((bestLen == 5) && (addr6[5] == 0xffff)) { 608 return ("::ffff:" + decodeA(pos - 4)); 609 } 610 } 611 612 // If bestBase != -1, compress zeros in [bestBase, bestBase+bestLen) 613 boolean compress = (bestBase != -1); 614 615 StringBuilder sb = new StringBuilder(40); 616 if (bestBase == 0) { 617 sb.append(':'); 618 } 619 for (int i = 0; i < 8; i++) { 620 if (!compress || (i < bestBase) || (i >= bestBase + bestLen)) { 621 sb.append(Integer.toHexString(addr6[i])); 622 if (i < 7) { 623 sb.append(':'); 624 } 625 } else if (compress && (i == bestBase)) { // first compressed zero 626 sb.append(':'); 627 } 628 } 629 630 return sb.toString(); 631 } 632 633 //------------------------------------------------------------------------- 634 635 private static final boolean debug = false; 636 dprint(String mess)637 private static void dprint(String mess) { 638 if (debug) { 639 System.err.println("DNS: " + mess); 640 } 641 } 642 643 } 644