src/share/classes/com/sun/jndi/dns/ResourceRecord.java
Print this page
rev 9296 : 8035105: Detect compression loops in the JNDI DNS client
@@ -1,7 +1,7 @@
/*
- * Copyright (c) 2000, 2002, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
@@ -23,12 +23,17 @@
* questions.
*/
package com.sun.jndi.dns;
+import javax.naming.CommunicationException;
import javax.naming.InvalidNameException;
+import java.io.IOException;
+
+import java.nio.charset.StandardCharsets;
+
/**
* The ResourceRecord class represents a DNS resource record.
* The string format is based on the master file representation in
* RFC 1035.
@@ -82,10 +87,15 @@
*/
static final String rrClassNames[] = {
null, "IN", null, null, "HS"
};
+ /*
+ * Maximum number of compression references in labels.
+ * Used to detect compression loops.
+ */
+ private static final int MAXIMUM_COMPRESSION_REFERENCES = 16;
byte[] msg; // DNS message
int msgLen; // msg size (in octets)
boolean qSection; // true if this RR is part of question section
// and therefore has no ttl or rdata
@@ -108,16 +118,16 @@
* not a true resource record in that case, but is treated as if it
* were a shortened one (with no ttl or rdata). If decodeRdata is
* false, the rdata is not decoded (and getRdata() will return null)
* unless this is an SOA record.
*
- * @throws InvalidNameException if a decoded domain name isn't valid.
+ * @throws CommunicationException if a decoded domain name isn't valid.
* @throws ArrayIndexOutOfBoundsException given certain other corrupt data.
*/
ResourceRecord(byte[] msg, int msgLen, int offset,
boolean qSection, boolean decodeRdata)
- throws InvalidNameException {
+ throws CommunicationException {
this.msg = msg;
this.msgLen = msgLen;
this.offset = offset;
this.qSection = qSection;
@@ -233,11 +243,11 @@
/*
* Decodes the binary format of the RR.
* May throw ArrayIndexOutOfBoundsException given corrupt data.
*/
- private void decode(boolean decodeRdata) throws InvalidNameException {
+ private void decode(boolean decodeRdata) throws CommunicationException {
int pos = offset; // index of next unread octet
name = new DnsName(); // NAME
pos = decodeName(pos, name);
@@ -314,47 +324,64 @@
}
/*
* Returns the name encoded at msg[pos], including the root label.
*/
- private DnsName decodeName(int pos) throws InvalidNameException {
+ private DnsName decodeName(int pos) throws CommunicationException {
DnsName n = new DnsName();
decodeName(pos, n);
return n;
}
/*
* Prepends to "n" the domain name encoded at msg[pos], including the root
* label. Returns the index into "msg" following the name.
*/
- private int decodeName(int pos, DnsName n) throws InvalidNameException {
- if (msg[pos] == 0) { // end of name
- n.add(0, "");
- return (pos + 1);
- } else if ((msg[pos] & 0xC0) != 0) { // name compression
- decodeName(getUShort(pos) & 0x3FFF, n);
- return (pos + 2);
- } else { // append a label
- int len = msg[pos++];
+ private int decodeName(int pos, DnsName n) throws CommunicationException {
+ int endPos = -1;
+ int level = 0;
try {
- n.add(0, new String(msg, pos, len, "ISO-8859-1"));
- } catch (java.io.UnsupportedEncodingException e) {
- // assert false : "ISO-Latin-1 charset unavailable";
- }
- return decodeName(pos + len, n);
- }
+ while (true) {
+ if (level > MAXIMUM_COMPRESSION_REFERENCES)
+ throw new IOException("Too many compression references");
+ int type = msg[pos] & 0xFF;
+ if (type == 0) { // end of name
+ ++pos;
+ n.add(0, "");
+ break;
+ } else if (type <= 63) { // regular label
+ ++pos;
+ n.add(0, new String(msg, pos, type,
+ StandardCharsets.ISO_8859_1));
+ pos += type;
+ } else if ((msg[pos] & 0xC0) == 0xC0) { // name compression
+ ++level;
+ endPos = pos + 2;
+ pos = getUShort(pos) & 0x3FFF;
+ } else
+ throw new IOException("Invalid label type: " + type);
+ }
+ } catch (IOException | InvalidNameException e) {
+ CommunicationException ce =new CommunicationException(
+ "DNS error: malformed packet");
+ ce.initCause(e);
+ throw ce;
+ }
+ if (endPos == -1)
+ endPos = pos;
+ return endPos;
}
/*
* Returns the rdata encoded at msg[pos]. The format is dependent
* on the rrtype and rrclass values, which have already been set.
* The length of the encoded data is rdlen, which has already been
* set.
* The rdata of records with unknown type/class combinations is
* returned in a newly-allocated byte array.
*/
- private Object decodeRdata(int pos) throws InvalidNameException {
+ private Object decodeRdata(int pos) throws CommunicationException {
if (rrclass == CLASS_INTERNET) {
switch (rrtype) {
case TYPE_A:
return decodeA(pos);
case TYPE_AAAA:
@@ -384,21 +411,21 @@
}
/*
* Returns the rdata of an MX record that is encoded at msg[pos].
*/
- private String decodeMx(int pos) throws InvalidNameException {
+ private String decodeMx(int pos) throws CommunicationException {
int preference = getUShort(pos);
pos += 2;
DnsName name = decodeName(pos);
return (preference + " " + name);
}
/*
* Returns the rdata of an SOA record that is encoded at msg[pos].
*/
- private String decodeSoa(int pos) throws InvalidNameException {
+ private String decodeSoa(int pos) throws CommunicationException {
DnsName mname = new DnsName();
pos = decodeName(pos, mname);
DnsName rname = new DnsName();
pos = decodeName(pos, rname);
@@ -419,11 +446,11 @@
/*
* Returns the rdata of an SRV record that is encoded at msg[pos].
* See RFC 2782.
*/
- private String decodeSrv(int pos) throws InvalidNameException {
+ private String decodeSrv(int pos) throws CommunicationException {
int priority = getUShort(pos);
pos += 2;
int weight = getUShort(pos);
pos += 2;
int port = getUShort(pos);
@@ -434,11 +461,11 @@
/*
* Returns the rdata of an NAPTR record that is encoded at msg[pos].
* See RFC 2915.
*/
- private String decodeNaptr(int pos) throws InvalidNameException {
+ private String decodeNaptr(int pos) throws CommunicationException {
int order = getUShort(pos);
pos += 2;
int preference = getUShort(pos);
pos += 2;
StringBuffer flags = new StringBuffer();