001: /* jcifs smb client library in Java
002: * Copyright (C) 2000 "Michael B. Allen" <jcifs at samba dot org>
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2.1 of the License, or (at your option) any later version.
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */
018:
019: package jcifs;
020:
021: import java.net.InetAddress;
022: import java.net.UnknownHostException;
023: import java.io.IOException;
024: import java.util.StringTokenizer;
025: import jcifs.netbios.NbtAddress;
026: import jcifs.netbios.Lmhosts;
027: import jcifs.util.LogStream;
028:
029: /**
030: * <p>Under normal conditions it is not necessary to use
031: * this class to use jCIFS properly. Name resolusion is
032: * handled internally to the <code>jcifs.smb</code> package.
033: * <p>
034: * This class is a wrapper for both {@link jcifs.netbios.NbtAddress}
035: * and {@link java.net.InetAddress}. The name resolution mechanisms
036: * used will systematically query all available configured resolution
037: * services including WINS, broadcasts, DNS, and LMHOSTS. See
038: * <a href="../../resolver.html">Setting Name Resolution Properties</a>
039: * and the <code>jcifs.resolveOrder</code> property. Changing
040: * jCIFS name resolution properties can greatly affect the behavior of
041: * the client and may be necessary for proper operation.
042: * <p>
043: * This class should be used in favor of <tt>InetAddress</tt> to resolve
044: * hostnames on LANs and WANs that support a mixture of NetBIOS/WINS and
045: * DNS resolvable hosts.
046: */
047:
048: public class UniAddress {
049:
050: private static final int RESOLVER_WINS = 0;
051: private static final int RESOLVER_BCAST = 1;
052: private static final int RESOLVER_DNS = 2;
053: private static final int RESOLVER_LMHOSTS = 3;
054:
055: private static int[] resolveOrder;
056: private static InetAddress baddr;
057:
058: private static LogStream log = LogStream.getInstance();
059:
060: static {
061: String ro = Config.getProperty("jcifs.resolveOrder");
062: InetAddress nbns = NbtAddress.getWINSAddress();
063:
064: try {
065: baddr = Config.getInetAddress("jcifs.netbios.baddr",
066: InetAddress.getByName("255.255.255.255"));
067: } catch (UnknownHostException uhe) {
068: }
069:
070: if (ro == null || ro.length() == 0) {
071:
072: /* No resolveOrder has been specified, use the
073: * default which is LMHOSTS,WINS,BCAST,DNS or just
074: * LMHOSTS,BCAST,DNS if jcifs.netbios.wins has not
075: * been specified.
076: */
077:
078: if (nbns == null) {
079: resolveOrder = new int[3];
080: resolveOrder[0] = RESOLVER_LMHOSTS;
081: resolveOrder[1] = RESOLVER_BCAST;
082: resolveOrder[2] = RESOLVER_DNS;
083: } else {
084: resolveOrder = new int[4];
085: resolveOrder[0] = RESOLVER_LMHOSTS;
086: resolveOrder[1] = RESOLVER_WINS;
087: resolveOrder[2] = RESOLVER_BCAST;
088: resolveOrder[3] = RESOLVER_DNS;
089: }
090: } else {
091: int[] tmp = new int[4];
092: StringTokenizer st = new StringTokenizer(ro, ",");
093: int i = 0;
094: while (st.hasMoreTokens()) {
095: String s = st.nextToken().trim();
096: if (s.equalsIgnoreCase("LMHOSTS")) {
097: tmp[i++] = RESOLVER_LMHOSTS;
098: } else if (s.equalsIgnoreCase("WINS")) {
099: if (nbns == null) {
100: if (log.level > 1) {
101: log
102: .println("UniAddress resolveOrder specifies WINS however the "
103: + "jcifs.netbios.wins property has not been set");
104: }
105: continue;
106: }
107: tmp[i++] = RESOLVER_WINS;
108: } else if (s.equalsIgnoreCase("BCAST")) {
109: tmp[i++] = RESOLVER_BCAST;
110: } else if (s.equalsIgnoreCase("DNS")) {
111: tmp[i++] = RESOLVER_DNS;
112: } else if (log.level > 1) {
113: log.println("unknown resolver method: " + s);
114: }
115: }
116: resolveOrder = new int[i];
117: System.arraycopy(tmp, 0, resolveOrder, 0, i);
118: }
119: }
120:
121: static class Sem {
122: Sem(int count) {
123: this .count = count;
124: }
125:
126: int count;
127: }
128:
129: static class QueryThread extends Thread {
130:
131: Sem sem;
132: String host, scope;
133: int type;
134: NbtAddress ans = null;
135: InetAddress svr;
136: UnknownHostException uhe;
137:
138: QueryThread(Sem sem, String host, int type, String scope,
139: InetAddress svr) {
140: super ("JCIFS-QueryThread: " + host);
141: this .sem = sem;
142: this .host = host;
143: this .type = type;
144: this .scope = scope;
145: this .svr = svr;
146: }
147:
148: public void run() {
149: try {
150: ans = NbtAddress.getByName(host, type, scope, svr);
151: } catch (UnknownHostException uhe) {
152: this .uhe = uhe;
153: } catch (Exception ex) {
154: this .uhe = new UnknownHostException(ex.getMessage());
155: } finally {
156: synchronized (sem) {
157: sem.count--;
158: sem.notify();
159: }
160: }
161: }
162: }
163:
164: static NbtAddress lookupServerOrWorkgroup(String name,
165: InetAddress svr) throws UnknownHostException {
166: Sem sem = new Sem(2);
167: int type = NbtAddress.isWINS(svr) ? 0x1b : 0x1d;
168:
169: QueryThread q1x = new QueryThread(sem, name, type, null, svr);
170: QueryThread q20 = new QueryThread(sem, name, 0x20, null, svr);
171: q1x.setDaemon(true);
172: q20.setDaemon(true);
173: try {
174: synchronized (sem) {
175: q1x.start();
176: q20.start();
177:
178: while (sem.count > 0 && q1x.ans == null
179: && q20.ans == null) {
180: sem.wait();
181: }
182: }
183: } catch (InterruptedException ie) {
184: throw new UnknownHostException(name);
185: }
186: if (q1x.ans != null) {
187: return q1x.ans;
188: } else if (q20.ans != null) {
189: return q20.ans;
190: } else {
191: throw q1x.uhe;
192: }
193: }
194:
195: /**
196: * Determines the address of a host given it's host name. The name can be a
197: * machine name like "jcifs.samba.org", or an IP address like "192.168.1.15".
198: *
199: * @param hostname NetBIOS or DNS hostname to resolve
200: * @throws java.net.UnknownHostException if there is an error resolving the name
201: */
202:
203: public static UniAddress getByName(String hostname)
204: throws UnknownHostException {
205: return getByName(hostname, false);
206: }
207:
208: static boolean isDotQuadIP(String hostname) {
209: if (Character.isDigit(hostname.charAt(0))) {
210: int i, len, dots;
211: char[] data;
212:
213: i = dots = 0; /* quick IP address validation */
214: len = hostname.length();
215: data = hostname.toCharArray();
216: while (i < len && Character.isDigit(data[i++])) {
217: if (i == len && dots == 3) {
218: // probably an IP address
219: return true;
220: }
221: if (i < len && data[i] == '.') {
222: dots++;
223: i++;
224: }
225: }
226: }
227:
228: return false;
229: }
230:
231: static boolean isValidDnsName(String hostname) {
232: // Simple for now. Just handles "1" --> 1/0.0.0.1 kind of stuff
233: return Character.isDigit(hostname.charAt(0)) == false;
234: }
235:
236: /**
237: * Lookup <tt>hostname</tt> and return it's <tt>UniAddress</tt>. If the
238: * <tt>possibleNTDomainOrWorkgroup</tt> parameter is <tt>true</tt> an
239: * addtional name query will be performed to locate a master browser.
240: */
241:
242: public static UniAddress getByName(String hostname,
243: boolean possibleNTDomainOrWorkgroup)
244: throws UnknownHostException {
245: Object addr;
246: int i;
247:
248: if (hostname == null || hostname.length() == 0) {
249: throw new UnknownHostException();
250: }
251:
252: if (isDotQuadIP(hostname)) {
253: return new UniAddress(NbtAddress.getByName(hostname));
254: }
255:
256: for (i = 0; i < resolveOrder.length; i++) {
257: try {
258: switch (resolveOrder[i]) {
259: case RESOLVER_LMHOSTS:
260: if ((addr = Lmhosts.getByName(hostname)) == null) {
261: continue;
262: }
263: break;
264: case RESOLVER_WINS:
265: if (hostname == NbtAddress.MASTER_BROWSER_NAME
266: || hostname.length() > 15) {
267: // invalid netbios name
268: continue;
269: }
270: if (possibleNTDomainOrWorkgroup) {
271: addr = lookupServerOrWorkgroup(hostname,
272: NbtAddress.getWINSAddress());
273: } else {
274: addr = NbtAddress.getByName(hostname, 0x20,
275: null, NbtAddress.getWINSAddress());
276: }
277: break;
278: case RESOLVER_BCAST:
279: if (hostname.length() > 15) {
280: // invalid netbios name
281: continue;
282: }
283: if (possibleNTDomainOrWorkgroup) {
284: addr = lookupServerOrWorkgroup(hostname, baddr);
285: } else {
286: addr = NbtAddress.getByName(hostname, 0x20,
287: null, baddr);
288: }
289: break;
290: case RESOLVER_DNS:
291: if (isValidDnsName(hostname) == false) {
292: throw new UnknownHostException(hostname);
293: }
294: addr = InetAddress.getByName(hostname);
295: break;
296: default:
297: throw new UnknownHostException(hostname);
298: }
299: return new UniAddress(addr); // Success
300: } catch (IOException ioe) {
301: // Failure
302: }
303: }
304: throw new UnknownHostException(hostname);
305: }
306:
307: Object addr;
308: String calledName;
309:
310: /**
311: * Create a <tt>UniAddress</tt> by wrapping an <tt>InetAddress</tt> or
312: * <tt>NbtAddress</tt>.
313: */
314:
315: public UniAddress(Object addr) {
316: if (addr == null) {
317: throw new IllegalArgumentException();
318: }
319: this .addr = addr;
320: }
321:
322: /**
323: * Return the IP address of this address as a 32 bit integer.
324: */
325:
326: public int hashCode() {
327: return addr.hashCode();
328: }
329:
330: /**
331: * Compare two addresses for equality. Two <tt>UniAddress</tt>s are equal
332: * if they are both <tt>UniAddress' and refer to the same IP address.
333: */
334:
335: public boolean equals(Object obj) {
336: return obj instanceof UniAddress
337: && addr.hashCode() == obj.hashCode();
338: }
339:
340: /**
341: * Guess first called name to try for session establishment. This
342: * method is used exclusively by the <tt>jcifs.smb</tt> package.
343: */
344:
345: public String firstCalledName() {
346: if (addr instanceof NbtAddress) {
347: return ((NbtAddress) addr).firstCalledName();
348: } else {
349: calledName = ((InetAddress) addr).getHostName();
350: if (isDotQuadIP(calledName)) {
351: calledName = NbtAddress.SMBSERVER_NAME;
352: } else {
353: int i = calledName.indexOf('.');
354: if (i > 1 && i < 15) {
355: calledName = calledName.substring(0, i)
356: .toUpperCase();
357: } else if (calledName.length() > 15) {
358: calledName = NbtAddress.SMBSERVER_NAME;
359: } else {
360: calledName = calledName.toUpperCase();
361: }
362: }
363: }
364:
365: return calledName;
366: }
367:
368: /**
369: * Guess next called name to try for session establishment. This
370: * method is used exclusively by the <tt>jcifs.smb</tt> package.
371: */
372:
373: public String nextCalledName() {
374: if (addr instanceof NbtAddress) {
375: return ((NbtAddress) addr).nextCalledName();
376: } else if (calledName != NbtAddress.SMBSERVER_NAME) {
377: calledName = NbtAddress.SMBSERVER_NAME;
378: return calledName;
379: }
380: return null;
381: }
382:
383: /**
384: * Return the underlying <tt>NbtAddress</tt> or <tt>InetAddress</tt>.
385: */
386:
387: public Object getAddress() {
388: return addr;
389: }
390:
391: /**
392: * Return the hostname of this address such as "MYCOMPUTER".
393: */
394:
395: public String getHostName() {
396: if (addr instanceof NbtAddress) {
397: return ((NbtAddress) addr).getHostName();
398: }
399: return ((InetAddress) addr).getHostName();
400: }
401:
402: /**
403: * Return the IP address as text such as "192.168.1.15".
404: */
405:
406: public String getHostAddress() {
407: if (addr instanceof NbtAddress) {
408: return ((NbtAddress) addr).getHostAddress();
409: }
410: return ((InetAddress) addr).getHostAddress();
411: }
412:
413: /**
414: * Return the a text representation of this address such as
415: * <tt>MYCOMPUTER/192.168.1.15</tt>.
416: */
417: public String toString() {
418: return addr.toString();
419: }
420: }
|