30
30
import java .io .IOException ;
31
31
import java .io .InputStreamReader ;
32
32
import java .net .HttpURLConnection ;
33
+ import java .net .InetSocketAddress ;
33
34
import java .net .URISyntaxException ;
34
35
import java .net .URL ;
36
+ import java .util .regex .Pattern ;
37
+
38
+ import org .spoutcraft .client .gui .server .ServerItem ;
39
+ import org .xbill .DNS .Lookup ;
40
+ import org .xbill .DNS .Record ;
41
+ import org .xbill .DNS .SRVRecord ;
42
+ import org .xbill .DNS .TextParseException ;
43
+ import org .xbill .DNS .Type ;
35
44
36
45
public class NetworkUtils {
46
+ /**
47
+ * A {@link Pattern} matching valid DNS hostnames (as opposed to IP addresses).
48
+ */
49
+ private static final Pattern HOSTNAME_PATTERN = Pattern .compile ("[a-zA-Z-]" );
50
+
37
51
public static void pingUrl (String Url ) {
38
52
try {
39
53
URL url = new URL (Url );
@@ -58,4 +72,96 @@ public static void openInBrowser(String url) {
58
72
e .printStackTrace ();
59
73
}
60
74
}
75
+
76
+ /**
77
+ * Resolves a hostname to an {@link InetSocketAddress}, encapsulating an IP address and
78
+ * port. <code>hostname</code> may represent a valid DNS hostname or an IP address. If
79
+ * <code>hostname</code> is a valid DNS hostname <em>and</em> <code>port</code> is equal
80
+ * to {@link ServerItem#DEFAULT_PORT} (implying the user may not have specified a server
81
+ * port), a <a href="http://en.wikipedia.org/wiki/SRV_record">SRV record</a> lookup may
82
+ * be attempted—see {@link #resolve(String)} for implementation details.
83
+ * <p>
84
+ * This method is the equivalent of passing <code>true</code> to {@link #resolve(String, int, boolean)}.
85
+ *
86
+ * @param hostname the DNS hostname to resolve.
87
+ * @param port the port number to encapsulate within the <code>InetSocketAddress</code>.
88
+ * @return an {@link InetSocketAddress}, which may be marked unresolved if hostname lookup failed.
89
+ */
90
+ public static InetSocketAddress resolve (String hostname , int port ) {
91
+ return resolve (hostname , port , true );
92
+ }
93
+
94
+ /**
95
+ * Resolves a hostname to an {@link InetSocketAddress}, encapsulating an IP address and
96
+ * port. An optional <a href="http://en.wikipedia.org/wiki/SRV_record">SRV record</a>
97
+ * lookup may be attempted—see {@link #resolve(String)} for implementation details.
98
+ * A SRV record lookup will only be attempted if <code>hostname</code> represents a valid
99
+ * DNS hostname, and <code>port</code> is equal to {@link ServerItem#DEFAULT_PORT} (implying
100
+ * the user may not have specified a server port).
101
+ * <p>
102
+ * If <code>srv</code> is <code>true</code> and SRV record lookup fails or returns no
103
+ * results, a regular DNS lookup will be attempted.
104
+ * <p>
105
+ * If the given <code>hostname</code> represents an IP address, it will not be resolved.
106
+ *
107
+ * @param hostname the DNS hostname to resolve.
108
+ * @param port the port number to encapsulate within the <code>InetSocketAddress</code>.
109
+ * @param srv whether to attempt a SRV record lookup.
110
+ * @return an {@link InetSocketAddress}, which may be marked unresolved if hostname lookup failed.
111
+ */
112
+ public static InetSocketAddress resolve (String hostname , int port , boolean srv ) {
113
+ if (srv && (port == ServerItem .DEFAULT_PORT ) && (HOSTNAME_PATTERN .matcher (hostname ).find ())) {
114
+ try {
115
+ InetSocketAddress address = resolve (hostname );
116
+ if (address != null ) {
117
+ return address ;
118
+ }
119
+ } catch (TextParseException e ) {
120
+ // Do nothing: fall back on a regular DNS lookup before failing
121
+ }
122
+ }
123
+
124
+ return new InetSocketAddress (hostname , port );
125
+ }
126
+
127
+ /**
128
+ * Resolves a hostname to an {@link InetSocketAddress}, encapsulating an IP address and
129
+ * port, using a <a href="http://en.wikipedia.org/wiki/SRV_record">SRV record</a> lookup.
130
+ * This method assumes <code>minecraft</code> as the service name during the DNS query,
131
+ * such that a matching record looks like:
132
+ * <p>
133
+ * <code>_minecraft._tcp.example.com. 86400 IN SRV 0 1 25565 minecraft.example.com.</code>
134
+ * <p>
135
+ * If multiple SRV records exist for a given hostname, the record with the highest priority
136
+ * (that is, the lowest priority value) is selected. This implementation does not take
137
+ * into account relative weights for records with identical priorities, and behavior in
138
+ * such cases is undefined.
139
+ *
140
+ * @param hostname the DNS hostname to on which to perform a SRV lookup.
141
+ * @return an {@link InetSocketAddress}, or <code>null</code> if no SRV record was found.
142
+ * @throws TextParseException if the hostname is malformed.
143
+ */
144
+ public static InetSocketAddress resolve (String hostname ) throws TextParseException {
145
+ String query = "_minecraft._tcp." + hostname .trim ();
146
+ Record [] records = new Lookup (query , Type .SRV ).run ();
147
+
148
+ if ((records != null ) && (records .length > 0 )) {
149
+ SRVRecord srv = (SRVRecord )records [0 ];
150
+
151
+ if (records .length > 1 ) {
152
+ for (int i = 1 ; i < records .length ; i ++) {
153
+ SRVRecord record = (SRVRecord )records [i ];
154
+ if (record .getPriority () < srv .getPriority ()) {
155
+ srv = record ;
156
+ }
157
+ }
158
+ }
159
+
160
+ String host = srv .getTarget ().toString ().replaceAll ("\\ .+$" , "" );
161
+ int port = srv .getPort ();
162
+ return new InetSocketAddress (host , port );
163
+ }
164
+
165
+ return null ;
166
+ }
61
167
}
0 commit comments