/* Unix SMB/Netbios implementation. Version 1.9. SMB client generic functions Copyright (C) Andrew Tridgell 1994-1998 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 the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #define NO_SYSLOG #include "includes.h" extern int DEBUGLEVEL; static void cli_process_oplock(struct cli_state *cli); /* * Change the port number used to call on */ int cli_set_port(struct cli_state *cli, int port) { if (port > 0) cli->port = port; return cli->port; } /**************************************************************************** recv an smb ****************************************************************************/ BOOL cli_receive_smb(struct cli_state *cli) { BOOL ret; again: ret = client_receive_smb(cli->fd,cli->inbuf,cli->timeout); if (ret) { /* 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) { if (cli->use_oplocks) cli_process_oplock(cli); /* try to prevent loops */ CVAL(cli->inbuf,smb_com) = 0xFF; goto again; } } return ret; } /**************************************************************************** send an smb to a fd and re-establish if necessary ****************************************************************************/ BOOL cli_send_smb(struct cli_state *cli) { size_t len; size_t nwritten=0; ssize_t ret; BOOL reestablished=False; len = smb_len(cli->outbuf) + 4; while (nwritten < len) { ret = write_socket(cli->fd,cli->outbuf+nwritten,len - nwritten); if (ret <= 0 && errno == EPIPE && !reestablished) { if (cli_reestablish_connection(cli)) { reestablished = True; nwritten=0; continue; } } if (ret <= 0) { DEBUG(0,("Error writing %d bytes to client. %d\n", (int)len,(int)ret)); return False; } nwritten += ret; } return True; } /**************************************************************************** setup basics in a outgoing packet ****************************************************************************/ void cli_setup_packet(struct cli_state *cli) { cli->rap_error = 0; cli->nt_error = 0; SSVAL(cli->outbuf,smb_pid,cli->pid); SSVAL(cli->outbuf,smb_uid,cli->vuid); SSVAL(cli->outbuf,smb_mid,cli->mid); if (cli->protocol > PROTOCOL_CORE) { uint16 flags2; SCVAL(cli->outbuf,smb_flg,0x8); flags2 = FLAGS2_LONG_PATH_COMPONENTS; if (cli->capabilities & CAP_UNICODE) { flags2 |= FLAGS2_UNICODE_STRINGS; } SSVAL(cli->outbuf,smb_flg2, flags2); } } /**************************************************************************** setup the bcc length of the packet from a pointer to the end of the data ****************************************************************************/ void cli_setup_bcc(struct cli_state *cli, void *p) { set_message_bcc(cli->outbuf, PTR_DIFF(p, smb_buf(cli->outbuf))); } /**************************************************************************** process an oplock break request from the server ****************************************************************************/ static void cli_process_oplock(struct cli_state *cli) { char *oldbuf = cli->outbuf; pstring buf; int fnum; unsigned char level; fnum = SVAL(cli->inbuf,smb_vwv2); level = CVAL(cli->inbuf,smb_vwv3+1); /* damn, we really need to keep a record of open files so we can detect a oplock break and a close crossing on the wire. for now this swallows the errors */ if (fnum == 0) return; /* Ignore level II break to none's. */ if (level == OPLOCKLEVEL_NONE) return; cli->outbuf = buf; memset(buf,'\0',smb_size); set_message(buf,8,0,True); CVAL(buf,smb_com) = SMBlockingX; SSVAL(buf,smb_tid, cli->cnum); cli_setup_packet(cli); SSVAL(buf,smb_vwv0,0xFF); SSVAL(buf,smb_vwv1,0); SSVAL(buf,smb_vwv2,fnum); if (cli->use_level_II_oplocks) SSVAL(buf,smb_vwv3,0x102); /* levelII oplock break ack */ else SSVAL(buf,smb_vwv3,2); /* exclusive oplock break ack */ SIVAL(buf,smb_vwv4,0); /* timoeut */ SSVAL(buf,smb_vwv6,0); /* unlockcount */ SSVAL(buf,smb_vwv7,0); /* lockcount */ cli_send_smb(cli); cli->outbuf = oldbuf; } /**************************************************************************** initialise a client structure ****************************************************************************/ void cli_init_creds(struct cli_state *cli, const struct ntuser_creds *usr) { /* copy_nt_creds(&cli->usr, usr); */ safe_strcpy(cli->domain , usr->domain , sizeof(usr->domain )-1); safe_strcpy(cli->user_name, usr->user_name, sizeof(usr->user_name)-1); memcpy(&cli->pwd, &usr->pwd, sizeof(usr->pwd)); cli->ntlmssp_flags = usr->ntlmssp_flags; cli->ntlmssp_cli_flgs = usr != NULL ? usr->ntlmssp_flags : 0; DEBUG(10,("cli_init_creds: user %s domain %s flgs: %x\nntlmssp_cli_flgs:%x\n", cli->user_name, cli->domain, cli->ntlmssp_flags,cli->ntlmssp_cli_flgs)); } /**************************************************************************** initialise a client structure ****************************************************************************/ struct cli_state *cli_initialise(struct cli_state *cli) { if (!cli) { cli = (struct cli_state *)malloc(sizeof(*cli)); if (!cli) return NULL; ZERO_STRUCTP(cli); } if (cli->initialised) { cli_shutdown(cli); } ZERO_STRUCTP(cli); cli->port = 0; cli->fd = -1; cli->cnum = -1; cli->pid = (uint16)sys_getpid(); cli->mid = 1; cli->vuid = UID_FIELD_INVALID; cli->protocol = PROTOCOL_NT1; cli->timeout = 20000; /* Timeout is in milliseconds. */ cli->bufsize = CLI_BUFFER_SIZE+4; cli->max_xmit = cli->bufsize; cli->outbuf = (char *)malloc(cli->bufsize); cli->inbuf = (char *)malloc(cli->bufsize); if (!cli->outbuf || !cli->inbuf) { return NULL; } if ((cli->mem_ctx = talloc_init()) == NULL) { free(cli->outbuf); free(cli->inbuf); return NULL; } memset(cli->outbuf, '\0', cli->bufsize); memset(cli->inbuf, '\0', cli->bufsize); cli->initialised = 1; return cli; } /**************************************************************************** shutdown a client structure ****************************************************************************/ void cli_shutdown(struct cli_state *cli) { if (cli->outbuf) { free(cli->outbuf); } if (cli->inbuf) { free(cli->inbuf); } if (cli->mem_ctx) talloc_destroy(cli->mem_ctx); #ifdef WITH_SSL if (cli->fd != -1) sslutil_disconnect(cli->fd); #endif /* WITH_SSL */ if (cli->fd != -1) close(cli->fd); memset(cli, 0, sizeof(*cli)); } /**************************************************************************** set socket options on a open connection ****************************************************************************/ void cli_sockopt(struct cli_state *cli, char *options) { set_socket_options(cli->fd, options); } /**************************************************************************** set the PID to use for smb messages. Return the old pid. ****************************************************************************/ uint16 cli_setpid(struct cli_state *cli, uint16 pid) { uint16 ret = cli->pid; cli->pid = pid; return ret; }