NameUtil.java

  1package de.measite.minidns.util;
  2
  3import java.io.ByteArrayOutputStream;
  4import java.io.DataInputStream;
  5import java.io.DataOutputStream;
  6import java.io.IOException;
  7import java.net.IDN;
  8import java.util.HashSet;
  9import java.util.Arrays;
 10
 11/**
 12 * Utilities related to internationalized domain names and dns name handling.
 13 */
 14public class NameUtil {
 15
 16    /**
 17     * Retrieve the rough binary length of a string
 18     * (length + 2 bytes length prefix).
 19     * @param name The name string.
 20     * @return The binary size of the string (length + 2).
 21     */
 22    public static int size(String name) {
 23        return name.length() + 2;
 24    }
 25
 26    /**
 27     * Check if two internationalized domain names are equal, possibly causing
 28     * a serialization of both domain names.
 29     * @param name1 The first domain name.
 30     * @param name2 The second domain name.
 31     * @return True if both domain names are the same.
 32     */
 33    public static boolean idnEquals(String name1, String name2) {
 34        if (name1 == name2) return true; // catches null, null
 35        if (name1 == null) return false;
 36        if (name2 == null) return false;
 37        if (name1.equals(name2)) return true;
 38
 39        try {
 40            return Arrays.equals(toByteArray(name1),toByteArray(name2));
 41        } catch (IOException e) {
 42            return false; // impossible
 43        }
 44    }
 45
 46    /**
 47     * Serialize a domain name under IDN rules.
 48     * @param name The domain name.
 49     * @return The binary domain name representation.
 50     * @throws IOException Should never happen.
 51     */
 52    public static byte[] toByteArray(String name) throws IOException {
 53        ByteArrayOutputStream baos = new ByteArrayOutputStream(64);
 54        DataOutputStream dos = new DataOutputStream(baos);
 55        for (String s: name.split("[.\u3002\uFF0E\uFF61]")) {
 56            byte[] buffer = IDN.toASCII(s).getBytes();
 57            dos.writeByte(buffer.length);
 58            dos.write(buffer);
 59        }
 60        dos.writeByte(0);
 61        dos.flush();
 62        return baos.toByteArray();
 63    }
 64
 65    /**
 66     * Parse a domain name starting at the current offset and moving the input
 67     * stream pointer past this domain name (even if cross references occure).
 68     * @param dis The input stream.
 69     * @param data The raw data (for cross references).
 70     * @return The domain name string.
 71     * @throws IOException Should never happen.
 72     */
 73    public static String parse(DataInputStream dis, byte data[]) 
 74        throws IOException
 75    {
 76        int c = dis.readUnsignedByte();
 77        if ((c & 0xc0) == 0xc0) {
 78            c = ((c & 0x3f) << 8) + dis.readUnsignedByte();
 79            HashSet<Integer> jumps = new HashSet<Integer>();
 80            jumps.add(c);
 81            return parse(data, c, jumps);
 82        }
 83        if (c == 0) {
 84            return "";
 85        }
 86        byte b[] = new byte[c];
 87        dis.readFully(b);
 88        String s = IDN.toUnicode(new String(b));
 89        String t = parse(dis, data);
 90        if (t.length() > 0) {
 91            s = s + "." + t;
 92        }
 93        return s;
 94    }
 95
 96    /**
 97     * Parse a domain name starting at the given offset. 
 98     * @param data The raw data.
 99     * @param offset The offset.
100     * @param jumps The list of jumps (by now).
101     * @return The parsed domain name.
102     * @throws IllegalStateException on cycles.
103     */
104    public static String parse(
105        byte data[],
106        int offset,
107        HashSet<Integer> jumps
108    ) {
109        int c = data[offset] & 0xff;
110        if ((c & 0xc0) == 0xc0) {
111            c = ((c & 0x3f) << 8) + (data[offset + 1] & 0xff);
112            if (jumps.contains(c)) {
113                throw new IllegalStateException("Cyclic offsets detected.");
114            }
115            jumps.add(c);
116            return parse(data, c, jumps);
117        }
118        if (c == 0) {
119            return "";
120        }
121        String s = new String(data,offset + 1, c);
122        String t = parse(data, offset + 1 + c, jumps);
123        if (t.length() > 0) {
124            s = s + "." + t;
125        }
126        return s;
127    }
128
129}