aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/de/measite/minidns/util/NameUtil.java
blob: 7ae373bcd9f77b032dbbf63b95da1dca43834c03 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package de.measite.minidns.util;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.IDN;
import java.util.HashSet;
import java.util.Arrays;

/**
 * Utilities related to internationalized domain names and dns name handling.
 */
public class NameUtil {

    /**
     * Retrieve the rough binary length of a string
     * (length + 2 bytes length prefix).
     * @param name The name string.
     * @return The binary size of the string (length + 2).
     */
    public static int size(String name) {
        return name.length() + 2;
    }

    /**
     * Check if two internationalized domain names are equal, possibly causing
     * a serialization of both domain names.
     * @param name1 The first domain name.
     * @param name2 The second domain name.
     * @return True if both domain names are the same.
     */
    public static boolean idnEquals(String name1, String name2) {
        if (name1 == name2) return true; // catches null, null
        if (name1 == null) return false;
        if (name2 == null) return false;
        if (name1.equals(name2)) return true;

        try {
            return Arrays.equals(toByteArray(name1),toByteArray(name2));
        } catch (IOException e) {
            return false; // impossible
        }
    }

    /**
     * Serialize a domain name under IDN rules.
     * @param name The domain name.
     * @return The binary domain name representation.
     * @throws IOException Should never happen.
     */
    public static byte[] toByteArray(String name) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream(64);
        DataOutputStream dos = new DataOutputStream(baos);
        for (String s: name.split("[.\u3002\uFF0E\uFF61]")) {
            byte[] buffer = IDN.toASCII(s).getBytes();
            dos.writeByte(buffer.length);
            dos.write(buffer);
        }
        dos.writeByte(0);
        dos.flush();
        return baos.toByteArray();
    }

    /**
     * Parse a domain name starting at the current offset and moving the input
     * stream pointer past this domain name (even if cross references occure).
     * @param dis The input stream.
     * @param data The raw data (for cross references).
     * @return The domain name string.
     * @throws IOException Should never happen.
     */
    public static String parse(DataInputStream dis, byte data[]) 
        throws IOException
    {
        int c = dis.readUnsignedByte();
        if ((c & 0xc0) == 0xc0) {
            c = ((c & 0x3f) << 8) + dis.readUnsignedByte();
            HashSet<Integer> jumps = new HashSet<Integer>();
            jumps.add(c);
            return parse(data, c, jumps);
        }
        if (c == 0) {
            return "";
        }
        byte b[] = new byte[c];
        dis.readFully(b);
        String s = IDN.toUnicode(new String(b));
        String t = parse(dis, data);
        if (t.length() > 0) {
            s = s + "." + t;
        }
        return s;
    }

    /**
     * Parse a domain name starting at the given offset. 
     * @param data The raw data.
     * @param offset The offset.
     * @param jumps The list of jumps (by now).
     * @return The parsed domain name.
     * @throws IllegalStateException on cycles.
     */
    public static String parse(
        byte data[],
        int offset,
        HashSet<Integer> jumps
    ) {
        int c = data[offset] & 0xff;
        if ((c & 0xc0) == 0xc0) {
            c = ((c & 0x3f) << 8) + (data[offset + 1] & 0xff);
            if (jumps.contains(c)) {
                throw new IllegalStateException("Cyclic offsets detected.");
            }
            jumps.add(c);
            return parse(data, c, jumps);
        }
        if (c == 0) {
            return "";
        }
        String s = new String(data,offset + 1, c);
        String t = parse(data, offset + 1 + c, jumps);
        if (t.length() > 0) {
            s = s + "." + t;
        }
        return s;
    }

}