summaryrefslogtreecommitdiff
path: root/source3/libsmb
diff options
context:
space:
mode:
Diffstat (limited to 'source3/libsmb')
-rw-r--r--source3/libsmb/clientgen.c127
-rw-r--r--source3/libsmb/clifsinfo.c2
-rw-r--r--source3/libsmb/clireadwrite.c50
-rw-r--r--source3/libsmb/smb_signing.c10
4 files changed, 164 insertions, 25 deletions
diff --git a/source3/libsmb/clientgen.c b/source3/libsmb/clientgen.c
index e1dacb3921..1a4b1f770f 100644
--- a/source3/libsmb/clientgen.c
+++ b/source3/libsmb/clientgen.c
@@ -2,6 +2,7 @@
Unix SMB/CIFS implementation.
SMB client generic functions
Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Jeremy Allison 2007.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -54,20 +55,20 @@ int cli_set_port(struct cli_state *cli, int port)
should never go into a blocking read.
****************************************************************************/
-static BOOL client_receive_smb(struct cli_state *cli, BOOL eat_keepalives)
+static ssize_t client_receive_smb(struct cli_state *cli, BOOL eat_keepalives, size_t maxlen)
{
- BOOL ret;
+ ssize_t len;
int fd = cli->fd;
char *buffer = cli->inbuf;
unsigned int timeout = cli->timeout;
for(;;) {
- ret = receive_smb_raw(fd, buffer, timeout);
+ len = receive_smb_raw(fd, buffer, timeout, maxlen);
- if (!ret) {
+ if (len < 0) {
DEBUG(10,("client_receive_smb failed\n"));
show_msg(buffer);
- return ret;
+ return len;
}
/* Ignore session keepalive packets. */
@@ -85,11 +86,11 @@ static BOOL client_receive_smb(struct cli_state *cli, BOOL eat_keepalives)
cli->smb_rw_error = READ_BAD_DECRYPT;
close(cli->fd);
cli->fd = -1;
- return False;
+ return -1;
}
}
show_msg(buffer);
- return ret;
+ return len;
}
/****************************************************************************
@@ -98,21 +99,21 @@ static BOOL client_receive_smb(struct cli_state *cli, BOOL eat_keepalives)
BOOL cli_receive_smb_internal(struct cli_state *cli, BOOL eat_keepalives)
{
- BOOL ret;
+ ssize_t len;
/* fd == -1 causes segfaults -- Tom (tom@ninja.nl) */
if (cli->fd == -1)
return False;
again:
- ret = client_receive_smb(cli, eat_keepalives);
+ len = client_receive_smb(cli, eat_keepalives, 0);
- if (ret && !eat_keepalives && (CVAL(cli->inbuf,0) == SMBkeepalive)) {
+ if (len >= 0 && !eat_keepalives && (CVAL(cli->inbuf,0) == SMBkeepalive)) {
/* Give back the keepalive. */
return True;
}
- if (ret) {
+ if (len > 0) {
/* it might be an oplock break request */
if (!(CVAL(cli->inbuf, smb_flg) & FLAG_REPLY) &&
CVAL(cli->inbuf,smb_com) == SMBlockingX &&
@@ -121,7 +122,9 @@ BOOL cli_receive_smb_internal(struct cli_state *cli, BOOL eat_keepalives)
if (cli->oplock_handler) {
int fnum = SVAL(cli->inbuf,smb_vwv2);
unsigned char level = CVAL(cli->inbuf,smb_vwv3+1);
- if (!cli->oplock_handler(cli, fnum, level)) return False;
+ if (!cli->oplock_handler(cli, fnum, level)) {
+ return False;
+ }
}
/* try to prevent loops */
SCVAL(cli->inbuf,smb_com,0xFF);
@@ -130,12 +133,12 @@ BOOL cli_receive_smb_internal(struct cli_state *cli, BOOL eat_keepalives)
}
/* If the server is not responding, note that now */
- if (!ret) {
+ if (len <= 0) {
DEBUG(0, ("Receiving SMB: Server stopped responding\n"));
cli->smb_rw_error = smb_read_error;
close(cli->fd);
cli->fd = -1;
- return ret;
+ return False;
}
if (!cli_check_sign_mac(cli)) {
@@ -187,6 +190,102 @@ BOOL cli_receive_smb_return_keepalive(struct cli_state *cli)
return cli_receive_smb_internal(cli, False);
}
+/****************************************************************************
+ Read the data portion of a readX smb.
+ The timeout is in milliseconds
+****************************************************************************/
+
+ssize_t cli_receive_smb_data(struct cli_state *cli, char *buffer, size_t len)
+{
+ if (cli->timeout > 0) {
+ return read_socket_with_timeout(cli->fd, buffer, len, len, cli->timeout);
+ } else {
+ return read_data(cli->fd, buffer, len);
+ }
+}
+
+/****************************************************************************
+ Read a smb readX header.
+ We can only use this if encryption and signing are off.
+****************************************************************************/
+
+BOOL cli_receive_smb_readX_header(struct cli_state *cli)
+{
+ ssize_t len, offset;
+
+ if (cli->fd == -1)
+ return False;
+
+ again:
+
+ /* Read up to the size of a readX header reply. */
+ len = client_receive_smb(cli, True, (smb_size - 4) + 24);
+
+ if (len > 0) {
+ /* it might be an oplock break request */
+ if (!(CVAL(cli->inbuf, smb_flg) & FLAG_REPLY) &&
+ CVAL(cli->inbuf,smb_com) == SMBlockingX &&
+ SVAL(cli->inbuf,smb_vwv6) == 0 &&
+ SVAL(cli->inbuf,smb_vwv7) == 0) {
+ ssize_t total_len = smb_len(cli->inbuf);
+
+ if (total_len > CLI_SAMBA_MAX_LARGE_READX_SIZE+SAFETY_MARGIN) {
+ goto read_err;
+ }
+
+ /* Read the rest of the data. */
+ if (!cli_receive_smb_data(cli,cli->inbuf+len,total_len - len)) {
+ goto read_err;
+ }
+
+ if (cli->oplock_handler) {
+ int fnum = SVAL(cli->inbuf,smb_vwv2);
+ unsigned char level = CVAL(cli->inbuf,smb_vwv3+1);
+ if (!cli->oplock_handler(cli, fnum, level)) return False;
+ }
+ /* try to prevent loops */
+ SCVAL(cli->inbuf,smb_com,0xFF);
+ goto again;
+ }
+ }
+
+ /* Check it's a non-chained readX reply. */
+ if (!(CVAL(cli->inbuf, smb_flg) & FLAG_REPLY) ||
+ (CVAL(cli->inbuf,smb_vwv0) != 0xFF) ||
+ (CVAL(cli->inbuf,smb_com) != SMBreadX)) {
+ /*
+ * We're not coping here with asnyc replies to
+ * other calls. Punt here - we need async client
+ * libs for this.
+ */
+ goto read_err;
+ }
+
+ /*
+ * We know it's a readX reply - ensure we've read the
+ * padding bytes also.
+ */
+
+ offset = SVAL(cli->inbuf,smb_vwv6);
+ if (offset > len) {
+ ssize_t ret;
+ size_t padbytes = offset - len;
+ ret = cli_receive_smb_data(cli,smb_buf(cli->inbuf),padbytes);
+ if (ret != padbytes) {
+ goto read_err;
+ }
+ }
+
+ return True;
+
+ read_err:
+
+ cli->smb_rw_error = smb_read_error = READ_ERROR;
+ close(cli->fd);
+ cli->fd = -1;
+ return False;
+}
+
static ssize_t write_socket(int fd, const char *buf, size_t len)
{
ssize_t ret=0;
diff --git a/source3/libsmb/clifsinfo.c b/source3/libsmb/clifsinfo.c
index d8ada1a896..28facb511d 100644
--- a/source3/libsmb/clifsinfo.c
+++ b/source3/libsmb/clifsinfo.c
@@ -66,7 +66,7 @@ BOOL cli_unix_extensions_version(struct cli_state *cli, uint16 *pmajor, uint16 *
*pmajor = SVAL(rdata,0);
*pminor = SVAL(rdata,2);
- *pcaplow = IVAL(rdata,4);
+ cli->posix_capabilities = *pcaplow = IVAL(rdata,4);
*pcaphigh = IVAL(rdata,8);
/* todo: but not yet needed
diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c
index 1c72cb2942..35d2afe2e7 100644
--- a/source3/libsmb/clireadwrite.c
+++ b/source3/libsmb/clireadwrite.c
@@ -46,7 +46,7 @@ static BOOL cli_issue_read(struct cli_state *cli, int fnum, off_t offset,
SIVAL(cli->outbuf,smb_vwv3,offset);
SSVAL(cli->outbuf,smb_vwv5,size);
SSVAL(cli->outbuf,smb_vwv6,size);
- SSVAL(cli->outbuf,smb_vwv7,((size >> 16) & 1));
+ SSVAL(cli->outbuf,smb_vwv7,(size >> 16));
SSVAL(cli->outbuf,smb_mid,cli->mid + i);
if (bigoffset) {
@@ -63,9 +63,11 @@ static BOOL cli_issue_read(struct cli_state *cli, int fnum, off_t offset,
ssize_t cli_read(struct cli_state *cli, int fnum, char *buf, off_t offset, size_t size)
{
char *p;
- int size2;
- int readsize;
+ size_t size2;
+ size_t readsize;
ssize_t total = 0;
+ /* We can only do direct reads if not signing. */
+ BOOL direct_reads = !client_is_signing_on(cli);
if (size == 0)
return 0;
@@ -75,7 +77,11 @@ ssize_t cli_read(struct cli_state *cli, int fnum, char *buf, off_t offset, size_
* rounded down to a multiple of 1024.
*/
- if (cli->capabilities & CAP_LARGE_READX) {
+ if (client_is_signing_on(cli) == False &&
+ cli_encryption_on(cli) == False &&
+ (cli->posix_capabilities & CIFS_UNIX_LARGE_READ_CAP)) {
+ readsize = CLI_SAMBA_MAX_POSIX_LARGE_READX_SIZE;
+ } else if (cli->capabilities & CAP_LARGE_READX) {
if (cli->is_samba) {
readsize = CLI_SAMBA_MAX_LARGE_READX_SIZE;
} else {
@@ -93,8 +99,13 @@ ssize_t cli_read(struct cli_state *cli, int fnum, char *buf, off_t offset, size_
if (!cli_issue_read(cli, fnum, offset, readsize, 0))
return -1;
- if (!cli_receive_smb(cli))
- return -1;
+ if (direct_reads) {
+ if (!cli_receive_smb_readX_header(cli))
+ return -1;
+ } else {
+ if (!cli_receive_smb(cli))
+ return -1;
+ }
/* Check for error. Make sure to check for DOS and NT
errors. */
@@ -125,7 +136,7 @@ ssize_t cli_read(struct cli_state *cli, int fnum, char *buf, off_t offset, size_
}
size2 = SVAL(cli->inbuf, smb_vwv5);
- size2 |= (((unsigned int)(SVAL(cli->inbuf, smb_vwv7) & 1)) << 16);
+ size2 |= (((unsigned int)(SVAL(cli->inbuf, smb_vwv7))) << 16);
if (size2 > readsize) {
DEBUG(5,("server returned more than we wanted!\n"));
@@ -135,10 +146,29 @@ ssize_t cli_read(struct cli_state *cli, int fnum, char *buf, off_t offset, size_
return -1;
}
- /* Copy data into buffer */
+ if (!direct_reads) {
+ /* Copy data into buffer */
+ p = smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_vwv6);
+ memcpy(buf + total, p, size2);
+ } else {
+ /* Ensure the remaining data matches the return size. */
+ ssize_t toread = smb_len_large(cli->inbuf) - SVAL(cli->inbuf,smb_vwv6);
- p = smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_vwv6);
- memcpy(buf + total, p, size2);
+ /* Ensure the size is correct. */
+ if (toread != size2) {
+ DEBUG(5,("direct read logic fail toread (%d) != size2 (%u)\n",
+ (int)toread, (unsigned int)size2 ));
+ return -1;
+ }
+
+ /* Read data directly into buffer */
+ toread = cli_receive_smb_data(cli,buf+total,size2);
+ if (toread != size2) {
+ DEBUG(5,("direct read read failure toread (%d) != size2 (%u)\n",
+ (int)toread, (unsigned int)size2 ));
+ return -1;
+ }
+ }
total += size2;
offset += size2;
diff --git a/source3/libsmb/smb_signing.c b/source3/libsmb/smb_signing.c
index d3384ce365..30f41476e3 100644
--- a/source3/libsmb/smb_signing.c
+++ b/source3/libsmb/smb_signing.c
@@ -658,6 +658,16 @@ BOOL client_set_trans_sign_state_off(struct cli_state *cli, uint16 mid)
}
/***********************************************************
+ Is client signing on ?
+************************************************************/
+
+BOOL client_is_signing_on(struct cli_state *cli)
+{
+ struct smb_sign_info *si = &cli->sign_info;
+ return si->doing_signing;
+}
+
+/***********************************************************
SMB signing - Server implementation - send the MAC.
************************************************************/