Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/com/sun/jndi/dns/DnsName.java
38924 views
/*1* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.jndi.dns;262728import java.util.ArrayList;29import java.util.Comparator;30import java.util.Enumeration;3132import javax.naming.*;333435/**36* <tt>DnsName</tt> implements compound names for DNS as specified by37* RFCs 1034 and 1035, and as updated and clarified by RFCs 1123 and 2181.38*39* <p> The labels in a domain name correspond to JNDI atomic names.40* Each label must be less than 64 octets in length, and only the41* optional root label at the end of the name may be 0 octets long.42* The sum of the lengths of all labels in a name, plus the number of43* non-root labels plus 1, must be less than 256. The textual44* representation of a domain name consists of the labels, escaped as45* needed, dot-separated, and ordered right-to-left.46*47* <p> A label consists of a sequence of octets, each of which may48* have any value from 0 to 255.49*50* <p> <em>Host names</em> are a subset of domain names.51* Their labels contain only ASCII letters, digits, and hyphens, and52* none may begin or end with a hyphen. While names not conforming to53* these rules may be valid domain names, they will not be usable by a54* number of DNS applications, and should in most cases be avoided.55*56* <p> DNS does not specify an encoding (such as UTF-8) to use for57* octets with non-ASCII values. As of this writing there is some58* work going on in this area, but it is not yet finalized.59* <tt>DnsName</tt> currently converts any non-ASCII octets into60* characters using ISO-LATIN-1 encoding, in effect taking the61* value of each octet and storing it directly into the low-order byte62* of a Java character and <i>vice versa</i>. As a consequence, no63* character in a DNS name will ever have a non-zero high-order byte.64* When the work on internationalizing domain names has stabilized65* (see for example <i>draft-ietf-idn-idna-10.txt</i>), <tt>DnsName</tt>66* may be updated to conform to that work.67*68* <p> Backslash (<tt>\</tt>) is used as the escape character in the69* textual representation of a domain name. The character sequence70* `<tt>\DDD</tt>', where <tt>DDD</tt> is a 3-digit decimal number71* (with leading zeros if needed), represents the octet whose value72* is <tt>DDD</tt>. The character sequence `<tt>\C</tt>', where73* <tt>C</tt> is a character other than <tt>'0'</tt> through74* <tt>'9'</tt>, represents the octet whose value is that of75* <tt>C</tt> (again using ISO-LATIN-1 encoding); this is particularly76* useful for escaping <tt>'.'</tt> or backslash itself. Backslash is77* otherwise not allowed in a domain name. Note that escape characters78* are interpreted when a name is parsed. So, for example, the character79* sequences `<tt>S</tt>', `<tt>\S</tt>', and `<tt>\083</tt>' each80* represent the same one-octet name. The <tt>toString()</tt> method81* does not generally insert escape sequences except where necessary.82* If, however, the <tt>DnsName</tt> was constructed using unneeded83* escapes, those escapes may appear in the <tt>toString</tt> result.84*85* <p> Atomic names passed as parameters to methods of86* <tt>DnsName</tt>, and those returned by them, are unescaped. So,87* for example, <tt>(new DnsName()).add("a.b")</tt> creates an88* object representing the one-label domain name <tt>a\.b</tt>, and89* calling <tt>get(0)</tt> on this object returns <tt>"a.b"</tt>.90*91* <p> While DNS names are case-preserving, comparisons between them92* are case-insensitive. When comparing names containing non-ASCII93* octets, <tt>DnsName</tt> uses case-insensitive comparison94* between pairs of ASCII values, and exact binary comparison95* otherwise.9697* <p> A <tt>DnsName</tt> instance is not synchronized against98* concurrent access by multiple threads.99*100* @author Scott Seligman101*/102103104public final class DnsName implements Name {105106// If non-null, the domain name represented by this DnsName.107private String domain = "";108109// The labels of this domain name, as a list of strings. Index 0110// corresponds to the leftmost (least significant) label: note that111// this is the reverse of the ordering used by the Name interface.112private ArrayList<String> labels = new ArrayList<>();113114// The number of octets needed to carry this domain name in a DNS115// packet. Equal to the sum of the lengths of each label, plus the116// number of non-root labels, plus 1. Must remain less than 256.117private short octets = 1;118119120/**121* Constructs a <tt>DnsName</tt> representing the empty domain name.122*/123public DnsName() {124}125126/**127* Constructs a <tt>DnsName</tt> representing a given domain name.128*129* @param name the domain name to parse130* @throws InvalidNameException if <tt>name</tt> does not conform131* to DNS syntax.132*/133public DnsName(String name) throws InvalidNameException {134parse(name);135}136137/*138* Returns a new DnsName with its name components initialized to139* the components of "n" in the range [beg,end). Indexing is as140* for the Name interface, with 0 being the most significant.141*/142private DnsName(DnsName n, int beg, int end) {143// Compute indexes into "labels", which has least-significant label144// at index 0 (opposite to the convention used for "beg" and "end").145int b = n.size() - end;146int e = n.size() - beg;147labels.addAll(n.labels.subList(b, e));148149if (size() == n.size()) {150domain = n.domain;151octets = n.octets;152} else {153for (String label: labels) {154if (label.length() > 0) {155octets += (short) (label.length() + 1);156}157}158}159}160161162public String toString() {163if (domain == null) {164StringBuilder buf = new StringBuilder();165for (String label: labels) {166if (buf.length() > 0 || label.length() == 0) {167buf.append('.');168}169escape(buf, label);170}171domain = buf.toString();172}173return domain;174}175176/**177* Does this domain name follow <em>host name</em> syntax?178*/179public boolean isHostName() {180for (String label: labels) {181if (!isHostNameLabel(label)) {182return false;183}184}185return true;186}187188public short getOctets() {189return octets;190}191192public int size() {193return labels.size();194}195196public boolean isEmpty() {197return (size() == 0);198}199200public int hashCode() {201int h = 0;202for (int i = 0; i < size(); i++) {203h = 31 * h + getKey(i).hashCode();204}205return h;206}207208public boolean equals(Object obj) {209if (!(obj instanceof Name) || (obj instanceof CompositeName)) {210return false;211}212Name n = (Name) obj;213return ((size() == n.size()) && // shortcut: do sizes differ?214(compareTo(obj) == 0));215}216217public int compareTo(Object obj) {218Name n = (Name) obj;219return compareRange(0, size(), n); // never 0 if sizes differ220}221222public boolean startsWith(Name n) {223return ((size() >= n.size()) &&224(compareRange(0, n.size(), n) == 0));225}226227public boolean endsWith(Name n) {228return ((size() >= n.size()) &&229(compareRange(size() - n.size(), size(), n) == 0));230}231232public String get(int pos) {233if (pos < 0 || pos >= size()) {234throw new ArrayIndexOutOfBoundsException();235}236int i = size() - pos - 1; // index of "pos" component in "labels"237return labels.get(i);238}239240public Enumeration<String> getAll() {241return new Enumeration<String>() {242int pos = 0;243public boolean hasMoreElements() {244return (pos < size());245}246public String nextElement() {247if (pos < size()) {248return get(pos++);249}250throw new java.util.NoSuchElementException();251}252};253}254255public Name getPrefix(int pos) {256return new DnsName(this, 0, pos);257}258259public Name getSuffix(int pos) {260return new DnsName(this, pos, size());261}262263public Object clone() {264return new DnsName(this, 0, size());265}266267public Object remove(int pos) {268if (pos < 0 || pos >= size()) {269throw new ArrayIndexOutOfBoundsException();270}271int i = size() - pos - 1; // index of element to remove in "labels"272String label = labels.remove(i);273int len = label.length();274if (len > 0) {275octets -= (short) (len + 1);276}277domain = null; // invalidate "domain"278return label;279}280281public Name add(String comp) throws InvalidNameException {282return add(size(), comp);283}284285public Name add(int pos, String comp) throws InvalidNameException {286if (pos < 0 || pos > size()) {287throw new ArrayIndexOutOfBoundsException();288}289// Check for empty labels: may have only one, and only at end.290int len = comp.length();291if ((pos > 0 && len == 0) ||292(pos == 0 && hasRootLabel())) {293throw new InvalidNameException(294"Empty label must be the last label in a domain name");295}296// Check total name length.297if (len > 0) {298if (octets + len + 1 >= 256) {299throw new InvalidNameException("Name too long");300}301octets += (short) (len + 1);302}303304int i = size() - pos; // index for insertion into "labels"305verifyLabel(comp);306labels.add(i, comp);307308domain = null; // invalidate "domain"309return this;310}311312public Name addAll(Name suffix) throws InvalidNameException {313return addAll(size(), suffix);314}315316public Name addAll(int pos, Name n) throws InvalidNameException {317if (n instanceof DnsName) {318// "n" is a DnsName so we can insert it as a whole, rather than319// verifying and inserting it component-by-component.320// More code, but less work.321DnsName dn = (DnsName) n;322323if (dn.isEmpty()) {324return this;325}326// Check for empty labels: may have only one, and only at end.327if ((pos > 0 && dn.hasRootLabel()) ||328(pos == 0 && hasRootLabel())) {329throw new InvalidNameException(330"Empty label must be the last label in a domain name");331}332333short newOctets = (short) (octets + dn.octets - 1);334if (newOctets > 255) {335throw new InvalidNameException("Name too long");336}337octets = newOctets;338int i = size() - pos; // index for insertion into "labels"339labels.addAll(i, dn.labels);340341// Preserve "domain" if we're appending or prepending,342// otherwise invalidate it.343if (isEmpty()) {344domain = dn.domain;345} else if (domain == null || dn.domain == null) {346domain = null;347} else if (pos == 0) {348domain += (dn.domain.equals(".") ? "" : ".") + dn.domain;349} else if (pos == size()) {350domain = dn.domain + (domain.equals(".") ? "" : ".") + domain;351} else {352domain = null;353}354355} else if (n instanceof CompositeName) {356n = (DnsName) n; // force ClassCastException357358} else { // "n" is a compound name, but not a DnsName.359// Add labels least-significant first: sometimes more efficient.360for (int i = n.size() - 1; i >= 0; i--) {361add(pos, n.get(i));362}363}364return this;365}366367368boolean hasRootLabel() {369return (!isEmpty() &&370get(0).equals(""));371}372373/*374* Helper method for public comparison methods. Lexicographically375* compares components of this name in the range [beg,end) with376* all components of "n". Indexing is as for the Name interface,377* with 0 being the most significant. Returns negative, zero, or378* positive as these name components are less than, equal to, or379* greater than those of "n".380*/381private int compareRange(int beg, int end, Name n) {382if (n instanceof CompositeName) {383n = (DnsName) n; // force ClassCastException384}385// Loop through labels, starting with most significant.386int minSize = Math.min(end - beg, n.size());387for (int i = 0; i < minSize; i++) {388String label1 = get(i + beg);389String label2 = n.get(i);390391int j = size() - (i + beg) - 1; // index of label1 in "labels"392// assert (label1 == labels.get(j));393394int c = compareLabels(label1, label2);395if (c != 0) {396return c;397}398}399return ((end - beg) - n.size()); // longer range wins400}401402/*403* Returns a key suitable for hashing the label at index i.404* Indexing is as for the Name interface, with 0 being the most405* significant.406*/407String getKey(int i) {408return keyForLabel(get(i));409}410411412/*413* Parses a domain name, setting the values of instance vars accordingly.414*/415private void parse(String name) throws InvalidNameException {416417StringBuffer label = new StringBuffer(); // label being parsed418419for (int i = 0; i < name.length(); i++) {420char c = name.charAt(i);421422if (c == '\\') { // found an escape sequence423c = getEscapedOctet(name, i++);424if (isDigit(name.charAt(i))) { // sequence is \DDD425i += 2; // consume remaining digits426}427label.append(c);428429} else if (c != '.') { // an unescaped octet430label.append(c);431432} else { // found '.' separator433add(0, label.toString()); // check syntax, then add label434// to end of name435label.delete(0, i); // clear buffer for next label436}437}438439// If name is neither "." nor "", the octets (zero or more)440// from the rightmost dot onward are now added as the final441// label of the name. Those two are special cases in that for442// all other domain names, the number of labels is one greater443// than the number of dot separators.444if (!name.equals("") && !name.equals(".")) {445add(0, label.toString());446}447448domain = name; // do this last, since add() sets it to null449}450451/*452* Returns (as a char) the octet indicated by the escape sequence453* at a given position within a domain name.454* @throws InvalidNameException if a valid escape sequence is not found.455*/456private static char getEscapedOctet(String name, int pos)457throws InvalidNameException {458try {459// assert (name.charAt(pos) == '\\');460char c1 = name.charAt(++pos);461if (isDigit(c1)) { // sequence is `\DDD'462char c2 = name.charAt(++pos);463char c3 = name.charAt(++pos);464if (isDigit(c2) && isDigit(c3)) {465return (char)466((c1 - '0') * 100 + (c2 - '0') * 10 + (c3 - '0'));467} else {468throw new InvalidNameException(469"Invalid escape sequence in " + name);470}471} else { // sequence is `\C'472return c1;473}474} catch (IndexOutOfBoundsException e) {475throw new InvalidNameException(476"Invalid escape sequence in " + name);477}478}479480/*481* Checks that this label is valid.482* @throws InvalidNameException if label is not valid.483*/484private static void verifyLabel(String label) throws InvalidNameException {485if (label.length() > 63) {486throw new InvalidNameException(487"Label exceeds 63 octets: " + label);488}489// Check for two-byte characters.490for (int i = 0; i < label.length(); i++) {491char c = label.charAt(i);492if ((c & 0xFF00) != 0) {493throw new InvalidNameException(494"Label has two-byte char: " + label);495}496}497}498499/*500* Does this label conform to host name syntax?501*/502private static boolean isHostNameLabel(String label) {503for (int i = 0; i < label.length(); i++) {504char c = label.charAt(i);505if (!isHostNameChar(c)) {506return false;507}508}509return !(label.startsWith("-") || label.endsWith("-"));510}511512private static boolean isHostNameChar(char c) {513return (c == '-' ||514c >= 'a' && c <= 'z' ||515c >= 'A' && c <= 'Z' ||516c >= '0' && c <= '9');517}518519private static boolean isDigit(char c) {520return (c >= '0' && c <= '9');521}522523/*524* Append a label to buf, escaping as needed.525*/526private static void escape(StringBuilder buf, String label) {527for (int i = 0; i < label.length(); i++) {528char c = label.charAt(i);529if (c == '.' || c == '\\') {530buf.append('\\');531}532buf.append(c);533}534}535536/*537* Compares two labels, ignoring case for ASCII values.538* Returns negative, zero, or positive as the first label539* is less than, equal to, or greater than the second.540* See keyForLabel().541*/542private static int compareLabels(String label1, String label2) {543int min = Math.min(label1.length(), label2.length());544for (int i = 0; i < min; i++) {545char c1 = label1.charAt(i);546char c2 = label2.charAt(i);547if (c1 >= 'A' && c1 <= 'Z') {548c1 += 'a' - 'A'; // to lower case549}550if (c2 >= 'A' && c2 <= 'Z') {551c2 += 'a' - 'A'; // to lower case552}553if (c1 != c2) {554return (c1 - c2);555}556}557return (label1.length() - label2.length()); // the longer one wins558}559560/*561* Returns a key suitable for hashing a label. Two labels map to562* the same key iff they are equal, taking possible case-folding563* into account. See compareLabels().564*/565private static String keyForLabel(String label) {566StringBuffer buf = new StringBuffer(label.length());567for (int i = 0; i < label.length(); i++) {568char c = label.charAt(i);569if (c >= 'A' && c <= 'Z') {570c += 'a' - 'A'; // to lower case571}572buf.append(c);573}574return buf.toString();575}576577578/**579* Serializes only the domain name string, for compactness and to avoid580* any implementation dependency.581*582* @serialdata The domain name string.583*/584private void writeObject(java.io.ObjectOutputStream s)585throws java.io.IOException {586s.writeObject(toString());587}588589private void readObject(java.io.ObjectInputStream s)590throws java.io.IOException, ClassNotFoundException {591try {592parse((String) s.readObject());593} catch (InvalidNameException e) {594// shouldn't happen595throw new java.io.StreamCorruptedException(596"Invalid name: " + domain);597}598}599600private static final long serialVersionUID = 7040187611324710271L;601}602603604