0001: /* jcifs smb client library in Java
0002: * Copyright (C) 2000 "Michael B. Allen" <jcifs at samba dot org>
0003: * "Eric Glass" <jcifs at samba dot org>
0004: *
0005: * This library is free software; you can redistribute it and/or
0006: * modify it under the terms of the GNU Lesser General Public
0007: * License as published by the Free Software Foundation; either
0008: * version 2.1 of the License, or (at your option) any later version.
0009: *
0010: * This library is distributed in the hope that it will be useful,
0011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0013: * Lesser General Public License for more details.
0014: *
0015: * You should have received a copy of the GNU Lesser General Public
0016: * License along with this library; if not, write to the Free Software
0017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0018: */
0019:
0020: package jcifs.smb;
0021:
0022: import jcifs.netbios.NbtSocket;
0023: import jcifs.netbios.NbtException;
0024: import jcifs.netbios.NbtAddress;
0025: import jcifs.UniAddress;
0026: import jcifs.Config;
0027: import java.io.InputStream;
0028: import java.io.OutputStream;
0029: import java.io.PushbackInputStream;
0030: import java.io.IOException;
0031: import java.io.InterruptedIOException;
0032: import java.net.InetAddress;
0033: import java.net.SocketException;
0034: import java.net.UnknownHostException;
0035:
0036: import java.security.MessageDigest;
0037:
0038: import java.util.Arrays;
0039: import java.util.LinkedList;
0040: import java.util.ListIterator;
0041: import java.util.Enumeration;
0042: import java.util.HashMap;
0043:
0044: import jcifs.util.LogStream;
0045: import jcifs.util.Hexdump;
0046:
0047: class SmbTransport implements Runnable {
0048:
0049: private static final int DEFAULT_MAX_MPX_COUNT = 10;
0050: private static final int DEFAULT_RESPONSE_TIMEOUT = 10000;
0051: private static final int DEFAULT_SO_TIMEOUT = 15000;
0052: private static final int DEFAULT_RCV_BUF_SIZE = 60416;
0053: private static final int DEFAULT_SND_BUF_SIZE = 16644;
0054: private static final int DEFAULT_SSN_LIMIT = 250;
0055:
0056: private static final InetAddress LADDR = Config.getInetAddress(
0057: "jcifs.smb.client.laddr", null);
0058: private static final int LPORT = Config.getInt(
0059: "jcifs.smb.client.lport", 0);
0060: private static final int MAX_MPX_COUNT = Config.getInt(
0061: "jcifs.smb.client.maxMpxCount", DEFAULT_MAX_MPX_COUNT);
0062: private static final int SND_BUF_SIZE = Config.getInt(
0063: "jcifs.smb.client.snd_buf_size", DEFAULT_SND_BUF_SIZE);
0064: private static final int RCV_BUF_SIZE = Config.getInt(
0065: "jcifs.smb.client.rcv_buf_size", DEFAULT_RCV_BUF_SIZE);
0066: private static final boolean USE_UNICODE = Config.getBoolean(
0067: "jcifs.smb.client.useUnicode", true);
0068: private static final boolean FORCE_UNICODE = Config.getBoolean(
0069: "jcifs.smb.client.useUnicode", false);
0070: private static final boolean USE_NTSTATUS = Config.getBoolean(
0071: "jcifs.smb.client.useNtStatus", true);
0072: private static final boolean SIGNPREF = Config.getBoolean(
0073: "jcifs.smb.client.signingPreferred", false);
0074: private static final boolean USE_NTSMBS = Config.getBoolean(
0075: "jcifs.smb.client.useNTSmbs", true);
0076: private static final int DEFAULT_FLAGS2 = ServerMessageBlock.FLAGS2_LONG_FILENAMES
0077: | ServerMessageBlock.FLAGS2_EXTENDED_ATTRIBUTES
0078: | (SIGNPREF ? ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES
0079: : 0)
0080: | (USE_NTSTATUS ? ServerMessageBlock.FLAGS2_STATUS32 : 0)
0081: | (USE_UNICODE ? ServerMessageBlock.FLAGS2_UNICODE : 0);
0082: private static final int DEFAULT_CAPABILITIES = (USE_NTSMBS ? ServerMessageBlock.CAP_NT_SMBS
0083: : 0)
0084: | (USE_NTSTATUS ? ServerMessageBlock.CAP_STATUS32 : 0)
0085: | (USE_UNICODE ? ServerMessageBlock.CAP_UNICODE : 0)
0086: | ServerMessageBlock.CAP_DFS;
0087: private static final int FLAGS2 = Config.getInt(
0088: "jcifs.smb.client.flags2", DEFAULT_FLAGS2);
0089: private static final int CAPABILITIES = Config.getInt(
0090: "jcifs.smb.client.capabilities", DEFAULT_CAPABILITIES);
0091: private static final boolean TCP_NODELAY = Config.getBoolean(
0092: "jcifs.smb.client.tcpNoDelay", false);
0093: private static final int RESPONSE_TIMEOUT = Config.getInt(
0094: "jcifs.smb.client.responseTimeout",
0095: DEFAULT_RESPONSE_TIMEOUT);
0096:
0097: private static final int PUSHBACK_BUF_SIZE = 64;
0098: private static final int MID_OFFSET = 30;
0099: private static final int FLAGS_RESPONSE = 0x80;
0100: private static final LinkedList CONNECTIONS = new LinkedList();
0101: private static final int MAGIC[] = { 0xFF, 'S', 'M', 'B' };
0102:
0103: private static final int ST_GROUND = 0;
0104: private static final int ST_NEGOTIATING = 1;
0105: private static final int ST_NEGOTIATED = 2;
0106:
0107: private static byte[] snd_buf = new byte[0xFFFF];
0108: private static byte[] rcv_buf = new byte[0xFFFF];
0109:
0110: private int state;
0111: private NbtSocket socket;
0112: private HashMap responseTable;
0113: private InputStream in;
0114: private OutputStream out;
0115: private int localPort;
0116: private InetAddress localAddr;
0117: private Thread thread;
0118: private Object outLock;
0119: private UniAddress address;
0120: private int port;
0121: private LinkedList referrals = new LinkedList();
0122: private Mid[] mids = new Mid[MAX_MPX_COUNT];
0123: private short mid_next;
0124: private IOException socketException;
0125: private long sessionExpiration = System.currentTimeMillis()
0126: + SO_TIMEOUT;
0127:
0128: static final int SSN_LIMIT = Config.getInt(
0129: "jcifs.smb.client.ssnLimit", DEFAULT_SSN_LIMIT);
0130: static final int SO_TIMEOUT = Config.getInt(
0131: "jcifs.smb.client.soTimeout", DEFAULT_SO_TIMEOUT);
0132: static final String NATIVE_OS = Config.getProperty(
0133: "jcifs.smb.client.nativeOs", System.getProperty("os.name"));
0134: static final String NATIVE_LANMAN = Config.getProperty(
0135: "jcifs.smb.client.nativeLanMan", "jCIFS");
0136: static final int VC_NUMBER = 1;
0137: static LogStream log = LogStream.getInstance();
0138: static final SmbTransport NULL_TRANSPORT = new SmbTransport();
0139:
0140: class Mid {
0141: short mid;
0142:
0143: public int hashCode() {
0144: return mid;
0145: }
0146:
0147: public boolean equals(Object obj) {
0148: return ((Mid) obj).mid == mid;
0149: }
0150: }
0151:
0152: class ServerData {
0153: byte flags;
0154: int flags2;
0155: int maxMpxCount;
0156: int maxBufferSize;
0157: int sessionKey;
0158: // NT 4 Workstation is 0x43FD
0159: int capabilities;
0160: String oemDomainName;
0161: int securityMode;
0162: int security;
0163: boolean encryptedPasswords;
0164: boolean signaturesEnabled;
0165: boolean signaturesRequired;
0166: int maxNumberVcs;
0167: int maxRawSize;
0168: long serverTime;
0169: int serverTimeZone;
0170: int encryptionKeyLength;
0171: byte[] encryptionKey;
0172: }
0173:
0174: int flags2 = FLAGS2;
0175: int maxMpxCount = MAX_MPX_COUNT;
0176: int snd_buf_size = SND_BUF_SIZE;
0177: int rcv_buf_size = RCV_BUF_SIZE;
0178: int capabilities = CAPABILITIES;
0179: int sessionKey = 0x00000000;
0180: boolean useUnicode = USE_UNICODE;
0181: String tconHostName;
0182: ServerData server;
0183: SigningDigest digest = null;
0184: LinkedList sessions;
0185:
0186: static synchronized SmbTransport getSmbTransport(
0187: UniAddress address, int port) {
0188: return getSmbTransport(address, port, LADDR, LPORT);
0189: }
0190:
0191: static synchronized SmbTransport getSmbTransport(
0192: UniAddress address, int port, InetAddress localAddr,
0193: int localPort) {
0194: SmbTransport conn;
0195:
0196: synchronized (CONNECTIONS) {
0197: if (SSN_LIMIT != 1) {
0198: ListIterator iter = CONNECTIONS.listIterator();
0199: while (iter.hasNext()) {
0200: conn = (SmbTransport) iter.next();
0201: if (conn.matches(address, port, localAddr,
0202: localPort)
0203: && (SSN_LIMIT == 0 || conn.sessions.size() < SSN_LIMIT)) {
0204: return conn;
0205: }
0206: }
0207: }
0208:
0209: conn = new SmbTransport(address, port, localAddr, localPort);
0210: CONNECTIONS.add(0, conn);
0211: }
0212:
0213: return conn;
0214: }
0215:
0216: SmbTransport(UniAddress address, int port, InetAddress localAddr,
0217: int localPort) {
0218: this .address = address;
0219: this .port = port;
0220: this .localAddr = localAddr;
0221: this .localPort = localPort;
0222:
0223: sessions = new LinkedList();
0224: responseTable = new HashMap();
0225: outLock = new Object();
0226: state = ST_GROUND;
0227:
0228: int i;
0229: for (i = 0; i < MAX_MPX_COUNT; i++) {
0230: mids[i] = new Mid();
0231: }
0232: }
0233:
0234: SmbTransport() {
0235: }
0236:
0237: synchronized SmbSession getSmbSession() {
0238: return getSmbSession(new NtlmPasswordAuthentication(null, null,
0239: null));
0240: }
0241:
0242: synchronized SmbSession getSmbSession(
0243: NtlmPasswordAuthentication auth) {
0244: SmbSession ssn;
0245:
0246: ListIterator iter = sessions.listIterator();
0247: while (iter.hasNext()) {
0248: ssn = (SmbSession) iter.next();
0249: if (ssn.matches(auth)) {
0250: ssn.auth = auth;
0251: return ssn;
0252: }
0253: }
0254:
0255: /* close old sessions */
0256: long now = System.currentTimeMillis();
0257: if (sessionExpiration < now) {
0258: sessionExpiration = now + SO_TIMEOUT;
0259: iter = sessions.listIterator();
0260: while (iter.hasNext()) {
0261: ssn = (SmbSession) iter.next();
0262: if (ssn.expiration < now) {
0263: ssn.logoff(false);
0264: iter.remove();
0265: }
0266: }
0267: }
0268:
0269: ssn = new SmbSession(address, port, localAddr, localPort, auth);
0270: ssn.transport = this ;
0271: sessions.add(ssn);
0272:
0273: return ssn;
0274: }
0275:
0276: boolean matches(UniAddress address, int port,
0277: InetAddress localAddr, int localPort) {
0278: InetAddress defaultLocal = null;
0279: try {
0280: defaultLocal = InetAddress.getLocalHost();
0281: } catch (UnknownHostException uhe) {
0282: }
0283: int p1 = (port == 0 || port == 139) ? 0 : port;
0284: int p2 = (this .port == 0 || this .port == 139) ? 0 : this .port;
0285: InetAddress la1 = localAddr == null ? defaultLocal : localAddr;
0286: InetAddress la2 = this .localAddr == null ? defaultLocal
0287: : this .localAddr;
0288: return address.equals(this .address) && p1 == p2
0289: && la1.equals(la2) && localPort == this .localPort;
0290: }
0291:
0292: boolean hasCapability(int cap) throws SmbException {
0293: if (state == ST_GROUND) {
0294: negotiate();
0295: }
0296: return (capabilities & cap) == cap;
0297: }
0298:
0299: boolean isSignatureSetupRequired(NtlmPasswordAuthentication auth) {
0300: return (flags2 & ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES) != 0
0301: && digest == null
0302: && auth != NtlmPasswordAuthentication.NULL
0303: && NtlmPasswordAuthentication.NULL.equals(auth) == false;
0304: }
0305:
0306: void tryClose(boolean inError) {
0307: SmbSession ssn;
0308:
0309: if (socket == null) {
0310: inError = true;
0311: }
0312:
0313: ListIterator iter = sessions.listIterator();
0314: while (iter.hasNext()) {
0315: ssn = (SmbSession) iter.next();
0316: ssn.logoff(inError);
0317: }
0318: if (socket != null) {
0319: try {
0320: socket.close();
0321: } catch (IOException ioe) {
0322: }
0323: }
0324: digest = null;
0325: in = null;
0326: out = null;
0327: socket = null;
0328: thread = null;
0329: responseTable.clear();
0330: referrals.clear();
0331: sessions.clear();
0332: synchronized (CONNECTIONS) {
0333: CONNECTIONS.remove(this );
0334: }
0335: state = ST_GROUND;
0336: }
0337:
0338: void start() throws Exception {
0339: thread = new Thread(this , "JCIFS-SmbTransport");
0340: thread.setDaemon(true);
0341: thread.start();
0342:
0343: wait(RESPONSE_TIMEOUT); /* wait for the thread to be started and socket opened */
0344:
0345: if (socket == null) { /* failed to open socket for some reason */
0346: throw new SmbException("Timeout trying to open socket",
0347: socketException);
0348: }
0349: }
0350:
0351: public void run() {
0352: Mid mid = new Mid();
0353: int i, m, nbtlen;
0354: ServerMessageBlock response;
0355:
0356: try {
0357: Object obj;
0358: NbtAddress naddr;
0359: String calledName;
0360:
0361: obj = address.getAddress();
0362: if (obj instanceof NbtAddress) {
0363: naddr = (NbtAddress) obj;
0364: } else {
0365: try {
0366: naddr = NbtAddress.getByName(((InetAddress) obj)
0367: .getHostAddress());
0368: } catch (UnknownHostException uhe) {
0369: naddr = null; // never happen
0370: }
0371: }
0372:
0373: calledName = address.firstCalledName();
0374: do {
0375: try {
0376: socket = new NbtSocket(naddr, calledName, port,
0377: localAddr, localPort);
0378: if (TCP_NODELAY) {
0379: socket.setTcpNoDelay(true);
0380: }
0381: socket.setSoTimeout(SO_TIMEOUT);
0382: in = new PushbackInputStream(socket
0383: .getInputStream(), PUSHBACK_BUF_SIZE);
0384: out = socket.getOutputStream();
0385: break;
0386: } catch (NbtException ne) {
0387: if (ne.errorClass == NbtException.ERR_SSN_SRVC
0388: && (ne.errorCode == NbtException.CALLED_NOT_PRESENT || ne.errorCode == NbtException.NOT_LISTENING_CALLED)) {
0389: if (log.level > 2)
0390: ne.printStackTrace(log);
0391: } else {
0392: throw ne;
0393: }
0394: }
0395: } while ((calledName = address.nextCalledName()) != null);
0396:
0397: if (socket == null) {
0398: throw new IOException(
0399: "Failed to establish session with " + address);
0400: }
0401:
0402: /* Save the calledName for using on SMB_COM_TREE_CONNECT
0403: */
0404: if (calledName == NbtAddress.SMBSERVER_NAME) {
0405: tconHostName = address.getHostAddress();
0406: } else {
0407: tconHostName = calledName;
0408: }
0409: } catch (IOException ioe) {
0410: socketException = ioe;
0411: if (log.level > 1)
0412: ioe.printStackTrace(log);
0413: return;
0414: } finally {
0415: synchronized (this ) {
0416: notifyAll();
0417: }
0418: }
0419:
0420: while (thread == Thread.currentThread()) {
0421: try {
0422: m = 0;
0423: while (m < 4) {
0424: if ((i = in.read()) < 0) {
0425: return;
0426: }
0427: if ((i & 0xFF) == MAGIC[m]) {
0428: m++;
0429: } else if ((i & 0xFF) == 0xFF) {
0430: m = 1;
0431: } else {
0432: m = 0;
0433: }
0434: }
0435:
0436: nbtlen = 4 + in.available();
0437:
0438: synchronized (rcv_buf) {
0439: rcv_buf[0] = (byte) 0xFF;
0440: rcv_buf[1] = (byte) 'S';
0441: rcv_buf[2] = (byte) 'M';
0442: rcv_buf[3] = (byte) 'B';
0443:
0444: if (in.read(rcv_buf, 4,
0445: ServerMessageBlock.HEADER_LENGTH - 4) != (ServerMessageBlock.HEADER_LENGTH - 4)) {
0446: /* Read on a netbios SocketInputStream does not
0447: * return until len bytes have been read or the stream is
0448: * closed. This must be the latter case.
0449: */
0450: break;
0451: }
0452: ((PushbackInputStream) in).unread(rcv_buf, 0,
0453: ServerMessageBlock.HEADER_LENGTH);
0454: if (rcv_buf[0] != (byte) 0xFF
0455: || rcv_buf[1] != (byte) 'S'
0456: || rcv_buf[2] != (byte) 'M'
0457: || rcv_buf[3] != (byte) 'B') {
0458: if (log.level > 1)
0459: log
0460: .println("bad smb header, purging session message: "
0461: + address);
0462: in.skip(in.available());
0463: continue;
0464: }
0465: if ((rcv_buf[ServerMessageBlock.FLAGS_OFFSET] & ServerMessageBlock.FLAGS_RESPONSE) == ServerMessageBlock.FLAGS_RESPONSE) {
0466: mid.mid = (short) (ServerMessageBlock.readInt2(
0467: rcv_buf, MID_OFFSET) & 0xFFFF);
0468:
0469: response = (ServerMessageBlock) responseTable
0470: .get(mid);
0471: if (response == null) {
0472: if (log.level > 2) {
0473: log.println("no handler for mid="
0474: + mid.mid
0475: + ", purging session message: "
0476: + address);
0477: }
0478: in.skip(in.available());
0479: continue;
0480: }
0481: synchronized (response) {
0482: response.useUnicode = useUnicode;
0483:
0484: if (log.level > 3)
0485: log
0486: .println("new data read from socket: "
0487: + address);
0488:
0489: if (response instanceof SmbComTransactionResponse) {
0490: Enumeration e = (Enumeration) response;
0491: if (e.hasMoreElements()) {
0492: e.nextElement();
0493: } else {
0494: if (log.level > 2)
0495: log
0496: .println("more responses to transaction than expected");
0497: continue;
0498: }
0499: if ((m = response.readWireFormat(in,
0500: rcv_buf, 0)) != nbtlen) {
0501: if (log.level > 1) {
0502: log
0503: .println("decoded "
0504: + m
0505: + " but nbtlen="
0506: + nbtlen
0507: + ", purging session message");
0508: }
0509: in.skip(in.available());
0510: }
0511: response.received = true;
0512:
0513: if (log.level > 3)
0514: log.println(response);
0515:
0516: if (response.errorCode != 0
0517: || e.hasMoreElements() == false) {
0518: ((SmbComTransactionResponse) response).hasMore = false;
0519: if (digest != null) {
0520: synchronized (outLock) {
0521: digest.verify(rcv_buf, 0,
0522: response);
0523: }
0524: }
0525: response.notify();
0526: }
0527: } else {
0528: response.readWireFormat(in, rcv_buf, 0);
0529: response.received = true;
0530:
0531: if (log.level > 3) {
0532: ServerMessageBlock smb = response;
0533:
0534: do {
0535: log.println(smb);
0536: } while (smb instanceof AndXServerMessageBlock
0537: && (smb = ((AndXServerMessageBlock) smb).andx) != null);
0538: if (log.level > 5) {
0539: Hexdump
0540: .hexdump(
0541: log,
0542: rcv_buf,
0543: 0,
0544: Math
0545: .min(
0546: response.length,
0547: 1024));
0548: }
0549: }
0550: if (digest != null) {
0551: synchronized (outLock) {
0552: digest.verify(rcv_buf, 0,
0553: response);
0554: }
0555: }
0556:
0557: response.notify();
0558: }
0559: }
0560: } else {
0561: // it's a request(break oplock)
0562: }
0563: }
0564: } catch (InterruptedIOException iioe) {
0565: if (responseTable.size() == 0) {
0566: tryClose(false);
0567: } else if (log.level > 1) {
0568: log.println("soTimeout has occured but there are "
0569: + responseTable.size()
0570: + " pending requests");
0571: }
0572: } catch (IOException ioe) {
0573: synchronized (this ) {
0574: tryClose(true);
0575: }
0576: if (log.level > 0
0577: && ioe.getMessage().startsWith(
0578: "Connection reset") == false) {
0579: log.println(ioe.getMessage() + ": " + address);
0580: ioe.printStackTrace(log);
0581: }
0582: }
0583: }
0584: }
0585:
0586: synchronized DfsReferral getDfsReferral(
0587: NtlmPasswordAuthentication auth, String path)
0588: throws SmbException {
0589: String subpath, node, host;
0590: DfsReferral dr = new DfsReferral();
0591: int p, n, i, s;
0592: UniAddress addr;
0593:
0594: SmbTree ipc = getSmbSession(auth).getSmbTree("IPC$", null);
0595: Trans2GetDfsReferralResponse resp = new Trans2GetDfsReferralResponse();
0596: ipc.sendTransaction(new Trans2GetDfsReferral(path), resp);
0597:
0598: subpath = path.substring(0, resp.pathConsumed);
0599: node = resp.referral.node;
0600: if (subpath.charAt(0) != '\\'
0601: || (i = subpath.indexOf('\\', 1)) < 2
0602: || (p = subpath.indexOf('\\', i + 1)) < (i + 2)
0603: || node.charAt(0) != '\\'
0604: || (s = node.indexOf('\\', 1)) < 2) {
0605: throw new SmbException("Invalid DFS path: " + path);
0606: }
0607: if ((n = node.indexOf('\\', s + 1)) == -1) {
0608: n = node.length();
0609: }
0610:
0611: dr.path = subpath.substring(p);
0612: dr.node = node.substring(0, n);
0613: dr.nodepath = node.substring(n);
0614: dr.server = node.substring(1, s);
0615: dr.share = node.substring(s + 1, n);
0616: dr.resolveHashes = auth.hashesExternal;
0617: /* NTLM HTTP Authentication must be re-negotiated
0618: * with challenge from 'server' to access DFS vol. */
0619: return dr;
0620: }
0621:
0622: synchronized DfsReferral lookupReferral(String unc) {
0623: DfsReferral dr;
0624: ListIterator iter = referrals.listIterator();
0625: int i, len;
0626:
0627: while (iter.hasNext()) {
0628: dr = (DfsReferral) iter.next();
0629: len = dr.path.length();
0630: for (i = 0; i < len && i < unc.length(); i++) {
0631: if (dr.path.charAt(i) != unc.charAt(i)) {
0632: break;
0633: }
0634: }
0635: if (i == len
0636: && (len == unc.length() || unc.charAt(len) == '\\')) {
0637: return dr;
0638: }
0639: }
0640:
0641: return null;
0642: }
0643:
0644: void send(ServerMessageBlock request, ServerMessageBlock response)
0645: throws SmbException {
0646: Mid mid = null;
0647:
0648: if (request.command != request.SMB_COM_NEGOTIATE)
0649: negotiate();
0650:
0651: request.flags2 |= flags2;
0652: request.useUnicode = useUnicode;
0653:
0654: if (response == null) {
0655: synchronized (outLock) {
0656: try {
0657: mid = aquireMid();
0658: request.mid = mid.mid;
0659: synchronized (snd_buf) {
0660: request.digest = digest;
0661: request.response = null;
0662: int length = request
0663: .writeWireFormat(snd_buf, 4);
0664: out.write(snd_buf, 4, length);
0665: out.flush();
0666:
0667: if (log.level > 3) {
0668: ServerMessageBlock smb = request;
0669:
0670: do {
0671: log.println(smb);
0672: } while (smb instanceof AndXServerMessageBlock
0673: && (smb = ((AndXServerMessageBlock) smb).andx) != null);
0674: if (log.level > 5) {
0675: Hexdump.hexdump(log, snd_buf, 0, Math
0676: .min(request.length, 1024));
0677: }
0678: }
0679: }
0680: } catch (IOException ioe) {
0681: tryClose(true);
0682: throw new SmbException(
0683: "An error occured sending the request.",
0684: ioe);
0685: } finally {
0686: releaseMid(mid);
0687: }
0688: }
0689:
0690: return;
0691: }
0692:
0693: // now for the normal case where response is not null
0694:
0695: synchronized (response) {
0696: try {
0697: synchronized (outLock) {
0698: response.received = false;
0699: mid = aquireMid();
0700: request.mid = mid.mid;
0701: responseTable.put(mid, response);
0702: synchronized (snd_buf) {
0703: if (digest != null) {
0704: request.digest = digest;
0705: request.response = response;
0706: }
0707: int length = request
0708: .writeWireFormat(snd_buf, 4);
0709: out.write(snd_buf, 4, length);
0710: out.flush();
0711:
0712: if (log.level > 3) {
0713: ServerMessageBlock smb = request;
0714:
0715: do {
0716: log.println(smb);
0717: } while (smb instanceof AndXServerMessageBlock
0718: && (smb = ((AndXServerMessageBlock) smb).andx) != null);
0719: if (log.level > 5) {
0720: Hexdump.hexdump(log, snd_buf, 0,
0721: request.length);
0722: }
0723: }
0724: }
0725: }
0726:
0727: // default it 1 so that 0 can be used as forever
0728: response
0729: .wait(response.responseTimeout == 1 ? RESPONSE_TIMEOUT
0730: : response.responseTimeout);
0731: } catch (InterruptedException ie) {
0732: tryClose(true);
0733: } catch (IOException ioe) {
0734: tryClose(true);
0735: throw new SmbException(
0736: "An error occured sending the request.", ioe);
0737: } finally {
0738: responseTable.remove(mid);
0739: synchronized (outLock) {
0740: releaseMid(mid);
0741: }
0742: }
0743: }
0744:
0745: if (response.received == false) {
0746: tryClose(true);
0747: throw new SmbException(
0748: "Timeout waiting for response from server: "
0749: + address);
0750: } else if (response.verifyFailed) {
0751: tryClose(true);
0752: throw new SmbException("Unverifiable signature: " + address);
0753: }
0754: response.errorCode = SmbException
0755: .getStatusByCode(response.errorCode);
0756: switch (response.errorCode) {
0757: case NtStatus.NT_STATUS_OK:
0758: break;
0759: case NtStatus.NT_STATUS_ACCESS_DENIED:
0760: case NtStatus.NT_STATUS_WRONG_PASSWORD:
0761: case NtStatus.NT_STATUS_LOGON_FAILURE:
0762: case NtStatus.NT_STATUS_ACCOUNT_RESTRICTION:
0763: case NtStatus.NT_STATUS_INVALID_LOGON_HOURS:
0764: case NtStatus.NT_STATUS_INVALID_WORKSTATION:
0765: case NtStatus.NT_STATUS_PASSWORD_EXPIRED:
0766: case NtStatus.NT_STATUS_ACCOUNT_DISABLED:
0767: case NtStatus.NT_STATUS_ACCOUNT_LOCKED_OUT:
0768: throw new SmbAuthException(response.errorCode);
0769: case NtStatus.NT_STATUS_PATH_NOT_COVERED:
0770: if (request.auth == null) {
0771: throw new SmbException(response.errorCode, null);
0772: }
0773: DfsReferral dr = getDfsReferral(request.auth, request.path);
0774: referrals.add(dr);
0775: throw dr;
0776: default:
0777: throw new SmbException(response.errorCode, null);
0778: }
0779: }
0780:
0781: void sendTransaction(SmbComTransaction request,
0782: SmbComTransactionResponse response) throws SmbException {
0783: Mid mid = null;
0784:
0785: negotiate();
0786:
0787: request.flags2 |= flags2;
0788: request.useUnicode = useUnicode;
0789: request.maxBufferSize = snd_buf_size;
0790: response.received = false;
0791: response.hasMore = true;
0792: response.isPrimary = true;
0793:
0794: try {
0795: synchronized (outLock) {
0796: mid = aquireMid();
0797: }
0798: request.txn_buf = BufferCache.getBuffer();
0799: response.txn_buf = BufferCache.getBuffer();
0800:
0801: request.nextElement();
0802: if (request.hasMoreElements()) {
0803: // multi-part request
0804:
0805: SmbComBlankResponse interimResponse = new SmbComBlankResponse();
0806:
0807: synchronized (interimResponse) {
0808: try {
0809: synchronized (outLock) {
0810: request.mid = mid.mid;
0811: responseTable.put(mid, interimResponse);
0812: synchronized (snd_buf) {
0813: request.digest = digest;
0814: request.response = response;
0815: int length = request.writeWireFormat(
0816: snd_buf, 4);
0817: out.write(snd_buf, 4, length);
0818: out.flush();
0819:
0820: if (log.level > 3) {
0821: log.println(request);
0822: if (log.level > 5) {
0823: Hexdump.hexdump(log, snd_buf,
0824: 0, request.length);
0825: }
0826: }
0827: }
0828: }
0829:
0830: interimResponse.wait(RESPONSE_TIMEOUT);
0831:
0832: if (interimResponse.received == false) {
0833: throw new SmbException(
0834: "Timeout waiting for response from server: "
0835: + address);
0836: }
0837: interimResponse.errorCode = SmbException
0838: .getStatusByCode(interimResponse.errorCode);
0839: switch (interimResponse.errorCode) {
0840: case NtStatus.NT_STATUS_OK:
0841: break;
0842: case NtStatus.NT_STATUS_ACCESS_DENIED:
0843: case NtStatus.NT_STATUS_WRONG_PASSWORD:
0844: case NtStatus.NT_STATUS_LOGON_FAILURE:
0845: case NtStatus.NT_STATUS_ACCOUNT_RESTRICTION:
0846: case NtStatus.NT_STATUS_INVALID_LOGON_HOURS:
0847: case NtStatus.NT_STATUS_INVALID_WORKSTATION:
0848: case NtStatus.NT_STATUS_PASSWORD_EXPIRED:
0849: case NtStatus.NT_STATUS_ACCOUNT_DISABLED:
0850: case NtStatus.NT_STATUS_ACCOUNT_LOCKED_OUT:
0851: throw new SmbAuthException(
0852: interimResponse.errorCode);
0853: case NtStatus.NT_STATUS_PATH_NOT_COVERED:
0854: if (request.auth == null) {
0855: throw new SmbException(
0856: interimResponse.errorCode, null);
0857: }
0858: DfsReferral dr = getDfsReferral(
0859: request.auth, request.path);
0860: referrals.add(dr);
0861: throw dr;
0862: default:
0863: throw new SmbException(
0864: interimResponse.errorCode, null);
0865: }
0866: } finally {
0867: responseTable.remove(mid);
0868: }
0869: }
0870:
0871: request.nextElement();
0872: }
0873:
0874: synchronized (response) {
0875: try {
0876: synchronized (outLock) {
0877: request.mid = mid.mid;
0878: responseTable.put(mid, response);
0879: do {
0880: synchronized (snd_buf) {
0881: request.digest = digest;
0882: request.response = response;
0883: int length = request.writeWireFormat(
0884: snd_buf, 4);
0885: out.write(snd_buf, 4, length);
0886: out.flush();
0887:
0888: if (log.level > 3) {
0889: log.println(request);
0890: if (log.level > 5) {
0891: Hexdump.hexdump(log, snd_buf,
0892: 0, request.length);
0893: }
0894: }
0895: }
0896: } while (request.hasMoreElements()
0897: && request.nextElement() != null);
0898: }
0899:
0900: do {
0901: // default it 1 so that 0 can be used as forever
0902: response.received = false;
0903: response
0904: .wait(response.responseTimeout == 1 ? RESPONSE_TIMEOUT
0905: : response.responseTimeout);
0906: } while (response.received
0907: && response.hasMoreElements());
0908: } finally {
0909: responseTable.remove(mid);
0910: }
0911: }
0912: } catch (InterruptedException ie) {
0913: tryClose(true);
0914: } catch (IOException ioe) {
0915: tryClose(true);
0916: throw new SmbException(
0917: "An error occured sending the request.", ioe);
0918: } finally {
0919: synchronized (outLock) {
0920: releaseMid(mid);
0921: }
0922: BufferCache.releaseBuffer(request.txn_buf);
0923: BufferCache.releaseBuffer(response.txn_buf);
0924: }
0925:
0926: if (response.received == false) {
0927: tryClose(true);
0928: throw new SmbException(
0929: "Timeout waiting for response from server: "
0930: + address);
0931: } else if (response.verifyFailed) {
0932: tryClose(true);
0933: throw new SmbException("Unverifiable signature: " + address);
0934: }
0935: response.errorCode = SmbException
0936: .getStatusByCode(response.errorCode);
0937: switch (response.errorCode) {
0938: case NtStatus.NT_STATUS_OK:
0939: break;
0940: case NtStatus.NT_STATUS_ACCESS_DENIED:
0941: case NtStatus.NT_STATUS_WRONG_PASSWORD:
0942: case NtStatus.NT_STATUS_LOGON_FAILURE:
0943: case NtStatus.NT_STATUS_ACCOUNT_RESTRICTION:
0944: case NtStatus.NT_STATUS_INVALID_LOGON_HOURS:
0945: case NtStatus.NT_STATUS_INVALID_WORKSTATION:
0946: case NtStatus.NT_STATUS_PASSWORD_EXPIRED:
0947: case NtStatus.NT_STATUS_ACCOUNT_DISABLED:
0948: case NtStatus.NT_STATUS_ACCOUNT_LOCKED_OUT:
0949: throw new SmbAuthException(response.errorCode);
0950: case NtStatus.NT_STATUS_PATH_NOT_COVERED:
0951: if (request.auth == null) {
0952: throw new SmbException(response.errorCode, null);
0953: }
0954: DfsReferral dr = getDfsReferral(request.auth, request.path);
0955: referrals.add(dr);
0956: throw dr;
0957: case 0x80000005:
0958: break;
0959: default:
0960: throw new SmbException(response.errorCode, null);
0961: }
0962: }
0963:
0964: synchronized void negotiate0() throws Exception {
0965: start(); /* start the transport thread (which opens the socket) */
0966:
0967: if (this == NULL_TRANSPORT) {
0968: throw new RuntimeException("Null transport cannot be used");
0969: }
0970:
0971: if (log.level > 2)
0972: log.println("requesting negotiation with " + address);
0973:
0974: /*
0975: * Negotiate Protocol Request / Response
0976: */
0977:
0978: SmbComNegotiateResponse response = new SmbComNegotiateResponse();
0979: send(new SmbComNegotiate(), response);
0980:
0981: if (response.dialectIndex > 10) {
0982: throw new SmbException(
0983: "This client does not support the negotiated dialect.");
0984: }
0985:
0986: server = new ServerData();
0987: server.securityMode = response.securityMode;
0988: server.security = response.security;
0989: server.encryptedPasswords = response.encryptedPasswords;
0990: server.signaturesEnabled = response.signaturesEnabled;
0991: server.signaturesRequired = response.signaturesRequired;
0992: server.maxMpxCount = response.maxMpxCount;
0993: server.maxNumberVcs = response.maxNumberVcs;
0994: server.maxBufferSize = response.maxBufferSize;
0995: server.maxRawSize = response.maxRawSize;
0996: server.sessionKey = response.sessionKey;
0997: server.capabilities = response.capabilities;
0998: server.serverTime = response.serverTime;
0999: server.serverTimeZone = response.serverTimeZone;
1000: server.encryptionKeyLength = response.encryptionKeyLength;
1001: server.encryptionKey = response.encryptionKey;
1002: server.oemDomainName = response.oemDomainName;
1003:
1004: if (server.signaturesRequired
1005: || (server.signaturesEnabled && SIGNPREF)) {
1006: flags2 |= ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES;
1007: } else {
1008: flags2 &= 0xFFFF ^ ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES;
1009: }
1010:
1011: maxMpxCount = maxMpxCount < server.maxMpxCount ? maxMpxCount
1012: : server.maxMpxCount;
1013: maxMpxCount = maxMpxCount < 1 ? 1 : maxMpxCount;
1014:
1015: snd_buf_size = snd_buf_size < server.maxBufferSize ? snd_buf_size
1016: : server.maxBufferSize;
1017:
1018: capabilities &= server.capabilities;
1019: if ((capabilities & ServerMessageBlock.CAP_UNICODE) == 0) {
1020: // server doesn't want unicode
1021: if (FORCE_UNICODE) {
1022: capabilities |= ServerMessageBlock.CAP_UNICODE;
1023: } else {
1024: useUnicode = false;
1025: flags2 &= 0xFFFF ^ ServerMessageBlock.FLAGS2_UNICODE;
1026: }
1027: }
1028: }
1029:
1030: synchronized void negotiate() throws SmbException {
1031: if (state == ST_GROUND) {
1032: state = ST_NEGOTIATING;
1033: } else {
1034: while (state != ST_NEGOTIATED) {
1035: try {
1036: wait();
1037: } catch (InterruptedException ie) {
1038: tryClose(true);
1039: throw new SmbException(
1040: "Interrupted opening socket", ie);
1041: }
1042: }
1043: return;
1044: }
1045:
1046: try {
1047: negotiate0();
1048: state = ST_NEGOTIATED;
1049: } catch (Exception ex) {
1050: if (log.level > 1)
1051: ex.printStackTrace(log);
1052: tryClose(true);
1053: throw new SmbException("Failed to negotiate", ex);
1054: } finally {
1055: notifyAll();
1056: }
1057: }
1058:
1059: public String toString() {
1060: String ret = "SmbTransport[address=" + address;
1061: if (socket == null) {
1062: ret += ",port=,localAddr=,localPort=]";
1063: } else {
1064: ret += ",port=" + socket.getPort() + ",localAddr="
1065: + socket.getLocalAddress() + ",localPort="
1066: + socket.getLocalPort() + "]";
1067: }
1068: return ret;
1069: }
1070:
1071: /* Manage MIDs */
1072:
1073: Mid aquireMid() throws SmbException {
1074: int i;
1075:
1076: if (mid_next == 0) {
1077: mid_next++;
1078: }
1079:
1080: for (;;) {
1081: for (i = 0; i < maxMpxCount; i++) {
1082: if (mids[i].mid == 0) {
1083: break;
1084: }
1085: }
1086: if (i == maxMpxCount) {
1087: try {
1088: outLock.wait();
1089: } catch (InterruptedException ie) {
1090: throw new SmbException("Interrupted aquiring mid",
1091: ie);
1092: }
1093: } else {
1094: break;
1095: }
1096: }
1097:
1098: mids[i].mid = mid_next++;
1099:
1100: return mids[i];
1101: }
1102:
1103: void releaseMid(Mid mid) {
1104: int i;
1105:
1106: for (i = 0; i < maxMpxCount; i++) {
1107: if (mids[i].mid == mid.mid) {
1108: mid.mid = 0;
1109: outLock.notify();
1110: return;
1111: }
1112: }
1113: if (log.level > 1)
1114: log.println("mid not found");
1115: }
1116: }
|