Shuyi Chen | d7955ce | 2013-05-22 14:51:55 -0700 | [diff] [blame] | 1 | // Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) |
| 2 | |
| 3 | package org.xbill.DNS; |
| 4 | |
| 5 | import java.io.*; |
| 6 | import java.security.*; |
| 7 | |
| 8 | import org.xbill.DNS.utils.*; |
| 9 | |
| 10 | /** |
| 11 | * Next SECure name 3 - this record contains the next hashed name in an |
| 12 | * ordered list of hashed names in the zone, and a set of types for which |
| 13 | * records exist for this name. The presence of this record in a response |
| 14 | * signifies a negative response from a DNSSEC-signed zone. |
| 15 | * |
| 16 | * This replaces the NSEC and NXT records, when used. |
| 17 | * |
| 18 | * @author Brian Wellington |
| 19 | * @author David Blacka |
| 20 | */ |
| 21 | |
| 22 | public class NSEC3Record extends Record { |
| 23 | |
| 24 | public static class Flags { |
| 25 | /** |
| 26 | * NSEC3 flags identifiers. |
| 27 | */ |
| 28 | |
| 29 | private Flags() {} |
| 30 | |
| 31 | /** Unsigned delegation are not included in the NSEC3 chain. |
| 32 | * |
| 33 | */ |
| 34 | public static final int OPT_OUT = 0x01; |
| 35 | } |
| 36 | |
| 37 | public static class Digest { |
| 38 | private Digest() {} |
| 39 | |
| 40 | /** SHA-1 */ |
| 41 | public static final int SHA1 = 1; |
| 42 | } |
| 43 | |
| 44 | public static final int SHA1_DIGEST_ID = Digest.SHA1; |
| 45 | |
| 46 | private static final long serialVersionUID = -7123504635968932855L; |
| 47 | |
| 48 | private int hashAlg; |
| 49 | private int flags; |
| 50 | private int iterations; |
| 51 | private byte [] salt; |
| 52 | private byte [] next; |
| 53 | private TypeBitmap types; |
| 54 | |
| 55 | private static final base32 b32 = new base32(base32.Alphabet.BASE32HEX, |
| 56 | false, false); |
| 57 | |
| 58 | NSEC3Record() {} |
| 59 | |
| 60 | Record getObject() { |
| 61 | return new NSEC3Record(); |
| 62 | } |
| 63 | |
| 64 | /** |
| 65 | * Creates an NSEC3 record from the given data. |
| 66 | * |
| 67 | * @param name The ownername of the NSEC3 record (base32'd hash plus zonename). |
| 68 | * @param dclass The class. |
| 69 | * @param ttl The TTL. |
| 70 | * @param hashAlg The hash algorithm. |
| 71 | * @param flags The value of the flags field. |
| 72 | * @param iterations The number of hash iterations. |
| 73 | * @param salt The salt to use (may be null). |
| 74 | * @param next The next hash (may not be null). |
| 75 | * @param types The types present at the original ownername. |
| 76 | */ |
| 77 | public NSEC3Record(Name name, int dclass, long ttl, int hashAlg, |
| 78 | int flags, int iterations, byte [] salt, byte [] next, |
| 79 | int [] types) |
| 80 | { |
| 81 | super(name, Type.NSEC3, dclass, ttl); |
| 82 | this.hashAlg = checkU8("hashAlg", hashAlg); |
| 83 | this.flags = checkU8("flags", flags); |
| 84 | this.iterations = checkU16("iterations", iterations); |
| 85 | |
| 86 | if (salt != null) { |
| 87 | if (salt.length > 255) |
| 88 | throw new IllegalArgumentException("Invalid salt"); |
| 89 | if (salt.length > 0) { |
| 90 | this.salt = new byte[salt.length]; |
| 91 | System.arraycopy(salt, 0, this.salt, 0, salt.length); |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | if (next.length > 255) { |
| 96 | throw new IllegalArgumentException("Invalid next hash"); |
| 97 | } |
| 98 | this.next = new byte[next.length]; |
| 99 | System.arraycopy(next, 0, this.next, 0, next.length); |
| 100 | this.types = new TypeBitmap(types); |
| 101 | } |
| 102 | |
| 103 | void |
| 104 | rrFromWire(DNSInput in) throws IOException { |
| 105 | hashAlg = in.readU8(); |
| 106 | flags = in.readU8(); |
| 107 | iterations = in.readU16(); |
| 108 | |
| 109 | int salt_length = in.readU8(); |
| 110 | if (salt_length > 0) |
| 111 | salt = in.readByteArray(salt_length); |
| 112 | else |
| 113 | salt = null; |
| 114 | |
| 115 | int next_length = in.readU8(); |
| 116 | next = in.readByteArray(next_length); |
| 117 | types = new TypeBitmap(in); |
| 118 | } |
| 119 | |
| 120 | void |
| 121 | rrToWire(DNSOutput out, Compression c, boolean canonical) { |
| 122 | out.writeU8(hashAlg); |
| 123 | out.writeU8(flags); |
| 124 | out.writeU16(iterations); |
| 125 | |
| 126 | if (salt != null) { |
| 127 | out.writeU8(salt.length); |
| 128 | out.writeByteArray(salt); |
| 129 | } else |
| 130 | out.writeU8(0); |
| 131 | |
| 132 | out.writeU8(next.length); |
| 133 | out.writeByteArray(next); |
| 134 | types.toWire(out); |
| 135 | } |
| 136 | |
| 137 | void |
| 138 | rdataFromString(Tokenizer st, Name origin) throws IOException { |
| 139 | hashAlg = st.getUInt8(); |
| 140 | flags = st.getUInt8(); |
| 141 | iterations = st.getUInt16(); |
| 142 | |
| 143 | String s = st.getString(); |
| 144 | if (s.equals("-")) |
| 145 | salt = null; |
| 146 | else { |
| 147 | st.unget(); |
| 148 | salt = st.getHexString(); |
| 149 | if (salt.length > 255) |
| 150 | throw st.exception("salt value too long"); |
| 151 | } |
| 152 | |
| 153 | next = st.getBase32String(b32); |
| 154 | types = new TypeBitmap(st); |
| 155 | } |
| 156 | |
| 157 | /** Converts rdata to a String */ |
| 158 | String |
| 159 | rrToString() { |
| 160 | StringBuffer sb = new StringBuffer(); |
| 161 | sb.append(hashAlg); |
| 162 | sb.append(' '); |
| 163 | sb.append(flags); |
| 164 | sb.append(' '); |
| 165 | sb.append(iterations); |
| 166 | sb.append(' '); |
| 167 | if (salt == null) |
| 168 | sb.append('-'); |
| 169 | else |
| 170 | sb.append(base16.toString(salt)); |
| 171 | sb.append(' '); |
| 172 | sb.append(b32.toString(next)); |
| 173 | |
| 174 | if (!types.empty()) { |
| 175 | sb.append(' '); |
| 176 | sb.append(types.toString()); |
| 177 | } |
| 178 | |
| 179 | return sb.toString(); |
| 180 | } |
| 181 | |
| 182 | /** Returns the hash algorithm */ |
| 183 | public int |
| 184 | getHashAlgorithm() { |
| 185 | return hashAlg; |
| 186 | } |
| 187 | |
| 188 | /** Returns the flags */ |
| 189 | public int |
| 190 | getFlags() { |
| 191 | return flags; |
| 192 | } |
| 193 | |
| 194 | /** Returns the number of iterations */ |
| 195 | public int |
| 196 | getIterations() { |
| 197 | return iterations; |
| 198 | } |
| 199 | |
| 200 | /** Returns the salt */ |
| 201 | public byte [] |
| 202 | getSalt() |
| 203 | { |
| 204 | return salt; |
| 205 | } |
| 206 | |
| 207 | /** Returns the next hash */ |
| 208 | public byte [] |
| 209 | getNext() { |
| 210 | return next; |
| 211 | } |
| 212 | |
| 213 | /** Returns the set of types defined for this name */ |
| 214 | public int [] |
| 215 | getTypes() { |
| 216 | return types.toArray(); |
| 217 | } |
| 218 | |
| 219 | /** Returns whether a specific type is in the set of types. */ |
| 220 | public boolean |
| 221 | hasType(int type) |
| 222 | { |
| 223 | return types.contains(type); |
| 224 | } |
| 225 | |
| 226 | static byte [] |
| 227 | hashName(Name name, int hashAlg, int iterations, byte [] salt) |
| 228 | throws NoSuchAlgorithmException |
| 229 | { |
| 230 | MessageDigest digest; |
| 231 | switch (hashAlg) { |
| 232 | case Digest.SHA1: |
| 233 | digest = MessageDigest.getInstance("sha-1"); |
| 234 | break; |
| 235 | default: |
| 236 | throw new NoSuchAlgorithmException("Unknown NSEC3 algorithm" + |
| 237 | "identifier: " + |
| 238 | hashAlg); |
| 239 | } |
| 240 | byte [] hash = null; |
| 241 | for (int i = 0; i <= iterations; i++) { |
| 242 | digest.reset(); |
| 243 | if (i == 0) |
| 244 | digest.update(name.toWireCanonical()); |
| 245 | else |
| 246 | digest.update(hash); |
| 247 | if (salt != null) |
| 248 | digest.update(salt); |
| 249 | hash = digest.digest(); |
| 250 | } |
| 251 | return hash; |
| 252 | } |
| 253 | |
| 254 | /** |
| 255 | * Hashes a name with the parameters of this NSEC3 record. |
| 256 | * @param name The name to hash |
| 257 | * @return The hashed version of the name |
| 258 | * @throws NoSuchAlgorithmException The hash algorithm is unknown. |
| 259 | */ |
| 260 | public byte [] |
| 261 | hashName(Name name) throws NoSuchAlgorithmException |
| 262 | { |
| 263 | return hashName(name, hashAlg, iterations, salt); |
| 264 | } |
| 265 | |
| 266 | } |