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.dnslabel; 012 013import java.io.ByteArrayOutputStream; 014import java.util.Locale; 015 016/** 017 * A DNS label is an individual component of a DNS name. Labels are usually shown separated by dots. 018 * 019 * @see <a href="https://tools.ietf.org/html/rfc5890#section-2.2">RFC 5890 § 2.2. DNS-Related Terminology</a> 020 * @author Florian Schmaus 021 * 022 */ 023public abstract class DnsLabel implements CharSequence { 024 025 /** 026 * The maximum length of a DNS label in octets. 027 * 028 * @see <a href="https://tools.ietf.org/html/rfc1035">RFC 1035 § 2.3.4.</a> 029 */ 030 public static final int MAX_LABEL_LENGTH_IN_OCTETS = 63; 031 032 /** 033 * Whether or not the DNS label is validated on construction. 034 */ 035 public static boolean VALIDATE = true; 036 037 public final String label; 038 039 protected DnsLabel(String label) { 040 this.label = label; 041 042 if (!VALIDATE) { 043 return; 044 } 045 046 setBytesIfRequired(); 047 if (byteCache.length > MAX_LABEL_LENGTH_IN_OCTETS) { 048 throw new LabelToLongException(label); 049 } 050 } 051 052 private transient String internationalizedRepresentation; 053 054 public final String getInternationalizedRepresentation() { 055 if (internationalizedRepresentation == null) { 056 internationalizedRepresentation = getInternationalizedRepresentationInternal(); 057 } 058 return internationalizedRepresentation; 059 } 060 061 protected String getInternationalizedRepresentationInternal() { 062 return label; 063 } 064 065 public final String getLabelType() { 066 return getClass().getSimpleName(); 067 } 068 069 @Override 070 public final int length() { 071 return label.length(); 072 } 073 074 @Override 075 public final char charAt(int index) { 076 return label.charAt(index); 077 } 078 079 @Override 080 public final CharSequence subSequence(int start, int end) { 081 return label.subSequence(start, end); 082 } 083 084 @Override 085 public final String toString() { 086 return label; 087 } 088 089 @Override 090 public final boolean equals(Object other) { 091 if (!(other instanceof DnsLabel)) { 092 return false; 093 } 094 DnsLabel otherDnsLabel = (DnsLabel) other; 095 return label.equals(otherDnsLabel.label); 096 } 097 098 @Override 099 public final int hashCode() { 100 return label.hashCode(); 101 } 102 103 private transient DnsLabel lowercasedVariant; 104 105 public final DnsLabel asLowercaseVariant() { 106 if (lowercasedVariant == null) { 107 String lowercaseLabel = label.toLowerCase(Locale.US); 108 lowercasedVariant = DnsLabel.from(lowercaseLabel); 109 } 110 return lowercasedVariant; 111 } 112 113 private transient byte[] byteCache; 114 115 private void setBytesIfRequired() { 116 if (byteCache == null) { 117 byteCache = label.getBytes(); 118 } 119 } 120 121 public final void writeToBoas(ByteArrayOutputStream byteArrayOutputStream) { 122 setBytesIfRequired(); 123 124 byteArrayOutputStream.write(byteCache.length); 125 byteArrayOutputStream.write(byteCache, 0, byteCache.length); 126 } 127 128 public static DnsLabel from(String label) { 129 if (label == null || label.isEmpty()) { 130 throw new IllegalArgumentException("Label is null or empty"); 131 } 132 133 if (LdhLabel.isLdhLabel(label)) { 134 return LdhLabel.fromInternal(label); 135 } 136 137 return NonLdhLabel.fromInternal(label); 138 } 139 140 public static DnsLabel[] from(String[] labels) { 141 DnsLabel[] res = new DnsLabel[labels.length]; 142 143 for (int i = 0; i < labels.length; i++) { 144 res[i] = DnsLabel.from(labels[i]); 145 } 146 147 return res; 148 } 149 150 public static boolean isIdnAcePrefixed(String string) { 151 return string.toLowerCase(Locale.US).startsWith("xn--"); 152 } 153 154 public static class LabelToLongException extends IllegalArgumentException { 155 156 /** 157 * 158 */ 159 private static final long serialVersionUID = 1L; 160 161 public final String label; 162 163 LabelToLongException(String label) { 164 this.label = label; 165 } 166 } 167}