summaryrefslogtreecommitdiff
path: root/source3/libsmb/clientgen.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/libsmb/clientgen.c')
-rw-r--r--source3/libsmb/clientgen.c127
1 files changed, 113 insertions, 14 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;