mirror of
https://github.com/obarthel/amiga-smbfs.git
synced 2025-12-08 14:58:35 +00:00
654 lines
15 KiB
C
654 lines
15 KiB
C
/*
|
|
* :ts=4
|
|
*
|
|
* sock.c
|
|
*
|
|
* Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke
|
|
* Modified by Christian Starkjohann <cs -at- hal -dot- kph -dot- tuwien -dot- ac -dot- at>
|
|
* Modified for use with AmigaOS by Olaf Barthel <obarthel -at- gmx -dot- net>
|
|
*/
|
|
|
|
#include "smbfs.h"
|
|
|
|
/*****************************************************************************/
|
|
|
|
#include <smb/smb_fs.h>
|
|
#include <smb/smb.h>
|
|
#include <smb/smbno.h>
|
|
|
|
/*****************************************************************************/
|
|
|
|
#include "smb_abstraction.h"
|
|
#include "dump_smb.h"
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* Byte offsets into the packet buffer reference the following data
|
|
* (with the first four octets used by the NetBIOS session header):
|
|
*
|
|
* 0: WORD netbios_session[2] = NetBIOS session header
|
|
* 4: BYTE smb_idf[4] = contains 0xFF, 'SMB'
|
|
* 8: BYTE smb_com = command code
|
|
* 9: BYTE smb_rcls = error code class
|
|
* 10: BYTE smb_reh = reserved (contains AH if DOS INT-24 ERR)
|
|
* 11: WORD smb_err = error code
|
|
* 13: BYTE smb_reb = reserved
|
|
* 14: WORD smb_res[7] = reserved
|
|
* 28: WORD smb_tid = tree id #
|
|
* 30: WORD smb_pid = caller's process id #
|
|
* 32: WORD smb_uid = user id #
|
|
* 34: WORD smb_mid = mutiplex id #
|
|
* 36: BYTE smb_wct = count of parameter words
|
|
* 37: WORD smb_vwv[] = variable # words of params
|
|
* 39: WORD smb_bcc = # bytes of data following
|
|
* 41: BYTE smb_data[] = data bytes
|
|
*/
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* smb_receive_raw
|
|
fs points to the correct segment, sock != NULL, target != NULL
|
|
The smb header is only stored if want_header != 0. */
|
|
static int
|
|
smb_receive_raw (const struct smb_server *server, int sock_fd, unsigned char *target, int max_raw_length, int want_header)
|
|
{
|
|
int len, result;
|
|
int already_read;
|
|
unsigned char netbios_session_buf[256];
|
|
int netbios_session_payload_size;
|
|
|
|
re_recv:
|
|
|
|
/* Read the NetBIOS session header (rfc-1002, section 4.3.1) */
|
|
result = recvfrom (sock_fd, netbios_session_buf, 4, 0, NULL, NULL);
|
|
if (result < 0)
|
|
{
|
|
LOG (("smb_receive_raw: recv error = %ld\n", errno));
|
|
result = (-errno);
|
|
goto out;
|
|
}
|
|
|
|
if (result < 4)
|
|
{
|
|
LOG (("smb_receive_raw: got less than 4 bytes\n"));
|
|
result = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
netbios_session_payload_size = (int)smb_len (netbios_session_buf);
|
|
SHOWVALUE(netbios_session_payload_size);
|
|
|
|
#if defined(DUMP_SMB)
|
|
{
|
|
if(netbios_session_buf[0] != 0x00 && netbios_session_payload_size > 0)
|
|
{
|
|
if(netbios_session_payload_size > 256 - 4)
|
|
netbios_session_payload_size = 256 - 4;
|
|
|
|
result = recvfrom (sock_fd, &netbios_session_buf[4], netbios_session_payload_size - 4, 0, NULL, NULL);
|
|
if (result < 0)
|
|
{
|
|
LOG (("smb_receive_raw: recv error = %ld\n", errno));
|
|
result = (-errno);
|
|
goto out;
|
|
}
|
|
|
|
if(result < netbios_session_payload_size - 4)
|
|
{
|
|
result = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
dump_netbios_header(__FILE__,__LINE__,netbios_session_buf,&netbios_session_buf[4],netbios_session_payload_size);
|
|
}
|
|
else
|
|
{
|
|
dump_netbios_header(__FILE__,__LINE__,netbios_session_buf,NULL,0);
|
|
}
|
|
}
|
|
#endif /* defined(DUMP_SMB) */
|
|
|
|
/* Check the session type. */
|
|
switch (netbios_session_buf[0])
|
|
{
|
|
/* 0x00 == session message */
|
|
case 0x00:
|
|
|
|
break;
|
|
|
|
/* 0x85 == session keepalive */
|
|
case 0x85:
|
|
|
|
LOG (("smb_receive_raw: Got SESSION KEEP ALIVE\n"));
|
|
goto re_recv;
|
|
|
|
/* 0x81 == session request */
|
|
/* 0x82 == positive session response */
|
|
/* 0x83 == negative session response */
|
|
/* 0x84 == retarget session response */
|
|
default:
|
|
|
|
LOG (("smb_receive_raw: Invalid session header type 0x%02lx\n", netbios_session_buf[0]));
|
|
result = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
/* The length in the RFC NB header is the raw data length (17 bits) */
|
|
len = netbios_session_payload_size;
|
|
if (len > max_raw_length)
|
|
{
|
|
LOG (("smb_receive_raw: Received length (%ld) > max_xmit (%ld)!\n", len, max_raw_length));
|
|
result = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
/* Prepend the NetBIOS header to what is read? */
|
|
if (want_header)
|
|
{
|
|
/* Check for buffer overflow. */
|
|
if(len + 4 > max_raw_length)
|
|
{
|
|
LOG (("smb_receive_raw: Received length (%ld) > max_xmit (%ld)!\n", len, max_raw_length));
|
|
result = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
memcpy (target, netbios_session_buf, 4);
|
|
target += 4;
|
|
}
|
|
|
|
for(already_read = 0 ; already_read < len ; already_read += result)
|
|
{
|
|
result = recvfrom (sock_fd, (void *) (target + already_read), len - already_read, 0, NULL, NULL);
|
|
if (result < 0)
|
|
{
|
|
LOG (("smb_receive_raw: recvfrom error = %ld\n", errno));
|
|
|
|
result = (-errno);
|
|
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
#if defined(DUMP_SMB)
|
|
{
|
|
/* If want_header==0 then this is the data returned by SMB_COM_READ_RAW. */
|
|
dump_smb(__FILE__,__LINE__,!want_header,target,already_read,smb_packet_to_consumer,server->max_recv);
|
|
}
|
|
#endif /* defined(DUMP_SMB) */
|
|
|
|
result = already_read;
|
|
|
|
out:
|
|
|
|
return result;
|
|
}
|
|
|
|
/* smb_receive
|
|
fs points to the correct segment, server != NULL, sock!=NULL */
|
|
int
|
|
smb_receive (struct smb_server *server, int sock_fd)
|
|
{
|
|
byte * packet = server->packet;
|
|
int result;
|
|
|
|
result = smb_receive_raw (server, sock_fd, packet,
|
|
server->max_recv - 4, /* max_xmit in server includes NB header */
|
|
1); /* We want the header */
|
|
if (result < 0)
|
|
{
|
|
LOG (("smb_receive: receive error: %ld\n", result));
|
|
goto out;
|
|
}
|
|
|
|
server->rcls = *((unsigned char *) (packet + 9));
|
|
server->err = WVAL (packet, 11);
|
|
|
|
if (server->rcls != 0)
|
|
LOG (("smb_receive: rcls=%ld, err=%ld\n", server->rcls, server->err));
|
|
|
|
out:
|
|
|
|
return result;
|
|
}
|
|
|
|
/* smb_receive's preconditions also apply here. */
|
|
static int
|
|
smb_receive_trans2 (struct smb_server *server, int sock_fd, int *data_len, int *param_len, char **data, char **param)
|
|
{
|
|
unsigned char *inbuf = server->packet;
|
|
int total_data;
|
|
int total_param;
|
|
int result;
|
|
|
|
LOG (("smb_receive_trans2: enter\n"));
|
|
|
|
(*data_len) = (*param_len) = 0;
|
|
(*param) = (*data) = NULL;
|
|
|
|
result = smb_receive (server, sock_fd);
|
|
if (result < 0)
|
|
goto fail;
|
|
|
|
if (server->rcls != 0)
|
|
goto fail;
|
|
|
|
/* parse out the lengths */
|
|
total_data = WVAL (inbuf, smb_tdrcnt);
|
|
total_param = WVAL (inbuf, smb_tprcnt);
|
|
|
|
if ((total_data > server->max_recv) || (total_param > server->max_recv))
|
|
{
|
|
LOG (("smb_receive_trans2: data/param too long\n"));
|
|
|
|
result = -EIO;
|
|
goto fail;
|
|
}
|
|
|
|
/* Allocate it, but only if there is something to allocate
|
|
in the first place. ZZZ this may not be the proper approach,
|
|
and we should return an error for 'no data'. */
|
|
if(total_data > 0)
|
|
{
|
|
(*data) = malloc (total_data);
|
|
if ((*data) == NULL)
|
|
{
|
|
LOG (("smb_receive_trans2: could not alloc data area\n"));
|
|
|
|
result = -ENOMEM;
|
|
goto fail;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
(*data) = NULL;
|
|
}
|
|
|
|
/* Allocate it, but only if there is something to allocate
|
|
in the first place. ZZZ this may not be the proper approach,
|
|
and we should return an error for 'no parameters'. */
|
|
if(total_param > 0)
|
|
{
|
|
(*param) = malloc(total_param);
|
|
if ((*param) == NULL)
|
|
{
|
|
LOG (("smb_receive_trans2: could not alloc param area\n"));
|
|
|
|
result = -ENOMEM;
|
|
goto fail;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
(*param) = NULL;
|
|
}
|
|
|
|
LOG (("smb_rec_trans2: total_data/param: %ld/%ld\n", total_data, total_param));
|
|
|
|
while (1)
|
|
{
|
|
if (WVAL (inbuf, smb_prdisp) + WVAL (inbuf, smb_prcnt) > total_param)
|
|
{
|
|
LOG (("smb_receive_trans2: invalid parameters\n"));
|
|
result = -EIO;
|
|
goto fail;
|
|
}
|
|
|
|
if((*param) != NULL)
|
|
memcpy ((*param) + WVAL (inbuf, smb_prdisp), smb_base (inbuf) + WVAL (inbuf, smb_proff), WVAL (inbuf, smb_prcnt));
|
|
|
|
(*param_len) += WVAL (inbuf, smb_prcnt);
|
|
|
|
if (WVAL (inbuf, smb_drdisp) + WVAL (inbuf, smb_drcnt) > total_data)
|
|
{
|
|
LOG (("smb_receive_trans2: invalid data block\n"));
|
|
result = -EIO;
|
|
goto fail;
|
|
}
|
|
|
|
if((*data) != NULL)
|
|
memcpy ((*data) + WVAL (inbuf, smb_drdisp), smb_base (inbuf) + WVAL (inbuf, smb_droff), WVAL (inbuf, smb_drcnt));
|
|
|
|
(*data_len) += WVAL (inbuf, smb_drcnt);
|
|
|
|
LOG (("smb_rec_trans2: drcnt/prcnt: %ld/%ld\n", WVAL (inbuf, smb_drcnt), WVAL (inbuf, smb_prcnt)));
|
|
|
|
/* parse out the total lengths again - they can shrink! */
|
|
if ((WVAL (inbuf, smb_tdrcnt) > total_data) || (WVAL (inbuf, smb_tprcnt) > total_param))
|
|
{
|
|
LOG (("smb_receive_trans2: data/params grew!\n"));
|
|
result = -EIO;
|
|
goto fail;
|
|
}
|
|
|
|
total_data = WVAL (inbuf, smb_tdrcnt);
|
|
total_param = WVAL (inbuf, smb_tprcnt);
|
|
if (total_data <= (*data_len) && total_param <= (*param_len))
|
|
break;
|
|
|
|
result = smb_receive (server, sock_fd);
|
|
if (result < 0)
|
|
goto fail;
|
|
|
|
if (server->rcls != 0)
|
|
{
|
|
/* smb_trans2_request() will check server->rcls, etc. and
|
|
* produce a matching error code value.
|
|
*/
|
|
result = -EIO;
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
LOG (("smb_receive_trans2: normal exit\n"));
|
|
return 0;
|
|
|
|
fail:
|
|
|
|
LOG (("smb_receive_trans2: failed exit\n"));
|
|
|
|
if((*param) != NULL)
|
|
free (*param);
|
|
|
|
if((*data) != NULL)
|
|
free (*data);
|
|
|
|
(*param) = (*data) = NULL;
|
|
|
|
return result;
|
|
}
|
|
|
|
int
|
|
smb_release (struct smb_server *server)
|
|
{
|
|
int result;
|
|
|
|
if (server->mount_data.fd >= 0)
|
|
CloseSocket (server->mount_data.fd);
|
|
|
|
server->mount_data.fd = socket (AF_INET, SOCK_STREAM, 0);
|
|
if (server->mount_data.fd < 0)
|
|
{
|
|
result = (-errno);
|
|
goto out;
|
|
}
|
|
|
|
result = 0;
|
|
|
|
out:
|
|
|
|
return result;
|
|
}
|
|
|
|
int
|
|
smb_connect (struct smb_server *server)
|
|
{
|
|
int sock_fd = server->mount_data.fd;
|
|
int result;
|
|
|
|
if (sock_fd < 0)
|
|
{
|
|
result = (-EBADF);
|
|
goto out;
|
|
}
|
|
|
|
result = connect (sock_fd, (struct sockaddr *)&server->mount_data.addr, sizeof(struct sockaddr_in));
|
|
if(result < 0)
|
|
result = (-errno);
|
|
|
|
out:
|
|
|
|
return(result);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* This routine was once taken from nfs, which is for udp. Here TCP does
|
|
* most of the ugly stuff for us (thanks, Alan!)
|
|
*
|
|
****************************************************************************/
|
|
|
|
/* Returns number of bytes received (>= 0) or a negative value in
|
|
* case of error.
|
|
*/
|
|
int
|
|
smb_request (struct smb_server *server)
|
|
{
|
|
int len, result;
|
|
int sock_fd = server->mount_data.fd;
|
|
unsigned char *buffer = server->packet;
|
|
|
|
if ((sock_fd < 0) || (buffer == NULL))
|
|
{
|
|
LOG (("smb_request: Bad server!\n"));
|
|
result = -EBADF;
|
|
goto out;
|
|
}
|
|
|
|
if (server->state != CONN_VALID)
|
|
{
|
|
result = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
/* Length includes the NetBIOS session header (4 bytes), which
|
|
* is prepended to the packet to be sent.
|
|
*/
|
|
len = smb_len (buffer) + 4;
|
|
|
|
LOG (("smb_request: len = %ld cmd = 0x%lx\n", len, buffer[8]));
|
|
|
|
#if defined(DUMP_SMB)
|
|
dump_netbios_header(__FILE__,__LINE__,buffer,&buffer[4],len);
|
|
dump_smb(__FILE__,__LINE__,0,buffer+4,len-4,smb_packet_from_consumer,server->max_recv);
|
|
#endif /* defined(DUMP_SMB) */
|
|
|
|
result = send (sock_fd, (void *) buffer, len, 0);
|
|
if (result < 0)
|
|
{
|
|
LOG (("smb_request: send error = %ld\n", errno));
|
|
|
|
result = (-errno);
|
|
}
|
|
else
|
|
{
|
|
result = smb_receive (server, sock_fd);
|
|
}
|
|
|
|
out:
|
|
|
|
if (result < 0)
|
|
{
|
|
server->state = CONN_INVALID;
|
|
smb_invalidate_all_inodes (server);
|
|
}
|
|
|
|
LOG (("smb_request: result = %ld\n", result));
|
|
|
|
return (result);
|
|
}
|
|
|
|
/* This is not really a trans2 request, we assume that you only have
|
|
one packet to send. */
|
|
int
|
|
smb_trans2_request (struct smb_server *server, int *data_len, int *param_len, char **data, char **param)
|
|
{
|
|
int len, result;
|
|
int sock_fd = server->mount_data.fd;
|
|
unsigned char *buffer = server->packet;
|
|
|
|
if (server->state != CONN_VALID)
|
|
{
|
|
result = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
/* Length includes the NetBIOS session header (4 bytes), which
|
|
* is prepended to the packet to be sent.
|
|
*/
|
|
len = smb_len (buffer) + 4;
|
|
|
|
LOG (("smb_request: len = %ld cmd = 0x%02lx\n", len, buffer[8]));
|
|
|
|
#if defined(DUMP_SMB)
|
|
dump_netbios_header(__FILE__,__LINE__,buffer,NULL,0);
|
|
dump_smb(__FILE__,__LINE__,0,buffer+4,len-4,smb_packet_from_consumer,server->max_recv);
|
|
#endif /* defined(DUMP_SMB) */
|
|
|
|
result = send (sock_fd, (void *) buffer, len, 0);
|
|
if (result < 0)
|
|
{
|
|
LOG (("smb_trans2_request: send error = %ld\n", errno));
|
|
|
|
result = (-errno);
|
|
}
|
|
else
|
|
{
|
|
result = smb_receive_trans2 (server, sock_fd, data_len, param_len, data, param);
|
|
}
|
|
|
|
out:
|
|
|
|
if (result < 0)
|
|
{
|
|
server->state = CONN_INVALID;
|
|
smb_invalidate_all_inodes (server);
|
|
}
|
|
|
|
LOG (("smb_trans2_request: result = %ld\n", result));
|
|
|
|
return result;
|
|
}
|
|
|
|
/* target must be in user space */
|
|
int
|
|
smb_request_read_raw (struct smb_server *server, unsigned char *target, int max_len)
|
|
{
|
|
int len, result;
|
|
int sock_fd = server->mount_data.fd;
|
|
unsigned char *buffer = server->packet;
|
|
|
|
if (server->state != CONN_VALID)
|
|
{
|
|
result = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
/* Length includes the NetBIOS session header (4 bytes), which
|
|
* is prepended to the packet to be sent.
|
|
*/
|
|
len = smb_len (buffer) + 4;
|
|
|
|
LOG (("smb_request_read_raw: len = %ld cmd = 0x%02lx\n", len, buffer[8]));
|
|
LOG (("smb_request_read_raw: target=%lx, max_len=%ld\n", (unsigned int) target, max_len));
|
|
LOG (("smb_request_read_raw: buffer=%lx, sock=%lx\n", (unsigned int) buffer, (unsigned int) sock_fd));
|
|
|
|
#if defined(DUMP_SMB)
|
|
dump_netbios_header(__FILE__,__LINE__,buffer,NULL,0);
|
|
dump_smb(__FILE__,__LINE__,0,buffer+4,len-4,smb_packet_from_consumer,server->max_recv);
|
|
#endif /* defined(DUMP_SMB) */
|
|
|
|
/* Request that data should be read in raw mode. */
|
|
result = send (sock_fd, (void *) buffer, len, 0);
|
|
|
|
LOG (("smb_request_read_raw: send returned %ld\n", result));
|
|
|
|
if (result < 0)
|
|
{
|
|
LOG (("smb_request_read_raw: send error = %ld\n", errno));
|
|
|
|
result = (-errno);
|
|
}
|
|
else
|
|
{
|
|
/* Wait for the raw data to be sent by the server. */
|
|
result = smb_receive_raw (server, sock_fd, target, max_len, 0);
|
|
}
|
|
|
|
out:
|
|
|
|
if (result < 0)
|
|
{
|
|
server->state = CONN_INVALID;
|
|
smb_invalidate_all_inodes (server);
|
|
}
|
|
|
|
LOG (("smb_request_read_raw: result = %ld\n", result));
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Source must be in user space. smb_request_write_raw assumes that
|
|
the request SMBwriteBraw has been completed successfully, so that
|
|
we can send the raw data now. */
|
|
int
|
|
smb_request_write_raw (struct smb_server *server, unsigned const char *source, int length)
|
|
{
|
|
int result;
|
|
byte nb_header[4];
|
|
int sock_fd = server->mount_data.fd;
|
|
int num_bytes_written = 0;
|
|
|
|
if (server->state != CONN_VALID)
|
|
{
|
|
result = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
/* Send the NetBIOS header. */
|
|
smb_encode_smb_length (nb_header, length);
|
|
|
|
#if defined(DUMP_SMB)
|
|
dump_netbios_header(__FILE__,__LINE__,nb_header,NULL,0);
|
|
#endif /* defined(DUMP_SMB) */
|
|
|
|
result = send (sock_fd, nb_header, 4, 0);
|
|
if (result == 4)
|
|
{
|
|
#if defined(DUMP_SMB)
|
|
dump_smb(__FILE__,__LINE__,0,source,length,smb_packet_from_consumer,server->max_recv);
|
|
#endif /* defined(DUMP_SMB) */
|
|
|
|
/* Now send the data to be written. */
|
|
result = send (sock_fd, (void *)source, length, 0);
|
|
if(result >= 0)
|
|
{
|
|
num_bytes_written = result;
|
|
|
|
/* Wait for the server to respond. */
|
|
if(!server->write_behind)
|
|
result = smb_receive (server, sock_fd);
|
|
}
|
|
else
|
|
{
|
|
result = (-errno);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(result < 0)
|
|
result = (-errno);
|
|
else
|
|
result = -EIO;
|
|
}
|
|
|
|
LOG (("smb_request_write_raw: send returned %ld\n", result));
|
|
|
|
out:
|
|
|
|
if (result >= 0)
|
|
{
|
|
result = length;
|
|
}
|
|
else
|
|
{
|
|
server->state = CONN_INVALID;
|
|
|
|
smb_invalidate_all_inodes (server);
|
|
}
|
|
|
|
LOG (("smb_request_write_raw: result = %ld\n", result));
|
|
|
|
return result;
|
|
}
|