/* jcifs smb client library in Java * Copyright (C) 2000 "Michael B. Allen" <jcifs at samba dot org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package com.knowgate.jcifs; import java.net.InetAddress; import java.net.UnknownHostException; import java.io.IOException; import java.util.StringTokenizer; import com.knowgate.jcifs.netbios.NbtAddress; import com.knowgate.jcifs.netbios.Lmhosts; import com.knowgate.debug.*; /** * <p>Under normal conditions it is not necessary to use * this class to use jCIFS properly. Name resolusion is * handled internally to the <code>jcifs.smb</code> package. * <p> * This class is a wrapper for both {@link jcifs.netbios.NbtAddress} * and {@link java.net.InetAddress}. The name resolution mechanisms * used will systematically query all available configured resolution * services including WINS, broadcasts, DNS, and LMHOSTS. See * <a href="../../resolver.html">Setting Name Resolution Properties</a> * and the <code>jcifs.resolveOrder</code> property. Changing * jCIFS name resolution properties can greatly affect the behavior of * the client and may be necessary for proper operation. * <p> * This class should be used in favor of <tt>InetAddress</tt> to resolve * hostnames on LANs and WANs that support a mixture of NetBIOS/WINS and * DNS resolvable hosts. */ public class UniAddress { private static final int RESOLVER_WINS = 0; private static final int RESOLVER_BCAST = 1; private static final int RESOLVER_DNS = 2; private static final int RESOLVER_LMHOSTS = 3; private static int[] resolveOrder; private static InetAddress baddr; static { String ro = Config.getProperty( "jcifs.resolveOrder" ); InetAddress nbns = NbtAddress.getWINSAddress(); try { baddr = Config.getInetAddress( "jcifs.netbios.baddr", InetAddress.getByName( "255.255.255.255" )); } catch( UnknownHostException uhe ) { } if( ro == null || ro.length() == 0 ) { /* No resolveOrder has been specified, use the * default which is LMHOSTS,WINS,BCAST,DNS or just * LMHOSTS,BCAST,DNS if jcifs.netbios.wins has not * been specified. */ if( nbns == null ) { resolveOrder = new int[3]; resolveOrder[0] = RESOLVER_LMHOSTS; resolveOrder[1] = RESOLVER_BCAST; resolveOrder[2] = RESOLVER_DNS; } else { resolveOrder = new int[4]; resolveOrder[0] = RESOLVER_LMHOSTS; resolveOrder[1] = RESOLVER_WINS; resolveOrder[2] = RESOLVER_BCAST; resolveOrder[3] = RESOLVER_DNS; } } else { int[] tmp = new int[4]; StringTokenizer st = new StringTokenizer( ro, "," ); int i = 0; while( st.hasMoreTokens() ) { String s = st.nextToken().trim(); if( s.equalsIgnoreCase( "LMHOSTS" )) { tmp[i++] = RESOLVER_LMHOSTS; } else if( s.equalsIgnoreCase( "WINS" )) { if( nbns == null ) { if( DebugFile.trace ) { DebugFile.writeln( "UniAddress resolveOrder specifies WINS however the " + "jcifs.netbios.wins property has not been set" ); } continue; } tmp[i++] = RESOLVER_WINS; } else if( s.equalsIgnoreCase( "BCAST" )) { tmp[i++] = RESOLVER_BCAST; } else if( s.equalsIgnoreCase( "DNS" )) { tmp[i++] = RESOLVER_DNS; } else if( DebugFile.trace ) { DebugFile.writeln( "unknown resolver method: " + s ); } } resolveOrder = new int[i]; System.arraycopy( tmp, 0, resolveOrder, 0, i ); } } static class Sem { Sem( int count ) { this.count = count; } int count; } static class QueryThread extends Thread { Sem sem; String host, scope; int type; NbtAddress ans = null; InetAddress svr; UnknownHostException uhe; QueryThread( Sem sem, String host, int type, String scope, InetAddress svr ) { super( "JCIFS-QueryThread: " + host ); this.sem = sem; this.host = host; this.type = type; this.scope = scope; this.svr = svr; } public void run() { try { ans = NbtAddress.getByName( host, type, scope, svr ); } catch( UnknownHostException uhe ) { this.uhe = uhe; } catch( Exception ex ) { this.uhe = new UnknownHostException( ex.getMessage() ); } finally { synchronized( sem ) { sem.count--; sem.notify(); } } } } static NbtAddress lookupServerOrWorkgroup( String name, InetAddress svr ) throws UnknownHostException { Sem sem = new Sem( 2 ); int type = NbtAddress.isWINS( svr ) ? 0x1b : 0x1d; QueryThread q1x = new QueryThread( sem, name, type, null, svr ); QueryThread q20 = new QueryThread( sem, name, 0x20, null, svr ); q1x.setDaemon( true ); q20.setDaemon( true ); try { synchronized( sem ) { q1x.start(); q20.start(); while( sem.count > 0 && q1x.ans == null && q20.ans == null ) { sem.wait(); } } } catch( InterruptedException ie ) { throw new UnknownHostException( name ); } if( q1x.ans != null ) { return q1x.ans; } else if( q20.ans != null ) { return q20.ans; } else { throw q1x.uhe; } } /** * Determines the address of a host given it's host name. The name can be a * machine name like "jcifs.samba.org", or an IP address like "192.168.1.15". * * @param host NetBIOS or DNS hostname to resolve * @throws java.net.UnknownHostException if there is an error resolving the name */ public static UniAddress getByName( String hostname ) throws UnknownHostException { return getByName( hostname, false ); } static boolean isDotQuadIP( String hostname ) { if( Character.isDigit( hostname.charAt( 0 ))) { int i, len, dots; char[] data; i = dots = 0; /* quick IP address validation */ len = hostname.length(); data = hostname.toCharArray(); while( i < len && Character.isDigit( data[i++] )) { if( i == len && dots == 3 ) { // probably an IP address return true; } if( i < len && data[i] == '.' ) { dots++; i++; } } } return false; } static boolean isValidDnsName( String hostname ) { // Simple for now. Just handles "1" --> 1/0.0.0.1 kind of stuff return Character.isDigit( hostname.charAt( 0 )) == false; } /** * Lookup <tt>hostname</tt> and return it's <tt>UniAddress</tt>. If the * <tt>possibleNTDomainOrWorkgroup</tt> parameter is <tt>true</tt> an * addtional name query will be performed to locate a master browser. */ public static UniAddress getByName( String hostname, boolean possibleNTDomainOrWorkgroup ) throws UnknownHostException { Object addr; int i; if( hostname == null || hostname.length() == 0 ) { throw new UnknownHostException(); } if( isDotQuadIP( hostname )) { return new UniAddress( NbtAddress.getByName( hostname )); } for( i = 0; i < resolveOrder.length; i++ ) { try { switch( resolveOrder[i] ) { case RESOLVER_LMHOSTS: if(( addr = Lmhosts.getByName( hostname )) == null ) { continue; } break; case RESOLVER_WINS: if( hostname == NbtAddress.MASTER_BROWSER_NAME || hostname.length() > 15 ) { // invalid netbios name continue; } if( possibleNTDomainOrWorkgroup ) { addr = lookupServerOrWorkgroup( hostname, NbtAddress.getWINSAddress() ); } else { addr = NbtAddress.getByName( hostname, 0x20, null, NbtAddress.getWINSAddress() ); } break; case RESOLVER_BCAST: if( hostname.length() > 15 ) { // invalid netbios name continue; } if( possibleNTDomainOrWorkgroup ) { addr = lookupServerOrWorkgroup( hostname, baddr ); } else { addr = NbtAddress.getByName( hostname, 0x20, null, baddr ); } break; case RESOLVER_DNS: if( isValidDnsName( hostname ) == false ) { throw new UnknownHostException( hostname ); } addr = InetAddress.getByName( hostname ); break; default: throw new UnknownHostException( hostname ); } return new UniAddress( addr ); // Success } catch( IOException ioe ) { // Failure } } throw new UnknownHostException( hostname ); } Object addr; String calledName; /** * Create a <tt>UniAddress</tt> by wrapping an <tt>InetAddress</tt> or * <tt>NbtAddress</tt>. */ public UniAddress( Object addr ) { if( addr == null ) { throw new IllegalArgumentException(); } this.addr = addr; } /** * Return the IP address of this address as a 32 bit integer. */ public int hashCode() { return addr.hashCode(); } /** * Compare two addresses for equality. Two <tt>UniAddress</tt>s are equal * if they are both <tt>UniAddress' and refer to the same IP address. */ public boolean equals( Object obj ) { return obj instanceof UniAddress && addr.hashCode() == obj.hashCode(); } /** * Guess first called name to try for session establishment. This * method is used exclusively by the <tt>jcifs.smb</tt> package. */ public String firstCalledName() { if( addr instanceof NbtAddress ) { return ((NbtAddress)addr).firstCalledName(); } else { calledName = ((InetAddress)addr).getHostName(); if( isDotQuadIP( calledName )) { calledName = NbtAddress.SMBSERVER_NAME; } else { int i = calledName.indexOf( '.' ); if( i > 1 && i < 15 ) { calledName = calledName.substring( 0, i ).toUpperCase(); } else if( calledName.length() > 15 ) { calledName = NbtAddress.SMBSERVER_NAME; } else { calledName = calledName.toUpperCase(); } } } return calledName; } /** * Guess next called name to try for session establishment. This * method is used exclusively by the <tt>jcifs.smb</tt> package. */ public String nextCalledName() { if( addr instanceof NbtAddress ) { return ((NbtAddress)addr).nextCalledName(); } else if( calledName != NbtAddress.SMBSERVER_NAME ) { calledName = NbtAddress.SMBSERVER_NAME; return calledName; } return null; } /** * Return the underlying <tt>NbtAddress</tt> or <tt>InetAddress</tt>. */ public Object getAddress() { return addr; } /** * Return the hostname of this address such as "MYCOMPUTER". */ public String getHostName() { if( addr instanceof NbtAddress ) { return ((NbtAddress)addr).getHostName(); } return ((InetAddress)addr).getHostName(); } /** * Return the IP address as text such as "192.168.1.15". */ public String getHostAddress() { if( addr instanceof NbtAddress ) { return ((NbtAddress)addr).getHostAddress(); } return ((InetAddress)addr).getHostAddress(); } /** * Return the a text representation of this address such as * <tt>MYCOMPUTER/192.168.1.15</tt>. */ public String toString() { return addr.toString(); } }