mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-06-02 17:35:18 +00:00
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2005 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
1938 lines
52 KiB
Java
1938 lines
52 KiB
Java
/*
|
|
* Copyright (C) 2005 Alfresco, Inc.
|
|
*
|
|
* Licensed under the Mozilla Public License version 1.1
|
|
* with a permitted attribution clause. You may obtain a
|
|
* copy of the License at
|
|
*
|
|
* http://www.alfresco.org/legal/license.txt
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
|
* either express or implied. See the License for the specific
|
|
* language governing permissions and limitations under the
|
|
* License.
|
|
*/
|
|
package org.alfresco.filesys.netbios;
|
|
|
|
import java.io.DataInputStream;
|
|
import java.io.DataOutputStream;
|
|
import java.io.IOException;
|
|
import java.net.DatagramPacket;
|
|
import java.net.DatagramSocket;
|
|
import java.net.InetAddress;
|
|
import java.net.Socket;
|
|
import java.net.SocketException;
|
|
import java.net.UnknownHostException;
|
|
import java.util.Vector;
|
|
|
|
import org.alfresco.filesys.smb.NetworkSession;
|
|
import org.alfresco.filesys.util.DataPacker;
|
|
import org.alfresco.filesys.util.HexDump;
|
|
import org.alfresco.filesys.util.StringList;
|
|
import org.apache.commons.logging.Log;
|
|
import org.apache.commons.logging.LogFactory;
|
|
|
|
/**
|
|
* NetBIOS session class.
|
|
*/
|
|
public final class NetBIOSSession implements NetworkSession
|
|
{
|
|
private static final Log logger = LogFactory.getLog("org.alfresco.smb.protocol.netbios");
|
|
|
|
// Constants
|
|
//
|
|
// Caller name template
|
|
|
|
public static final int MaxCallerNameTemplateLength = 8;
|
|
public static final char SessionIdChar = '#';
|
|
public static final char JVMIdChar = '@';
|
|
public static final String ValidTemplateChars = "@#_";
|
|
|
|
// Default find name buffer size
|
|
|
|
private static final int FindNameBufferSize = 2048;
|
|
|
|
// Default socket timeout, in milliseconds
|
|
|
|
private static int _defTimeout = RFCNetBIOSProtocol.TMO;
|
|
|
|
// Remote socket to connect to, default is 139.
|
|
|
|
private int m_remotePort;
|
|
|
|
// Socket used to connect and read/write to remote host
|
|
|
|
private Socket m_nbSocket;
|
|
|
|
// Input and output data streams, from the socket network connection
|
|
|
|
private DataInputStream m_nbIn;
|
|
private DataOutputStream m_nbOut;
|
|
|
|
// Send/receive timeout, in milliseconds
|
|
|
|
private int m_tmo = _defTimeout;
|
|
|
|
// Local and remote name types
|
|
|
|
private char m_locNameType = NetBIOSName.FileServer;
|
|
private char m_remNameType = NetBIOSName.FileServer;
|
|
|
|
// Unique session identifier, used to generate a unique caller name when opening a new session
|
|
|
|
private static int m_sessIdx = 0;
|
|
|
|
// Unique JVM id, used to generate a unique caller name when multiple JVMs may be running on the
|
|
// same
|
|
// host
|
|
|
|
private static int m_jvmIdx = 0;
|
|
|
|
// Caller name template string. The template is used to create a unique caller name when opening
|
|
// a new session.
|
|
// The template is appended to the local host name, which may be truncated to allow room for the
|
|
// template to be
|
|
// appended and still be within the 16 character NetBIOS name limit.
|
|
//
|
|
// The caller name generation replaces '#' characters with a zero padded session index as a hex
|
|
// value and '@'
|
|
// characters with a zero padded JVM index. Multiple '#' and/or '@' characters can be specified
|
|
// to indicate the
|
|
// field width. Any other characters in the template are passed through to the final caller name
|
|
// string.
|
|
//
|
|
// The maximum template string length is 8 characters to allow for at least 8 characters from
|
|
// the host name.
|
|
|
|
private static String m_callerTemplate = "_##";
|
|
|
|
// Truncated host name, caller name generation appends the caller template result to this string
|
|
|
|
private static String m_localNamePart;
|
|
|
|
// Transaction identifier, used for datagrams
|
|
|
|
private static short m_tranIdx = 1;
|
|
|
|
// RFC NetBIOS name service datagram socket
|
|
|
|
private static DatagramSocket m_dgramSock = null;
|
|
|
|
// Debug enable flag
|
|
|
|
private static boolean m_debug = false;
|
|
|
|
// Subnet mask, required for broadcast name lookup requests
|
|
|
|
private static String m_subnetMask = null;
|
|
|
|
// WINS server address
|
|
|
|
private static InetAddress m_winsServer;
|
|
|
|
// Name lookup types
|
|
|
|
public static final int DNSOnly = 1;
|
|
public static final int WINSOnly = 2;
|
|
public static final int WINSAndDNS = 3;
|
|
|
|
// Flag to control whether name lookups use WINS/NetBIOS lookup or DNS
|
|
|
|
private static int m_lookupType = WINSAndDNS;
|
|
|
|
// NetBIOS name lookup timeout value.
|
|
|
|
private static int m_lookupTmo = 500;
|
|
|
|
// Flag to control use of the '*SMBSERVER' name when connecting to a file server
|
|
|
|
private static boolean m_useWildcardFileServer = true;
|
|
|
|
/**
|
|
* NetBIOS session class constructor. Create a NetBIOS session with the default socket number
|
|
* and no current network connection.
|
|
*/
|
|
public NetBIOSSession()
|
|
{
|
|
m_remotePort = RFCNetBIOSProtocol.PORT;
|
|
m_nbSocket = null;
|
|
}
|
|
|
|
/**
|
|
* NetBIOS session class constructor
|
|
*
|
|
* @param tmo Send/receive timeout value in milliseconds
|
|
*/
|
|
public NetBIOSSession(int tmo)
|
|
{
|
|
m_tmo = tmo;
|
|
m_remotePort = RFCNetBIOSProtocol.PORT;
|
|
m_nbSocket = null;
|
|
}
|
|
|
|
/**
|
|
* NetBIOS session class constructor
|
|
*
|
|
* @param tmo Send/receive timeout value in milliseconds
|
|
* @param port Remote port to connect to
|
|
*/
|
|
public NetBIOSSession(int tmo, int port)
|
|
{
|
|
m_tmo = tmo;
|
|
m_remotePort = port;
|
|
m_nbSocket = null;
|
|
}
|
|
|
|
/**
|
|
* Return the protocol name
|
|
*
|
|
* @return String
|
|
*/
|
|
public final String getProtocolName()
|
|
{
|
|
return "TCP/IP NetBIOS";
|
|
}
|
|
|
|
/**
|
|
* Determine if the session is connected to a remote host
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public final boolean isConnected()
|
|
{
|
|
|
|
// Check if the socket is valid
|
|
|
|
if (m_nbSocket == null)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Check if there is data available on this network session
|
|
*
|
|
* @return boolean
|
|
* @exception IOException
|
|
*/
|
|
public final boolean hasData() throws IOException
|
|
{
|
|
|
|
// Check if the connection is active
|
|
|
|
if (m_nbSocket == null || m_nbIn == null)
|
|
return false;
|
|
|
|
// Check if there is data available
|
|
|
|
return m_nbIn.available() > 0 ? true : false;
|
|
}
|
|
|
|
/**
|
|
* Convert a host name string into RFC NetBIOS format.
|
|
*
|
|
* @param hostName Host name to be converted.
|
|
* @return Converted host name string.
|
|
*/
|
|
public static String ConvertName(String hostName)
|
|
{
|
|
return ConvertName(hostName, NetBIOSName.FileServer);
|
|
}
|
|
|
|
/**
|
|
* Convert a host name string into RFC NetBIOS format.
|
|
*
|
|
* @param hostName Host name to be converted.
|
|
* @param nameType NetBIOS name type, added as the 16th byte of the name before conversion.
|
|
* @return Converted host name string.
|
|
*/
|
|
public static String ConvertName(String hostName, char nameType)
|
|
{
|
|
|
|
// Build the name string with the name type, make sure that the host
|
|
// name is uppercase.
|
|
|
|
StringBuffer hName = new StringBuffer(hostName.toUpperCase());
|
|
|
|
if (hName.length() > 15)
|
|
hName.setLength(15);
|
|
|
|
// Space pad the name then add the NetBIOS name type
|
|
|
|
while (hName.length() < 15)
|
|
hName.append(' ');
|
|
hName.append(nameType);
|
|
|
|
// Convert the NetBIOS name string to the RFC NetBIOS name format
|
|
|
|
String convstr = new String("ABCDEFGHIJKLMNOP");
|
|
StringBuffer nameBuf = new StringBuffer(32);
|
|
|
|
int idx = 0;
|
|
|
|
while (idx < hName.length())
|
|
{
|
|
|
|
// Get the current character from the host name string
|
|
|
|
char ch = hName.charAt(idx++);
|
|
|
|
if (ch == ' ')
|
|
{
|
|
|
|
// Append an encoded <SPACE> character
|
|
|
|
nameBuf.append("CA");
|
|
}
|
|
else
|
|
{
|
|
|
|
// Append octet for the current character
|
|
|
|
nameBuf.append(convstr.charAt((int) ch / 16));
|
|
nameBuf.append(convstr.charAt((int) ch % 16));
|
|
}
|
|
|
|
} // end while
|
|
|
|
// Return the encoded string
|
|
|
|
return nameBuf.toString();
|
|
}
|
|
|
|
/**
|
|
* Convert an encoded NetBIOS name to a normal name string
|
|
*
|
|
* @param buf Buffer that contains the NetBIOS encoded name
|
|
* @param off Offset that the name starts within the buffer
|
|
* @return Normal NetBIOS name string
|
|
*/
|
|
public static String DecodeName(byte[] buf, int off)
|
|
{
|
|
|
|
// Convert the RFC NetBIOS name string to a normal NetBIOS name string
|
|
|
|
String convstr = new String("ABCDEFGHIJKLMNOP");
|
|
StringBuffer nameBuf = new StringBuffer(16);
|
|
|
|
int idx = 0;
|
|
char ch1, ch2;
|
|
|
|
while (idx < 32)
|
|
{
|
|
|
|
// Get the current encoded character pair from the encoded name string
|
|
|
|
ch1 = (char) buf[off + idx];
|
|
ch2 = (char) buf[off + idx + 1];
|
|
|
|
if (ch1 == 'C' && ch2 == 'A')
|
|
{
|
|
|
|
// Append a <SPACE> character
|
|
|
|
nameBuf.append(' ');
|
|
}
|
|
else
|
|
{
|
|
|
|
// Convert back to a character code
|
|
|
|
int val = convstr.indexOf(ch1) << 4;
|
|
val += convstr.indexOf(ch2);
|
|
|
|
// Append the current character to the decoded name
|
|
|
|
nameBuf.append((char) (val & 0xFF));
|
|
}
|
|
|
|
// Update the encoded string index
|
|
|
|
idx += 2;
|
|
|
|
} // end while
|
|
|
|
// Return the decoded string
|
|
|
|
return nameBuf.toString();
|
|
}
|
|
|
|
/**
|
|
* Convert an encoded NetBIOS name to a normal name string
|
|
*
|
|
* @param encnam RFC NetBIOS encoded name
|
|
* @return Normal NetBIOS name string
|
|
*/
|
|
|
|
public static String DecodeName(String encnam)
|
|
{
|
|
|
|
// Check if the encoded name string is valid, must be 32 characters
|
|
|
|
if (encnam == null || encnam.length() != 32)
|
|
return "";
|
|
|
|
// Convert the RFC NetBIOS name string to a normal NetBIOS name string
|
|
|
|
String convstr = new String("ABCDEFGHIJKLMNOP");
|
|
StringBuffer nameBuf = new StringBuffer(16);
|
|
|
|
int idx = 0;
|
|
char ch1, ch2;
|
|
|
|
while (idx < 32)
|
|
{
|
|
|
|
// Get the current encoded character pair from the encoded name string
|
|
|
|
ch1 = encnam.charAt(idx);
|
|
ch2 = encnam.charAt(idx + 1);
|
|
|
|
if (ch1 == 'C' && ch2 == 'A')
|
|
{
|
|
|
|
// Append a <SPACE> character
|
|
|
|
nameBuf.append(' ');
|
|
}
|
|
else
|
|
{
|
|
|
|
// Convert back to a character code
|
|
|
|
int val = convstr.indexOf(ch1) << 4;
|
|
val += convstr.indexOf(ch2);
|
|
|
|
// Append the current character to the decoded name
|
|
|
|
nameBuf.append((char) (val & 0xFF));
|
|
}
|
|
|
|
// Update the encoded string index
|
|
|
|
idx += 2;
|
|
|
|
} // end while
|
|
|
|
// Return the decoded string
|
|
|
|
return nameBuf.toString();
|
|
}
|
|
|
|
/**
|
|
* Convert a host name string into RFC NetBIOS format.
|
|
*
|
|
* @param hostName Host name to be converted.
|
|
* @param nameType NetBIOS name type, added as the 16th byte of the name before conversion.
|
|
* @param buf Buffer to write the encoded name into.
|
|
* @param off Offset within the buffer to start writing.
|
|
* @return Buffer position
|
|
*/
|
|
public static int EncodeName(String hostName, char nameType, byte[] buf, int off)
|
|
{
|
|
|
|
// Build the name string with the name type, make sure that the host
|
|
// name is uppercase.
|
|
|
|
StringBuffer hName = new StringBuffer(hostName.toUpperCase());
|
|
|
|
if (hName.length() > 15)
|
|
hName.setLength(15);
|
|
|
|
// Space pad the name then add the NetBIOS name type
|
|
|
|
while (hName.length() < 15)
|
|
hName.append(' ');
|
|
hName.append(nameType);
|
|
|
|
// Convert the NetBIOS name string to the RFC NetBIOS name format
|
|
|
|
String convstr = new String("ABCDEFGHIJKLMNOP");
|
|
int idx = 0;
|
|
int bufpos = off;
|
|
|
|
// Set the name length byte
|
|
|
|
buf[bufpos++] = 0x20;
|
|
|
|
// Copy the encoded NetBIOS name to the buffer
|
|
|
|
while (idx < hName.length())
|
|
{
|
|
|
|
// Get the current character from the host name string
|
|
|
|
char ch = hName.charAt(idx++);
|
|
|
|
if (ch == ' ')
|
|
{
|
|
|
|
// Append an encoded <SPACE> character
|
|
|
|
buf[bufpos++] = (byte) 'C';
|
|
buf[bufpos++] = (byte) 'A';
|
|
}
|
|
else
|
|
{
|
|
|
|
// Append octet for the current character
|
|
|
|
buf[bufpos++] = (byte) convstr.charAt((int) ch / 16);
|
|
buf[bufpos++] = (byte) convstr.charAt((int) ch % 16);
|
|
}
|
|
|
|
} // end while
|
|
|
|
// Null terminate the string
|
|
|
|
buf[bufpos++] = 0;
|
|
return bufpos;
|
|
}
|
|
|
|
/**
|
|
* Find a NetBIOS name on the network
|
|
*
|
|
* @param nbname NetBIOS name to search for, not yet RFC encoded
|
|
* @param nbType Name type, appended as the 16th byte of the name
|
|
* @param tmo Timeout value for receiving incoming datagrams
|
|
* @return NetBIOS name details
|
|
* @exception java.io.IOException If an I/O error occurs
|
|
*/
|
|
public static NetBIOSName FindName(String nbName, char nbType, int tmo) throws java.io.IOException
|
|
{
|
|
|
|
// Call the main FindName method
|
|
|
|
return FindName(new NetBIOSName(nbName, nbType, false), tmo);
|
|
}
|
|
|
|
/**
|
|
* Find a NetBIOS name on the network
|
|
*
|
|
* @param nbname NetBIOS name to search for
|
|
* @param tmo Timeout value for receiving incoming datagrams
|
|
* @return NetBIOS name details
|
|
* @exception java.io.IOException If an I/O error occurs
|
|
*/
|
|
public static NetBIOSName FindName(NetBIOSName nbName, int tmo) throws java.io.IOException
|
|
{
|
|
|
|
// Get the local address details
|
|
|
|
InetAddress locAddr = InetAddress.getLocalHost();
|
|
|
|
// Create a datagram socket
|
|
|
|
if (m_dgramSock == null)
|
|
{
|
|
|
|
// Create a datagram socket
|
|
|
|
m_dgramSock = new DatagramSocket();
|
|
}
|
|
|
|
// Set the datagram socket timeout, in milliseconds
|
|
|
|
m_dgramSock.setSoTimeout(tmo);
|
|
|
|
// Create a name lookup NetBIOS packet
|
|
|
|
NetBIOSPacket nbpkt = new NetBIOSPacket();
|
|
nbpkt.buildNameQueryRequest(nbName, m_tranIdx++);
|
|
|
|
// Get the local host numeric address
|
|
|
|
String locIP = locAddr.getHostAddress();
|
|
int dotIdx = locIP.indexOf('.');
|
|
if (dotIdx == -1)
|
|
return null;
|
|
|
|
// If a WINS server has been configured the request is sent directly to the WINS server, if
|
|
// not then a broadcast is done on the local subnet.
|
|
|
|
InetAddress destAddr = null;
|
|
|
|
if (hasWINSServer() == false)
|
|
{
|
|
|
|
// Check if the subnet mask has been set, if not then generate a subnet mask
|
|
|
|
if (getSubnetMask() == null)
|
|
GenerateSubnetMask(null);
|
|
|
|
// Build a broadcast destination address
|
|
|
|
destAddr = InetAddress.getByName(getSubnetMask());
|
|
}
|
|
else
|
|
{
|
|
|
|
// Use the WINS server address
|
|
|
|
destAddr = getWINSServer();
|
|
}
|
|
|
|
// Build the name lookup request
|
|
|
|
DatagramPacket dgram = new DatagramPacket(nbpkt.getBuffer(), nbpkt.getLength(), destAddr,
|
|
RFCNetBIOSProtocol.NAME_PORT);
|
|
|
|
// Allocate a receive datagram packet
|
|
|
|
byte[] rxbuf = new byte[FindNameBufferSize];
|
|
DatagramPacket rxdgram = new DatagramPacket(rxbuf, rxbuf.length);
|
|
|
|
// Create a NetBIOS packet using the receive buffer
|
|
|
|
NetBIOSPacket rxpkt = new NetBIOSPacket(rxbuf);
|
|
|
|
// DEBUG
|
|
|
|
if (m_debug)
|
|
nbpkt.DumpPacket(false);
|
|
|
|
// Send the find name datagram
|
|
|
|
m_dgramSock.send(dgram);
|
|
|
|
// Receive a reply datagram
|
|
|
|
boolean rxOK = false;
|
|
|
|
do
|
|
{
|
|
|
|
// Receive a datagram packet
|
|
|
|
m_dgramSock.receive(rxdgram);
|
|
|
|
// DEBUG
|
|
|
|
if (logger.isDebugEnabled() && m_debug)
|
|
{
|
|
logger.debug("NetBIOS: Rx Datagram");
|
|
rxpkt.DumpPacket(false);
|
|
}
|
|
|
|
// Check if this is a valid response datagram
|
|
|
|
if (rxpkt.isResponse() && rxpkt.getOpcode() == NetBIOSPacket.RESP_QUERY)
|
|
rxOK = true;
|
|
|
|
} while (!rxOK);
|
|
|
|
// Get the list of names from the response, should only be one name
|
|
|
|
NetBIOSNameList nameList = rxpkt.getAnswerNameList();
|
|
if (nameList != null && nameList.numberOfNames() > 0)
|
|
return nameList.getName(0);
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Build a list of nodes that own the specified NetBIOS name.
|
|
*
|
|
* @param nbname NetBIOS name to search for, not yet RFC encoded
|
|
* @param nbType Name type, appended as the 16th byte of the name
|
|
* @param tmo Timeout value for receiving incoming datagrams
|
|
* @return List of node name Strings
|
|
* @exception java.io.IOException If an I/O error occurs
|
|
*/
|
|
public static StringList FindNameList(String nbName, char nbType, int tmo) throws IOException
|
|
{
|
|
|
|
// Get the local address details
|
|
|
|
InetAddress locAddr = InetAddress.getLocalHost();
|
|
|
|
// Create a datagram socket
|
|
|
|
if (m_dgramSock == null)
|
|
{
|
|
|
|
// Create a datagram socket
|
|
|
|
m_dgramSock = new DatagramSocket();
|
|
}
|
|
|
|
// Set the datagram socket timeout, in milliseconds
|
|
|
|
m_dgramSock.setSoTimeout(tmo);
|
|
|
|
// Create a name lookup NetBIOS packet
|
|
|
|
NetBIOSPacket nbpkt = new NetBIOSPacket();
|
|
|
|
nbpkt.setTransactionId(m_tranIdx++);
|
|
nbpkt.setOpcode(NetBIOSPacket.NAME_QUERY);
|
|
nbpkt.setFlags(NetBIOSPacket.FLG_BROADCAST);
|
|
nbpkt.setQuestionCount(1);
|
|
nbpkt.setQuestionName(nbName, nbType, NetBIOSPacket.NAME_TYPE_NB, NetBIOSPacket.NAME_CLASS_IN);
|
|
|
|
// Get the local host numeric address
|
|
|
|
String locIP = locAddr.getHostAddress();
|
|
int dotIdx = locIP.indexOf('.');
|
|
if (dotIdx == -1)
|
|
return null;
|
|
|
|
// If a WINS server has been configured the request is sent directly to the WINS server, if
|
|
// not then a broadcast is done on the local subnet.
|
|
|
|
InetAddress destAddr = null;
|
|
|
|
if (hasWINSServer() == false)
|
|
{
|
|
|
|
// Check if the subnet mask has been set, if not then generate a subnet mask
|
|
|
|
if (getSubnetMask() == null)
|
|
GenerateSubnetMask(null);
|
|
|
|
// Build a broadcast destination address
|
|
|
|
destAddr = InetAddress.getByName(getSubnetMask());
|
|
}
|
|
else
|
|
{
|
|
|
|
// Use the WINS server address
|
|
|
|
destAddr = getWINSServer();
|
|
}
|
|
|
|
// Build the request datagram
|
|
|
|
DatagramPacket dgram = new DatagramPacket(nbpkt.getBuffer(), nbpkt.getLength(), destAddr,
|
|
RFCNetBIOSProtocol.NAME_PORT);
|
|
|
|
// Allocate a receive datagram packet
|
|
|
|
byte[] rxbuf = new byte[FindNameBufferSize];
|
|
DatagramPacket rxdgram = new DatagramPacket(rxbuf, rxbuf.length);
|
|
|
|
// Create a NetBIOS packet using the receive buffer
|
|
|
|
NetBIOSPacket rxpkt = new NetBIOSPacket(rxbuf);
|
|
|
|
// DEBUG
|
|
|
|
if (m_debug)
|
|
nbpkt.DumpPacket(false);
|
|
|
|
// Create a vector to store the remote host addresses
|
|
|
|
Vector<InetAddress> addrList = new Vector<InetAddress>();
|
|
|
|
// Calculate the end time, to stop receiving datagrams
|
|
|
|
long endTime = System.currentTimeMillis() + tmo;
|
|
|
|
// Send the find name datagram
|
|
|
|
m_dgramSock.send(dgram);
|
|
|
|
// Receive reply datagrams
|
|
|
|
do
|
|
{
|
|
|
|
// Receive a datagram packet
|
|
|
|
try
|
|
{
|
|
m_dgramSock.receive(rxdgram);
|
|
|
|
// DEBUG
|
|
|
|
if (logger.isDebugEnabled() && m_debug)
|
|
{
|
|
logger.debug("NetBIOS: Rx Datagram");
|
|
rxpkt.DumpPacket(false);
|
|
}
|
|
|
|
// Check if this is a valid response datagram
|
|
|
|
if (rxpkt.isResponse() && rxpkt.getOpcode() == NetBIOSPacket.RESP_QUERY)
|
|
{
|
|
|
|
// Get the address of the remote host for this datagram and add it to the list
|
|
// of responders
|
|
|
|
addrList.add(rxdgram.getAddress());
|
|
}
|
|
}
|
|
catch (java.io.IOException ex)
|
|
{
|
|
|
|
// DEBUG
|
|
|
|
if (logger.isDebugEnabled() && m_debug)
|
|
logger.debug(ex.toString());
|
|
}
|
|
|
|
} while (System.currentTimeMillis() < endTime);
|
|
|
|
// Check if we received any replies
|
|
|
|
if (addrList.size() == 0)
|
|
return null;
|
|
|
|
// Create a node name list
|
|
|
|
StringList nameList = new StringList();
|
|
|
|
// Convert the reply addresses to node names
|
|
|
|
for (int i = 0; i < addrList.size(); i++)
|
|
{
|
|
|
|
// Get the current address from the list
|
|
|
|
InetAddress addr = addrList.elementAt(i);
|
|
|
|
// Convert the address to a node name string
|
|
|
|
String name = NetBIOSName(addr.getHostName());
|
|
|
|
// Check if the name is already in the name list
|
|
|
|
if (!nameList.containsString(name))
|
|
nameList.addString(name);
|
|
}
|
|
|
|
// Return the node name list
|
|
|
|
return nameList;
|
|
}
|
|
|
|
/**
|
|
* Get the NetBIOS name list for the specified IP address
|
|
*
|
|
* @param ipAddr String
|
|
* @return NetBIOSNameList
|
|
*/
|
|
public static NetBIOSNameList FindNamesForAddress(String ipAddr) throws UnknownHostException, SocketException
|
|
{
|
|
|
|
// Create a datagram socket
|
|
|
|
if (m_dgramSock == null)
|
|
{
|
|
|
|
// Create a datagram socket
|
|
|
|
m_dgramSock = new DatagramSocket();
|
|
}
|
|
|
|
// Set the datagram socket timeout, in milliseconds
|
|
|
|
m_dgramSock.setSoTimeout(2000);
|
|
|
|
// Create a name lookup NetBIOS packet
|
|
|
|
NetBIOSPacket nbpkt = new NetBIOSPacket();
|
|
|
|
nbpkt.setTransactionId(m_tranIdx++);
|
|
nbpkt.setOpcode(NetBIOSPacket.NAME_QUERY);
|
|
nbpkt.setFlags(NetBIOSPacket.FLG_BROADCAST);
|
|
nbpkt.setQuestionCount(1);
|
|
nbpkt.setQuestionName("*\0\0\0\0\0\0\0\0\0\0\0\0\0\0", NetBIOSName.WorkStation, NetBIOSPacket.NAME_TYPE_NBSTAT,
|
|
NetBIOSPacket.NAME_CLASS_IN);
|
|
|
|
// Send the request to the specified address
|
|
|
|
InetAddress destAddr = InetAddress.getByName(ipAddr);
|
|
DatagramPacket dgram = new DatagramPacket(nbpkt.getBuffer(), nbpkt.getLength(), destAddr,
|
|
RFCNetBIOSProtocol.NAME_PORT);
|
|
|
|
// Allocate a receive datagram packet
|
|
|
|
byte[] rxbuf = new byte[FindNameBufferSize];
|
|
DatagramPacket rxdgram = new DatagramPacket(rxbuf, rxbuf.length);
|
|
|
|
// Create a NetBIOS packet using the receive buffer
|
|
|
|
NetBIOSPacket rxpkt = new NetBIOSPacket(rxbuf);
|
|
|
|
// DEBUG
|
|
|
|
if (logger.isDebugEnabled() && m_debug)
|
|
nbpkt.DumpPacket(false);
|
|
|
|
// Create a vector to store the remote hosts NetBIOS names
|
|
|
|
NetBIOSNameList nameList = null;
|
|
|
|
try
|
|
{
|
|
|
|
// Send the name query datagram
|
|
|
|
m_dgramSock.send(dgram);
|
|
|
|
// Receive a datagram packet
|
|
|
|
m_dgramSock.receive(rxdgram);
|
|
|
|
// DEBUG
|
|
|
|
if (logger.isDebugEnabled() && m_debug)
|
|
{
|
|
logger.debug("NetBIOS: Rx Datagram");
|
|
rxpkt.DumpPacket(false);
|
|
}
|
|
|
|
// Check if this is a valid response datagram
|
|
|
|
if (rxpkt.isResponse() && rxpkt.getOpcode() == NetBIOSPacket.RESP_QUERY && rxpkt.getAnswerCount() >= 1)
|
|
{
|
|
|
|
// Get the received name list
|
|
|
|
nameList = rxpkt.getAdapterStatusNameList();
|
|
|
|
// If the name list is valid update the names with the original address that was connected to
|
|
|
|
if( nameList != null)
|
|
{
|
|
for ( int i = 0; i < nameList.numberOfNames(); i++)
|
|
{
|
|
NetBIOSName nbName = nameList.getName(i);
|
|
nbName.addIPAddress(destAddr.getAddress());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (java.io.IOException ex)
|
|
{
|
|
|
|
// DEBUG
|
|
|
|
if (logger.isDebugEnabled() && m_debug)
|
|
logger.debug(ex.toString());
|
|
|
|
// Unknown host
|
|
|
|
throw new UnknownHostException(ipAddr);
|
|
}
|
|
|
|
// Return the NetBIOS name list
|
|
|
|
return nameList;
|
|
}
|
|
|
|
/**
|
|
* Determine the subnet mask from the local hosts TCP/IP address
|
|
*
|
|
* @param addr TCP/IP address to set the subnet mask for, in 'nnn.nnn.nnn.nnn' format.
|
|
*/
|
|
public static String GenerateSubnetMask(String addr) throws java.net.UnknownHostException
|
|
{
|
|
|
|
// Set the TCP/IP address string
|
|
|
|
String localIP = addr;
|
|
|
|
// Get the local TCP/IP address, if a null string has been specified
|
|
|
|
if (localIP == null)
|
|
localIP = InetAddress.getLocalHost().getHostAddress();
|
|
|
|
// Find the location of the first dot in the TCP/IP address
|
|
|
|
int dotPos = localIP.indexOf('.');
|
|
if (dotPos != -1)
|
|
{
|
|
|
|
// Extract the leading IP address value
|
|
|
|
String ipStr = localIP.substring(0, dotPos);
|
|
int ipVal = Integer.valueOf(ipStr).intValue();
|
|
|
|
// Determine the subnet mask to use
|
|
|
|
if (ipVal <= 127)
|
|
{
|
|
|
|
// Class A address
|
|
|
|
m_subnetMask = "" + ipVal + ".255.255.255";
|
|
}
|
|
else if (ipVal <= 191)
|
|
{
|
|
|
|
// Class B adddress
|
|
|
|
dotPos++;
|
|
while (localIP.charAt(dotPos) != '.' && dotPos < localIP.length())
|
|
dotPos++;
|
|
|
|
if (dotPos < localIP.length())
|
|
m_subnetMask = localIP.substring(0, dotPos) + ".255.255";
|
|
}
|
|
else if (ipVal <= 223)
|
|
{
|
|
|
|
// Class C address
|
|
|
|
dotPos++;
|
|
int dotCnt = 1;
|
|
|
|
while (dotCnt < 3 && dotPos < localIP.length())
|
|
{
|
|
|
|
// Check if the current character is a dot
|
|
|
|
if (localIP.charAt(dotPos++) == '.')
|
|
dotCnt++;
|
|
}
|
|
|
|
if (dotPos < localIP.length())
|
|
m_subnetMask = localIP.substring(0, dotPos - 1) + ".255";
|
|
}
|
|
}
|
|
|
|
// Check if the subnet mask has been set, if not then use a general
|
|
// broadcast mask
|
|
|
|
if (m_subnetMask == null)
|
|
{
|
|
|
|
// Invalid TCP/IP address string format, use a general broadcast mask
|
|
// for now.
|
|
|
|
m_subnetMask = "255.255.255.255";
|
|
}
|
|
|
|
// DEBUG
|
|
|
|
if (logger.isDebugEnabled() && m_debug)
|
|
logger.debug("NetBIOS: Set subnet mask to " + m_subnetMask);
|
|
|
|
// Return the subnet mask string
|
|
|
|
return m_subnetMask;
|
|
}
|
|
|
|
/**
|
|
* Get the WINS/NetBIOS name lookup timeout, in milliseconds.
|
|
*
|
|
* @return int
|
|
*/
|
|
public static int getLookupTimeout()
|
|
{
|
|
return m_lookupTmo;
|
|
}
|
|
|
|
/**
|
|
* Return the name lookup type that is used when setting up new sessions, valid values are
|
|
* DNSOnly, WINSOnly, WINSAndDNS. DNSOnly is the default type.
|
|
*
|
|
* @return int
|
|
*/
|
|
public static int getLookupType()
|
|
{
|
|
return m_lookupType;
|
|
}
|
|
|
|
/**
|
|
* Return the subnet mask string
|
|
*
|
|
* @return Subnet mask string, in 'nnn.nnn.nnn.nnn' format
|
|
*/
|
|
public static String getSubnetMask()
|
|
{
|
|
return m_subnetMask;
|
|
}
|
|
|
|
/**
|
|
* Determine if the WINS server address is configured
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public final static boolean hasWINSServer()
|
|
{
|
|
return m_winsServer != null ? true : false;
|
|
}
|
|
|
|
/**
|
|
* Return the WINS server address
|
|
*
|
|
* @return InetAddress
|
|
*/
|
|
public final static InetAddress getWINSServer()
|
|
{
|
|
return m_winsServer;
|
|
}
|
|
|
|
/**
|
|
* Determine if SMB session debugging is enabled
|
|
*
|
|
* @return true if debugging is enabled, else false.
|
|
*/
|
|
public static boolean isDebug()
|
|
{
|
|
return m_debug;
|
|
}
|
|
|
|
/**
|
|
* Return the next session index
|
|
*
|
|
* @return int
|
|
*/
|
|
private final static synchronized int getSessionId()
|
|
{
|
|
return m_sessIdx++;
|
|
}
|
|
|
|
/**
|
|
* Return the JVM unique id, used when generating caller names
|
|
*
|
|
* @return int
|
|
*/
|
|
public final static int getJVMIndex()
|
|
{
|
|
return m_jvmIdx;
|
|
}
|
|
|
|
/**
|
|
* Convert the TCP/IP host name to a NetBIOS name string.
|
|
*
|
|
* @return java.lang.String
|
|
* @param hostName java.lang.String
|
|
*/
|
|
public static String NetBIOSName(String hostName)
|
|
{
|
|
|
|
// Check if the host name contains a domain name
|
|
|
|
String nbName = new String(hostName.toUpperCase());
|
|
int pos = nbName.indexOf(".");
|
|
|
|
if (pos != -1)
|
|
{
|
|
|
|
// Strip the domain name for the NetBIOS name
|
|
|
|
nbName = nbName.substring(0, pos);
|
|
}
|
|
|
|
// Return the NetBIOS name string
|
|
|
|
return nbName;
|
|
}
|
|
|
|
/**
|
|
* Enable/disable NetBIOS session debugging
|
|
*
|
|
* @param dbg true to enable debugging, else false
|
|
*/
|
|
public static void setDebug(boolean dbg)
|
|
{
|
|
m_debug = dbg;
|
|
}
|
|
|
|
/**
|
|
* Set the WINS/NetBIOS name lookup timeout value, in milliseconds.
|
|
*
|
|
* @param tmo int
|
|
*/
|
|
public static void setLookupTimeout(int tmo)
|
|
{
|
|
if (tmo >= 250)
|
|
m_lookupTmo = tmo;
|
|
}
|
|
|
|
/**
|
|
* Set the name lookup type(s) to be used when opening new sessions, valid values are DNSOnly,
|
|
* WINSOnly, WINSAndDNS. DNSOnly is the default type.
|
|
*
|
|
* @param typ int
|
|
*/
|
|
public static void setLookupType(int typ)
|
|
{
|
|
if (typ >= DNSOnly && typ <= WINSAndDNS)
|
|
m_lookupType = typ;
|
|
}
|
|
|
|
/**
|
|
* Set the subnet mask string
|
|
*
|
|
* @param subnet Subnet mask string, in 'nnn.nnn.nnn.nnn' format
|
|
*/
|
|
public static void setSubnetMask(String subnet)
|
|
{
|
|
m_subnetMask = subnet;
|
|
}
|
|
|
|
/**
|
|
* Set the WINS server address
|
|
*
|
|
* @param addr InetAddress
|
|
*/
|
|
public final static void setWINSServer(InetAddress addr)
|
|
{
|
|
m_winsServer = addr;
|
|
}
|
|
|
|
/**
|
|
* Get the NetBIOS adapter status for the specified node.
|
|
*
|
|
* @return java.util.Vector
|
|
* @param nodeName java.lang.String
|
|
*/
|
|
private static Vector AdapterStatus(String nodeName) throws java.io.IOException
|
|
{
|
|
|
|
// Create the socket
|
|
|
|
DatagramSocket nameSock = new DatagramSocket();
|
|
|
|
// Enable the timeout on the socket
|
|
|
|
nameSock.setSoTimeout(2000);
|
|
|
|
// Create an adapter status NetBIOS packet
|
|
|
|
NetBIOSPacket nbpkt = new NetBIOSPacket();
|
|
|
|
// nbpkt.setTransactionId( m_tranIdx++);
|
|
nbpkt.setTransactionId(9999);
|
|
nbpkt.setOpcode(NetBIOSPacket.NAME_QUERY);
|
|
nbpkt.setFlags(NetBIOSPacket.FLG_BROADCAST);
|
|
nbpkt.setQuestionCount(1);
|
|
nbpkt.setQuestionName(nodeName, NetBIOSName.WorkStation, NetBIOSPacket.NAME_TYPE_NBSTAT,
|
|
NetBIOSPacket.NAME_CLASS_IN);
|
|
|
|
// Build a broadcast destination address
|
|
|
|
InetAddress destAddr = InetAddress.getByName(nodeName);
|
|
DatagramPacket dgram = new DatagramPacket(nbpkt.getBuffer(), nbpkt.getLength(), destAddr,
|
|
RFCNetBIOSProtocol.NAME_PORT);
|
|
|
|
// Allocate a receive datagram packet
|
|
|
|
byte[] rxbuf = new byte[512];
|
|
DatagramPacket rxdgram = new DatagramPacket(rxbuf, rxbuf.length);
|
|
|
|
// Create a NetBIOS packet using the receive buffer
|
|
|
|
NetBIOSPacket rxpkt = new NetBIOSPacket(rxbuf);
|
|
|
|
// DEBUG
|
|
|
|
if (logger.isDebugEnabled() && m_debug)
|
|
nbpkt.DumpPacket(false);
|
|
|
|
// Send the find name datagram
|
|
|
|
nameSock.send(dgram);
|
|
|
|
// Receive a reply datagram
|
|
|
|
boolean rxOK = false;
|
|
|
|
do
|
|
{
|
|
|
|
// Receive a datagram packet
|
|
|
|
nameSock.receive(rxdgram);
|
|
|
|
// DEBUG
|
|
|
|
if (logger.isDebugEnabled() && m_debug)
|
|
{
|
|
logger.debug("NetBIOS: Rx Datagram");
|
|
rxpkt.DumpPacket(false);
|
|
}
|
|
|
|
// Check if this is a valid response datagram
|
|
|
|
if (rxpkt.isResponse() && rxpkt.getOpcode() == NetBIOSPacket.RESP_QUERY)
|
|
rxOK = true;
|
|
|
|
} while (!rxOK);
|
|
|
|
// Return the remote host address
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Connect to a remote host.
|
|
*
|
|
* @param remHost Remote host node name/NetBIOS name.
|
|
* @param locName Local name/NetBIOS name.
|
|
* @param remAddr Optional remote address, if null then lookup will be done to convert name to
|
|
* address
|
|
* @exception java.io.IOException I/O error occurred.
|
|
* @exception java.net.UnknownHostException Remote host is unknown.
|
|
*/
|
|
public void Open(String remHost, String locName, String remAddr) throws java.io.IOException,
|
|
java.net.UnknownHostException
|
|
{
|
|
|
|
// Debug mode
|
|
|
|
if (logger.isDebugEnabled() && m_debug)
|
|
logger.debug("NetBIOS: Call " + remHost);
|
|
|
|
// Convert the remote host name to an address
|
|
|
|
boolean dnsLookup = false;
|
|
InetAddress addr = null;
|
|
|
|
// Set the remote address is specified
|
|
|
|
if (remAddr != null)
|
|
{
|
|
|
|
// Use the specified remote address
|
|
|
|
addr = InetAddress.getByName(remAddr);
|
|
}
|
|
else
|
|
{
|
|
|
|
// Try a WINS/NetBIOS type name lookup, if enabled
|
|
|
|
if (getLookupType() != DNSOnly)
|
|
{
|
|
try
|
|
{
|
|
NetBIOSName netName = FindName(remHost, NetBIOSName.FileServer, 500);
|
|
if (netName != null && netName.numberOfAddresses() > 0)
|
|
addr = InetAddress.getByName(netName.getIPAddressString(0));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
}
|
|
}
|
|
|
|
// Try a DNS type name lookup, if enabled
|
|
|
|
if (addr == null && getLookupType() != WINSOnly)
|
|
{
|
|
addr = InetAddress.getByName(remHost);
|
|
dnsLookup = true;
|
|
}
|
|
}
|
|
|
|
// Check if we translated the remote host name to an address
|
|
|
|
if (addr == null)
|
|
throw new java.net.UnknownHostException(remHost);
|
|
|
|
// Debug mode
|
|
|
|
if (logger.isDebugEnabled() && m_debug)
|
|
logger.debug("NetBIOS: Remote node hase address " + addr.getHostAddress() + " ("
|
|
+ (dnsLookup ? "DNS" : "WINS") + ")");
|
|
|
|
// Determine the remote name to call
|
|
|
|
String remoteName = null;
|
|
|
|
if (getRemoteNameType() == NetBIOSName.FileServer && useWildcardFileServerName() == true)
|
|
remoteName = "*SMBSERVER";
|
|
else
|
|
remoteName = remHost;
|
|
|
|
// Open a session to the remote server
|
|
|
|
int resp = openSession(remoteName, addr);
|
|
|
|
// Check the server response
|
|
|
|
if (resp == RFCNetBIOSProtocol.SESSION_ACK)
|
|
return;
|
|
else if (resp == RFCNetBIOSProtocol.SESSION_REJECT)
|
|
{
|
|
|
|
// Try the connection again with the remote host name
|
|
|
|
if (remoteName.equals(remHost) == false)
|
|
resp = openSession(remHost, addr);
|
|
|
|
// Check if we got a valid response this time
|
|
|
|
if (resp == RFCNetBIOSProtocol.SESSION_ACK)
|
|
return;
|
|
|
|
// Server rejected the connection
|
|
|
|
throw new java.io.IOException("NetBIOS session reject");
|
|
}
|
|
else if (resp == RFCNetBIOSProtocol.SESSION_RETARGET)
|
|
throw new java.io.IOException("NetBIOS ReTarget");
|
|
|
|
// Invalid session response, hangup the session
|
|
|
|
Close();
|
|
throw new java.io.IOException("Invalid NetBIOS response, 0x" + Integer.toHexString(resp));
|
|
}
|
|
|
|
/**
|
|
* Open a NetBIOS session to a remote server
|
|
*
|
|
* @param remoteName String
|
|
* @param addr InetAddress
|
|
* @return int
|
|
* @exception IOException
|
|
*/
|
|
private final int openSession(String remoteName, InetAddress addr) throws IOException
|
|
{
|
|
|
|
// Create the socket
|
|
|
|
m_nbSocket = new Socket(addr, m_remotePort);
|
|
|
|
// Enable the timeout on the socket, and disable Nagle algorithm
|
|
|
|
m_nbSocket.setSoTimeout(m_tmo);
|
|
m_nbSocket.setTcpNoDelay(true);
|
|
|
|
// Attach input/output streams to the socket
|
|
|
|
m_nbIn = new DataInputStream(m_nbSocket.getInputStream());
|
|
m_nbOut = new DataOutputStream(m_nbSocket.getOutputStream());
|
|
|
|
// Allocate a buffer to receive the session response
|
|
|
|
byte[] inpkt = new byte[RFCNetBIOSProtocol.SESSRESP_LEN];
|
|
|
|
// Create the from/to NetBIOS names
|
|
|
|
NetBIOSName fromName = createUniqueCallerName();
|
|
NetBIOSName toName = new NetBIOSName(remoteName, getRemoteNameType(), false);
|
|
|
|
// Debug
|
|
|
|
if (logger.isDebugEnabled() && m_debug)
|
|
logger.debug("NetBIOS: Call from " + fromName + " to " + toName);
|
|
|
|
// Build the session request packet
|
|
|
|
NetBIOSPacket nbPkt = new NetBIOSPacket();
|
|
nbPkt.buildSessionSetupRequest(fromName, toName);
|
|
|
|
// Send the session request packet
|
|
|
|
m_nbOut.write(nbPkt.getBuffer(), 0, nbPkt.getLength());
|
|
|
|
// Allocate a buffer for the session request response, and read the response
|
|
|
|
int resp = -1;
|
|
|
|
if (m_nbIn.read(inpkt, 0, RFCNetBIOSProtocol.SESSRESP_LEN) >= RFCNetBIOSProtocol.HEADER_LEN)
|
|
{
|
|
|
|
// Check the session request response
|
|
|
|
resp = (int) (inpkt[0] & 0xFF);
|
|
|
|
// Debug mode
|
|
|
|
if (logger.isDebugEnabled() && m_debug)
|
|
logger.debug("NetBIOS: Rx " + NetBIOSPacket.getTypeAsString(resp));
|
|
}
|
|
|
|
// Check for a positive response
|
|
|
|
if (resp != RFCNetBIOSProtocol.SESSION_ACK)
|
|
{
|
|
|
|
// Close the socket and streams
|
|
|
|
m_nbIn.close();
|
|
m_nbIn = null;
|
|
|
|
m_nbOut.close();
|
|
m_nbOut = null;
|
|
|
|
m_nbSocket.close();
|
|
m_nbSocket = null;
|
|
}
|
|
|
|
// Return the response code
|
|
|
|
return resp;
|
|
}
|
|
|
|
/**
|
|
* Return the local NetBIOS name type.
|
|
*
|
|
* @return char
|
|
*/
|
|
public char getLocalNameType()
|
|
{
|
|
return m_locNameType;
|
|
}
|
|
|
|
/**
|
|
* Return the remote NetBIOS name type.
|
|
*
|
|
* @return char
|
|
*/
|
|
public char getRemoteNameType()
|
|
{
|
|
return m_remNameType;
|
|
}
|
|
|
|
/**
|
|
* Get the session timeout value
|
|
*
|
|
* @return NetBIOS session timeout value
|
|
*/
|
|
public int getTimeout()
|
|
{
|
|
return m_tmo;
|
|
}
|
|
|
|
/**
|
|
* Close the NetBIOS session.
|
|
*
|
|
* @exception IOException If an I/O error occurs
|
|
*/
|
|
public void Close() throws IOException
|
|
{
|
|
|
|
// Debug mode
|
|
|
|
if (logger.isDebugEnabled() && m_debug)
|
|
logger.debug("NetBIOS: HangUp");
|
|
|
|
// Close the session if active
|
|
|
|
if (m_nbSocket != null)
|
|
{
|
|
m_nbSocket.close();
|
|
m_nbSocket = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Receive a data packet from the remote host.
|
|
*
|
|
* @param buf Byte buffer to receive the data into.
|
|
* @param tmo Receive timeout in milliseconds, or zero for no timeout
|
|
* @return Length of the received data.
|
|
* @exception java.io.IOException I/O error occurred.
|
|
*/
|
|
public int Receive(byte[] buf, int tmo) throws java.io.IOException
|
|
{
|
|
|
|
// Set the read timeout
|
|
|
|
if (tmo != m_tmo)
|
|
{
|
|
m_nbSocket.setSoTimeout(tmo);
|
|
m_tmo = tmo;
|
|
}
|
|
|
|
// Read a data packet, dump any session keep alive packets
|
|
|
|
int pkttyp;
|
|
int rdlen;
|
|
|
|
do
|
|
{
|
|
|
|
// Read a packet header
|
|
|
|
rdlen = m_nbIn.read(buf, 0, RFCNetBIOSProtocol.HEADER_LEN);
|
|
|
|
// Debug mode
|
|
|
|
if (logger.isDebugEnabled() && m_debug)
|
|
logger.debug("NetBIOS: Read " + rdlen + " bytes");
|
|
|
|
// Check if a header was received
|
|
|
|
if (rdlen < RFCNetBIOSProtocol.HEADER_LEN)
|
|
throw new java.io.IOException("NetBIOS Short Read");
|
|
|
|
// Get the packet type from the header
|
|
|
|
pkttyp = (int) (buf[0] & 0xFF);
|
|
|
|
} while (pkttyp == RFCNetBIOSProtocol.SESSION_KEEPALIVE);
|
|
|
|
// Debug mode
|
|
|
|
if (logger.isDebugEnabled() && m_debug)
|
|
logger.debug("NetBIOS: Rx Pkt Type = " + pkttyp + ", " + Integer.toHexString(pkttyp));
|
|
|
|
// Check that the packet is a session data packet
|
|
|
|
if (pkttyp != RFCNetBIOSProtocol.SESSION_MESSAGE)
|
|
throw new java.io.IOException("NetBIOS Unknown Packet Type, " + pkttyp);
|
|
|
|
// Extract the data size from the packet header
|
|
|
|
int pktlen = (int) DataPacker.getShort(buf, 2);
|
|
if (logger.isDebugEnabled() && m_debug)
|
|
logger.debug("NetBIOS: Rx Data Len = " + pktlen);
|
|
|
|
// Check if the user buffer is long enough to contain the data
|
|
|
|
if (buf.length < (pktlen + RFCNetBIOSProtocol.HEADER_LEN))
|
|
{
|
|
|
|
// Debug mode
|
|
|
|
logger.debug("NetBIOS: Rx Pkt Type = " + pkttyp + ", " + Integer.toHexString(pkttyp));
|
|
logger.debug("NetBIOS: Rx Buf Too Small pkt=" + pktlen + " buflen=" + buf.length);
|
|
HexDump.Dump(buf, 16, 0);
|
|
|
|
throw new java.io.IOException("NetBIOS Recv Buffer Too Small (pkt=" + pktlen + "/buf=" + buf.length + ")");
|
|
}
|
|
|
|
// Read the data part of the packet into the users buffer, this may take
|
|
// several reads
|
|
|
|
int totlen = 0;
|
|
int offset = RFCNetBIOSProtocol.HEADER_LEN;
|
|
|
|
while (pktlen > 0)
|
|
{
|
|
|
|
// Read the data
|
|
|
|
rdlen = m_nbIn.read(buf, offset, pktlen);
|
|
|
|
// Update the received length and remaining data length
|
|
|
|
totlen += rdlen;
|
|
pktlen -= rdlen;
|
|
|
|
// Update the user buffer offset as more reads will be required
|
|
// to complete the data read
|
|
|
|
offset += rdlen;
|
|
|
|
} // end while reading data
|
|
|
|
// Return the received data length, not including the NetBIOS header
|
|
|
|
return totlen;
|
|
}
|
|
|
|
/**
|
|
* Send a data packet to the remote host.
|
|
*
|
|
* @param data Byte array containing the data to be sent.
|
|
* @param siz Length of the data to send.
|
|
* @return true if the data was sent successfully, else false.
|
|
* @exception java.io.IOException I/O error occurred.
|
|
*/
|
|
public boolean Send(byte[] data, int siz) throws java.io.IOException
|
|
{
|
|
|
|
// Check that the session is valid
|
|
|
|
if (m_nbSocket == null)
|
|
return false;
|
|
|
|
// Debug mode
|
|
|
|
if (logger.isDebugEnabled() && m_debug)
|
|
logger.debug("NetBIOS: Tx " + siz + " bytes");
|
|
|
|
// Fill in the NetBIOS message header, this is already allocated as
|
|
// part of the users buffer.
|
|
|
|
data[0] = (byte) RFCNetBIOSProtocol.SESSION_MESSAGE;
|
|
data[1] = (byte) 0;
|
|
|
|
DataPacker.putShort((short) siz, data, 2);
|
|
|
|
// Output the data packet
|
|
|
|
int bufSiz = siz + RFCNetBIOSProtocol.HEADER_LEN;
|
|
m_nbOut.write(data, 0, bufSiz);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Set the local NetBIOS name type for this session.
|
|
*
|
|
* @param nameType int
|
|
*/
|
|
public void setLocalNameType(char nameType)
|
|
{
|
|
m_locNameType = nameType;
|
|
}
|
|
|
|
/**
|
|
* Set the remote NetBIOS name type.
|
|
*
|
|
* @param param char
|
|
*/
|
|
public void setRemoteNameType(char nameType)
|
|
{
|
|
m_remNameType = nameType;
|
|
}
|
|
|
|
/**
|
|
* Set the session timeout value
|
|
*
|
|
* @param tmo Session timeout value
|
|
*/
|
|
public void setTimeout(int tmo)
|
|
{
|
|
m_tmo = tmo;
|
|
}
|
|
|
|
/**
|
|
* Set the caller session name template string that is appended to the local host name to create
|
|
* a unique caller name.
|
|
*
|
|
* @param template String
|
|
* @exception NameTemplateExcepition
|
|
*/
|
|
public final static void setCallerNameTemplate(String template) throws NameTemplateException
|
|
{
|
|
|
|
// Check if the template string is valid, is not too long
|
|
|
|
if (template == null || template.length() == 0 || template.length() > MaxCallerNameTemplateLength)
|
|
throw new NameTemplateException("Invalid template string, " + template);
|
|
|
|
// Template must contain at least one session id template character
|
|
|
|
if (template.indexOf(SessionIdChar) == -1)
|
|
throw new NameTemplateException("No session id character in template");
|
|
|
|
// Check if the template contains any invalid characters
|
|
|
|
for (int i = 0; i < template.length(); i++)
|
|
{
|
|
if (ValidTemplateChars.indexOf(template.charAt(i)) == -1)
|
|
throw new NameTemplateException("Invalid character in template, '" + template.charAt(i) + "'");
|
|
}
|
|
|
|
// Set the caller name template string
|
|
|
|
m_callerTemplate = template;
|
|
|
|
// Clear the local name part string so that it will be regenerated to match the new template
|
|
// string
|
|
|
|
m_localNamePart = null;
|
|
}
|
|
|
|
/**
|
|
* Set the JVM index, used to generate unique caller names when multiple JVMs are run on the
|
|
* same host.
|
|
*
|
|
* @param jvmIdx int
|
|
*/
|
|
public final static void setJVMIndex(int jvmIdx)
|
|
{
|
|
if (jvmIdx >= 0)
|
|
m_jvmIdx = jvmIdx;
|
|
}
|
|
|
|
/**
|
|
* Create a unique caller name for a new NetBIOS session. The unique name contains the local
|
|
* host name plus an index that is unique for this JVM, plus an optional JVM index.
|
|
*
|
|
* @return NetBIOSName
|
|
*/
|
|
private final NetBIOSName createUniqueCallerName()
|
|
{
|
|
|
|
// Check if the local name part has been set
|
|
|
|
if (m_localNamePart == null)
|
|
{
|
|
|
|
String localName = null;
|
|
|
|
try
|
|
{
|
|
localName = InetAddress.getLocalHost().getHostName();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
}
|
|
|
|
// Check if the name contains a domain
|
|
|
|
int pos = localName.indexOf(".");
|
|
|
|
if (pos != -1)
|
|
localName = localName.substring(0, pos);
|
|
|
|
// Truncate the name if the host name plus the template is longer than 15 characters.
|
|
|
|
int nameLen = 16 - m_callerTemplate.length();
|
|
|
|
if (localName.length() > nameLen)
|
|
localName = localName.substring(0, nameLen - 1);
|
|
|
|
// Set the local host name part
|
|
|
|
m_localNamePart = localName.toUpperCase();
|
|
}
|
|
|
|
// Get a unique session id and the unique JVM id
|
|
|
|
int sessId = getSessionId();
|
|
int jvmId = getJVMIndex();
|
|
|
|
// Build the NetBIOS name string
|
|
|
|
StringBuffer nameBuf = new StringBuffer(16);
|
|
|
|
nameBuf.append(m_localNamePart);
|
|
|
|
// Process the caller name template string
|
|
|
|
int idx = 0;
|
|
int len = -1;
|
|
|
|
while (idx < m_callerTemplate.length())
|
|
{
|
|
|
|
// Get the current template character
|
|
|
|
char ch = m_callerTemplate.charAt(idx++);
|
|
|
|
switch (ch)
|
|
{
|
|
|
|
// Session id
|
|
|
|
case SessionIdChar:
|
|
len = findRepeatLength(m_callerTemplate, idx, SessionIdChar);
|
|
appendZeroPaddedHexValue(sessId, len, nameBuf);
|
|
idx += len - 1;
|
|
break;
|
|
|
|
// JVM id
|
|
|
|
case JVMIdChar:
|
|
len = findRepeatLength(m_callerTemplate, idx, JVMIdChar);
|
|
appendZeroPaddedHexValue(jvmId, len, nameBuf);
|
|
idx += len - 1;
|
|
break;
|
|
|
|
// Pass any other characters through to the name string
|
|
|
|
default:
|
|
nameBuf.append(ch);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Create the NetBIOS name object
|
|
|
|
return new NetBIOSName(nameBuf.toString(), getLocalNameType(), false);
|
|
}
|
|
|
|
/**
|
|
* Find the length of the character block in the specified string
|
|
*
|
|
* @param str String
|
|
* @param pos int
|
|
* @param ch char
|
|
* @return int
|
|
*/
|
|
private final int findRepeatLength(String str, int pos, char ch)
|
|
{
|
|
int len = 1;
|
|
|
|
while (pos < str.length() && str.charAt(pos++) == ch)
|
|
len++;
|
|
return len;
|
|
}
|
|
|
|
/**
|
|
* Append a zero filled hex string to the specified string
|
|
*
|
|
* @param val int
|
|
* @param len int
|
|
* @param str StringBuffer
|
|
*/
|
|
private final void appendZeroPaddedHexValue(int val, int len, StringBuffer str)
|
|
{
|
|
|
|
// Create the hex string of the value
|
|
|
|
String hex = Integer.toHexString(val);
|
|
|
|
// Pad the final string as required
|
|
|
|
for (int i = 0; i < len - hex.length(); i++)
|
|
str.append("0");
|
|
str.append(hex);
|
|
}
|
|
|
|
/**
|
|
* Return the default socket timeout value
|
|
*
|
|
* @return int
|
|
*/
|
|
public static final int getDefaultTimeout()
|
|
{
|
|
return _defTimeout;
|
|
}
|
|
|
|
/**
|
|
* Set the default socket timeout for new sessions
|
|
*
|
|
* @param tmo int
|
|
*/
|
|
public static final void setDefaultTimeout(int tmo)
|
|
{
|
|
_defTimeout = tmo;
|
|
}
|
|
|
|
/**
|
|
* Return the use wildcard file server name flag status. If true the target name when conencting
|
|
* to a remote file server will be '*SMBSERVER', if false the remote name will be used.
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public static final boolean useWildcardFileServerName()
|
|
{
|
|
return m_useWildcardFileServer;
|
|
}
|
|
|
|
/**
|
|
* Set the use wildcard file server name flag. If true the target name when conencting to a
|
|
* remote file server will be '*SMBSERVER', if false the remote name will be used.
|
|
*
|
|
* @param useWildcard boolean
|
|
*/
|
|
public static final void setWildcardFileServerName(boolean useWildcard)
|
|
{
|
|
m_useWildcardFileServer = useWildcard;
|
|
}
|
|
|
|
/**
|
|
* Finalize the NetBIOS session object
|
|
*/
|
|
protected void finalize()
|
|
{
|
|
|
|
// Close the socket
|
|
|
|
if (m_nbSocket != null)
|
|
{
|
|
try
|
|
{
|
|
m_nbSocket.close();
|
|
}
|
|
catch (java.io.IOException ex)
|
|
{
|
|
}
|
|
m_nbSocket = null;
|
|
}
|
|
}
|
|
} |