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