summaryrefslogtreecommitdiff
path: root/source3/smbd/process.c
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>1998-08-17 13:11:34 +0000
committerAndrew Tridgell <tridge@samba.org>1998-08-17 13:11:34 +0000
commitc3effa8b599a6a0a2fe05487edc3a0d13e83d427 (patch)
treea5359c3b289244233855d16f0c9cabac3ae6ab6e /source3/smbd/process.c
parent0922615405838af7430ef39490ee578711866320 (diff)
downloadsamba-c3effa8b599a6a0a2fe05487edc3a0d13e83d427.tar.gz
samba-c3effa8b599a6a0a2fe05487edc3a0d13e83d427.tar.bz2
samba-c3effa8b599a6a0a2fe05487edc3a0d13e83d427.zip
this completes the splitup of server.c.
the splitup was done with an axe, not a scalpel, so there are some rough edges. I mostly wanted to get the general form right with fine tuning of what goes where to come later. Still, this is better than what we had before where server.c was a general repository for anything that didn't fit elsewhere. (This used to be commit a6d194886a4a5f7507fa37289ff96c1be56f14a6)
Diffstat (limited to 'source3/smbd/process.c')
-rw-r--r--source3/smbd/process.c810
1 files changed, 810 insertions, 0 deletions
diff --git a/source3/smbd/process.c b/source3/smbd/process.c
new file mode 100644
index 0000000000..30abfdb894
--- /dev/null
+++ b/source3/smbd/process.c
@@ -0,0 +1,810 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ process incoming packets - main loop
+ Copyright (C) Andrew Tridgell 1992-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.
+*/
+
+#include "includes.h"
+
+extern int DEBUGLEVEL;
+
+extern int last_message;
+extern int global_oplock_break;
+extern pstring sesssetup_user;
+extern char *last_inbuf;
+extern time_t smb_last_time;
+extern char *InBuffer;
+extern char *OutBuffer;
+extern int oplock_sock;
+extern int smb_read_error;
+extern BOOL reload_after_sighup;
+extern BOOL global_machine_pasword_needs_changing;
+extern fstring global_myworkgroup;
+extern pstring global_myname;
+extern int max_send;
+
+
+/****************************************************************************
+Get the next SMB packet, doing the local message processing automatically.
+****************************************************************************/
+BOOL receive_next_smb(int smbfd, int oplockfd, char *inbuf, int bufsize, int timeout)
+{
+ BOOL got_smb = False;
+ BOOL ret;
+
+ do
+ {
+ ret = receive_message_or_smb(smbfd,oplockfd,inbuf,bufsize,
+ timeout,&got_smb);
+
+ if(ret && !got_smb)
+ {
+ /* Deal with oplock break requests from other smbd's. */
+ process_local_message(oplock_sock, inbuf, bufsize);
+ continue;
+ }
+
+ if(ret && (CVAL(inbuf,0) == 0x85))
+ {
+ /* Keepalive packet. */
+ got_smb = False;
+ }
+
+ }
+ while(ret && !got_smb);
+
+ return ret;
+}
+
+
+
+/****************************************************************************
+ process an smb from the client - split out from the process() code so
+ it can be used by the oplock break code.
+****************************************************************************/
+void process_smb(char *inbuf, char *outbuf)
+{
+ extern int Client;
+#ifdef WITH_SSL
+ extern BOOL sslEnabled; /* don't use function for performance reasons */
+ static int sslConnected = 0;
+#endif /* WITH_SSL */
+ static int trans_num;
+ int msg_type = CVAL(inbuf,0);
+ int32 len = smb_len(inbuf);
+ int nread = len + 4;
+
+ if (trans_num == 0) {
+ /* on the first packet, check the global hosts allow/ hosts
+ deny parameters before doing any parsing of the packet
+ passed to us by the client. This prevents attacks on our
+ parsing code from hosts not in the hosts allow list */
+ if (!check_access(Client, lp_hostsallow(-1), lp_hostsdeny(-1))) {
+ /* send a negative session response "not listining on calling
+ name" */
+ static unsigned char buf[5] = {0x83, 0, 0, 1, 0x81};
+ DEBUG( 1, ( "Connection denied from %s\n",
+ client_addr(Client) ) );
+ send_smb(Client,(char *)buf);
+ exit_server("connection denied");
+ }
+ }
+
+ DEBUG( 6, ( "got message type 0x%x of len 0x%x\n", msg_type, len ) );
+ DEBUG( 3, ( "Transaction %d of length %d\n", trans_num, nread ) );
+
+#ifdef WITH_SSL
+ if(sslEnabled && !sslConnected){
+ sslConnected = sslutil_negotiate_ssl(Client, msg_type);
+ if(sslConnected < 0){ /* an error occured */
+ exit_server("SSL negotiation failed");
+ }else if(sslConnected){
+ trans_num++;
+ return;
+ }
+ }
+#endif /* WITH_SSL */
+
+#ifdef WITH_VTP
+ if(trans_num == 1 && VT_Check(inbuf))
+ {
+ VT_Process();
+ return;
+ }
+#endif
+
+ if (msg_type == 0)
+ show_msg(inbuf);
+ else if(msg_type == 0x85)
+ return; /* Keepalive packet. */
+
+ nread = construct_reply(inbuf,outbuf,nread,max_send);
+
+ if(nread > 0)
+ {
+ if (CVAL(outbuf,0) == 0)
+ show_msg(outbuf);
+
+ if (nread != smb_len(outbuf) + 4)
+ {
+ DEBUG(0,("ERROR: Invalid message response size! %d %d\n",
+ nread, smb_len(outbuf)));
+ }
+ else
+ send_smb(Client,outbuf);
+ }
+ trans_num++;
+}
+
+
+/*
+These flags determine some of the permissions required to do an operation
+
+Note that I don't set NEED_WRITE on some write operations because they
+are used by some brain-dead clients when printing, and I don't want to
+force write permissions on print services.
+*/
+#define AS_USER (1<<0)
+#define NEED_WRITE (1<<1)
+#define TIME_INIT (1<<2)
+#define CAN_IPC (1<<3)
+#define AS_GUEST (1<<5)
+#define QUEUE_IN_OPLOCK (1<<6)
+
+/*
+ define a list of possible SMB messages and their corresponding
+ functions. Any message that has a NULL function is unimplemented -
+ please feel free to contribute implementations!
+*/
+struct smb_message_struct
+{
+ int code;
+ char *name;
+ int (*fn)(connection_struct *conn, char *, char *, int, int);
+ int flags;
+#if PROFILING
+ unsigned long time;
+#endif
+}
+ smb_messages[] = {
+
+ /* CORE PROTOCOL */
+
+ {SMBnegprot,"SMBnegprot",reply_negprot,0},
+ {SMBtcon,"SMBtcon",reply_tcon,0},
+ {SMBtdis,"SMBtdis",reply_tdis,0},
+ {SMBexit,"SMBexit",reply_exit,0},
+ {SMBioctl,"SMBioctl",reply_ioctl,0},
+ {SMBecho,"SMBecho",reply_echo,0},
+ {SMBsesssetupX,"SMBsesssetupX",reply_sesssetup_and_X,0},
+ {SMBtconX,"SMBtconX",reply_tcon_and_X,0},
+ {SMBulogoffX, "SMBulogoffX", reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */
+ {SMBgetatr,"SMBgetatr",reply_getatr,AS_USER},
+ {SMBsetatr,"SMBsetatr",reply_setatr,AS_USER | NEED_WRITE},
+ {SMBchkpth,"SMBchkpth",reply_chkpth,AS_USER},
+ {SMBsearch,"SMBsearch",reply_search,AS_USER},
+ {SMBopen,"SMBopen",reply_open,AS_USER | QUEUE_IN_OPLOCK },
+
+ /* note that SMBmknew and SMBcreate are deliberately overloaded */
+ {SMBcreate,"SMBcreate",reply_mknew,AS_USER},
+ {SMBmknew,"SMBmknew",reply_mknew,AS_USER},
+
+ {SMBunlink,"SMBunlink",reply_unlink,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK},
+ {SMBread,"SMBread",reply_read,AS_USER},
+ {SMBwrite,"SMBwrite",reply_write,AS_USER},
+ {SMBclose,"SMBclose",reply_close,AS_USER | CAN_IPC},
+ {SMBmkdir,"SMBmkdir",reply_mkdir,AS_USER | NEED_WRITE},
+ {SMBrmdir,"SMBrmdir",reply_rmdir,AS_USER | NEED_WRITE},
+ {SMBdskattr,"SMBdskattr",reply_dskattr,AS_USER},
+ {SMBmv,"SMBmv",reply_mv,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK},
+
+ /* this is a Pathworks specific call, allowing the
+ changing of the root path */
+ {pSETDIR,"pSETDIR",reply_setdir,AS_USER},
+
+ {SMBlseek,"SMBlseek",reply_lseek,AS_USER},
+ {SMBflush,"SMBflush",reply_flush,AS_USER},
+ {SMBctemp,"SMBctemp",reply_ctemp,AS_USER | QUEUE_IN_OPLOCK },
+ {SMBsplopen,"SMBsplopen",reply_printopen,AS_USER | QUEUE_IN_OPLOCK },
+ {SMBsplclose,"SMBsplclose",reply_printclose,AS_USER},
+ {SMBsplretq,"SMBsplretq",reply_printqueue,AS_USER},
+ {SMBsplwr,"SMBsplwr",reply_printwrite,AS_USER},
+ {SMBlock,"SMBlock",reply_lock,AS_USER},
+ {SMBunlock,"SMBunlock",reply_unlock,AS_USER},
+
+ /* CORE+ PROTOCOL FOLLOWS */
+
+ {SMBreadbraw,"SMBreadbraw",reply_readbraw,AS_USER},
+ {SMBwritebraw,"SMBwritebraw",reply_writebraw,AS_USER},
+ {SMBwriteclose,"SMBwriteclose",reply_writeclose,AS_USER},
+ {SMBlockread,"SMBlockread",reply_lockread,AS_USER},
+ {SMBwriteunlock,"SMBwriteunlock",reply_writeunlock,AS_USER},
+
+ /* LANMAN1.0 PROTOCOL FOLLOWS */
+
+ {SMBreadBmpx,"SMBreadBmpx",reply_readbmpx,AS_USER},
+ {SMBreadBs,"SMBreadBs",NULL,AS_USER},
+ {SMBwriteBmpx,"SMBwriteBmpx",reply_writebmpx,AS_USER},
+ {SMBwriteBs,"SMBwriteBs",reply_writebs,AS_USER},
+ {SMBwritec,"SMBwritec",NULL,AS_USER},
+ {SMBsetattrE,"SMBsetattrE",reply_setattrE,AS_USER | NEED_WRITE},
+ {SMBgetattrE,"SMBgetattrE",reply_getattrE,AS_USER},
+ {SMBtrans,"SMBtrans",reply_trans,AS_USER | CAN_IPC},
+ {SMBtranss,"SMBtranss",NULL,AS_USER | CAN_IPC},
+ {SMBioctls,"SMBioctls",NULL,AS_USER},
+ {SMBcopy,"SMBcopy",reply_copy,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK },
+ {SMBmove,"SMBmove",NULL,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK },
+
+ {SMBopenX,"SMBopenX",reply_open_and_X,AS_USER | CAN_IPC | QUEUE_IN_OPLOCK },
+ {SMBreadX,"SMBreadX",reply_read_and_X,AS_USER | CAN_IPC },
+ {SMBwriteX,"SMBwriteX",reply_write_and_X,AS_USER},
+ {SMBlockingX,"SMBlockingX",reply_lockingX,AS_USER},
+
+ {SMBffirst,"SMBffirst",reply_search,AS_USER},
+ {SMBfunique,"SMBfunique",reply_search,AS_USER},
+ {SMBfclose,"SMBfclose",reply_fclose,AS_USER},
+
+ /* LANMAN2.0 PROTOCOL FOLLOWS */
+ {SMBfindnclose, "SMBfindnclose", reply_findnclose, AS_USER},
+ {SMBfindclose, "SMBfindclose", reply_findclose,AS_USER},
+ {SMBtrans2, "SMBtrans2", reply_trans2, AS_USER },
+ {SMBtranss2, "SMBtranss2", reply_transs2, AS_USER},
+
+ /* NT PROTOCOL FOLLOWS */
+ {SMBntcreateX, "SMBntcreateX", reply_ntcreate_and_X, AS_USER | CAN_IPC | QUEUE_IN_OPLOCK },
+ {SMBnttrans, "SMBnttrans", reply_nttrans, AS_USER | CAN_IPC },
+ {SMBnttranss, "SMBnttranss", reply_nttranss, AS_USER | CAN_IPC },
+ {SMBntcancel, "SMBntcancel", reply_ntcancel, AS_USER },
+
+ /* messaging routines */
+ {SMBsends,"SMBsends",reply_sends,AS_GUEST},
+ {SMBsendstrt,"SMBsendstrt",reply_sendstrt,AS_GUEST},
+ {SMBsendend,"SMBsendend",reply_sendend,AS_GUEST},
+ {SMBsendtxt,"SMBsendtxt",reply_sendtxt,AS_GUEST},
+
+ /* NON-IMPLEMENTED PARTS OF THE CORE PROTOCOL */
+
+ {SMBsendb,"SMBsendb",NULL,AS_GUEST},
+ {SMBfwdname,"SMBfwdname",NULL,AS_GUEST},
+ {SMBcancelf,"SMBcancelf",NULL,AS_GUEST},
+ {SMBgetmac,"SMBgetmac",NULL,AS_GUEST}
+ };
+
+/****************************************************************************
+return a string containing the function name of a SMB command
+****************************************************************************/
+char *smb_fn_name(int type)
+{
+ static char *unknown_name = "SMBunknown";
+ static int num_smb_messages =
+ sizeof(smb_messages) / sizeof(struct smb_message_struct);
+ int match;
+
+ for (match=0;match<num_smb_messages;match++)
+ if (smb_messages[match].code == type)
+ break;
+
+ if (match == num_smb_messages)
+ return(unknown_name);
+
+ return(smb_messages[match].name);
+}
+
+
+/****************************************************************************
+do a switch on the message type, and return the response size
+****************************************************************************/
+static int switch_message(int type,char *inbuf,char *outbuf,int size,int bufsize)
+{
+ static int pid= -1;
+ int outsize = 0;
+ static int num_smb_messages =
+ sizeof(smb_messages) / sizeof(struct smb_message_struct);
+ int match;
+ extern int Client;
+
+#if PROFILING
+ struct timeval msg_start_time;
+ struct timeval msg_end_time;
+ static unsigned long total_time = 0;
+
+ GetTimeOfDay(&msg_start_time);
+#endif
+
+ if (pid == -1)
+ pid = getpid();
+
+ errno = 0;
+ last_message = type;
+
+ /* make sure this is an SMB packet */
+ if (strncmp(smb_base(inbuf),"\377SMB",4) != 0)
+ {
+ DEBUG(2,("Non-SMB packet of length %d\n",smb_len(inbuf)));
+ return(-1);
+ }
+
+ for (match=0;match<num_smb_messages;match++)
+ if (smb_messages[match].code == type)
+ break;
+
+ if (match == num_smb_messages)
+ {
+ DEBUG(0,("Unknown message type %d!\n",type));
+ outsize = reply_unknown(inbuf,outbuf);
+ }
+ else
+ {
+ DEBUG(3,("switch message %s (pid %d)\n",smb_messages[match].name,pid));
+
+ if(global_oplock_break && (smb_messages[match].flags & QUEUE_IN_OPLOCK))
+ {
+ /*
+ * Queue this message as we are the process of an oplock break.
+ */
+
+ DEBUG( 2, ( "switch_message: queueing message due to being in " ) );
+ DEBUGADD( 2, ( "oplock break state.\n" ) );
+
+ push_oplock_pending_smb_message( inbuf, size );
+ return -1;
+ }
+
+ if (smb_messages[match].fn)
+ {
+ int flags = smb_messages[match].flags;
+ static uint16 last_session_tag = UID_FIELD_INVALID;
+ /* In share mode security we must ignore the vuid. */
+ uint16 session_tag = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID : SVAL(inbuf,smb_uid);
+ connection_struct *conn = conn_find(SVAL(inbuf,smb_tid));
+
+
+ /* Ensure this value is replaced in the incoming packet. */
+ SSVAL(inbuf,smb_uid,session_tag);
+
+ /*
+ * Ensure the correct username is in sesssetup_user.
+ * This is a really ugly bugfix for problems with
+ * multiple session_setup_and_X's being done and
+ * allowing %U and %G substitutions to work correctly.
+ * There is a reason this code is done here, don't
+ * move it unless you know what you're doing... :-).
+ * JRA.
+ */
+ if (session_tag != last_session_tag) {
+ user_struct *vuser = NULL;
+
+ last_session_tag = session_tag;
+ if(session_tag != UID_FIELD_INVALID)
+ vuser = get_valid_user_struct(session_tag);
+ if(vuser != NULL)
+ pstrcpy( sesssetup_user, vuser->requested_name);
+ }
+
+ /* does this protocol need to be run as root? */
+ if (!(flags & AS_USER))
+ unbecome_user();
+
+ /* does this protocol need to be run as the connected user? */
+ if ((flags & AS_USER) && !become_user(conn,session_tag)) {
+ if (flags & AS_GUEST)
+ flags &= ~AS_USER;
+ else
+ return(ERROR(ERRSRV,ERRinvnid));
+ }
+ /* this code is to work around a bug is MS client 3 without
+ introducing a security hole - it needs to be able to do
+ print queue checks as guest if it isn't logged in properly */
+ if (flags & AS_USER)
+ flags &= ~AS_GUEST;
+
+ /* does it need write permission? */
+ if ((flags & NEED_WRITE) && !CAN_WRITE(conn))
+ return(ERROR(ERRSRV,ERRaccess));
+
+ /* ipc services are limited */
+ if (IS_IPC(conn) && (flags & AS_USER) && !(flags & CAN_IPC)) {
+ return(ERROR(ERRSRV,ERRaccess));
+ }
+
+ /* load service specific parameters */
+ if (conn &&
+ !become_service(conn,(flags & AS_USER)?True:False)) {
+ return(ERROR(ERRSRV,ERRaccess));
+ }
+
+ /* does this protocol need to be run as guest? */
+ if ((flags & AS_GUEST) &&
+ (!become_guest() ||
+ !check_access(Client, lp_hostsallow(-1), lp_hostsdeny(-1)))) {
+ return(ERROR(ERRSRV,ERRaccess));
+ }
+
+ last_inbuf = inbuf;
+
+ outsize = smb_messages[match].fn(conn, inbuf,outbuf,size,bufsize);
+ }
+ else
+ {
+ outsize = reply_unknown(inbuf,outbuf);
+ }
+ }
+
+#if PROFILING
+ GetTimeOfDay(&msg_end_time);
+ if (!(smb_messages[match].flags & TIME_INIT))
+ {
+ smb_messages[match].time = 0;
+ smb_messages[match].flags |= TIME_INIT;
+ }
+ {
+ unsigned long this_time =
+ (msg_end_time.tv_sec - msg_start_time.tv_sec)*1e6 +
+ (msg_end_time.tv_usec - msg_start_time.tv_usec);
+ smb_messages[match].time += this_time;
+ total_time += this_time;
+ }
+ DEBUG(2,("TIME %s %d usecs %g pct\n",
+ smb_fn_name(type),smb_messages[match].time,
+ (100.0*smb_messages[match].time) / total_time));
+#endif
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ construct a chained reply and add it to the already made reply
+ **************************************************************************/
+int chain_reply(char *inbuf,char *outbuf,int size,int bufsize)
+{
+ static char *orig_inbuf;
+ static char *orig_outbuf;
+ int smb_com1, smb_com2 = CVAL(inbuf,smb_vwv0);
+ unsigned smb_off2 = SVAL(inbuf,smb_vwv1);
+ char *inbuf2, *outbuf2;
+ int outsize2;
+ char inbuf_saved[smb_wct];
+ char outbuf_saved[smb_wct];
+ extern int chain_size;
+ int wct = CVAL(outbuf,smb_wct);
+ int outsize = smb_size + 2*wct + SVAL(outbuf,smb_vwv0+2*wct);
+
+ /* maybe its not chained */
+ if (smb_com2 == 0xFF) {
+ CVAL(outbuf,smb_vwv0) = 0xFF;
+ return outsize;
+ }
+
+ if (chain_size == 0) {
+ /* this is the first part of the chain */
+ orig_inbuf = inbuf;
+ orig_outbuf = outbuf;
+ }
+
+ /* we need to tell the client where the next part of the reply will be */
+ SSVAL(outbuf,smb_vwv1,smb_offset(outbuf+outsize,outbuf));
+ CVAL(outbuf,smb_vwv0) = smb_com2;
+
+ /* remember how much the caller added to the chain, only counting stuff
+ after the parameter words */
+ chain_size += outsize - smb_wct;
+
+ /* work out pointers into the original packets. The
+ headers on these need to be filled in */
+ inbuf2 = orig_inbuf + smb_off2 + 4 - smb_wct;
+ outbuf2 = orig_outbuf + SVAL(outbuf,smb_vwv1) + 4 - smb_wct;
+
+ /* remember the original command type */
+ smb_com1 = CVAL(orig_inbuf,smb_com);
+
+ /* save the data which will be overwritten by the new headers */
+ memcpy(inbuf_saved,inbuf2,smb_wct);
+ memcpy(outbuf_saved,outbuf2,smb_wct);
+
+ /* give the new packet the same header as the last part of the SMB */
+ memmove(inbuf2,inbuf,smb_wct);
+
+ /* create the in buffer */
+ CVAL(inbuf2,smb_com) = smb_com2;
+
+ /* create the out buffer */
+ bzero(outbuf2,smb_size);
+ set_message(outbuf2,0,0,True);
+ CVAL(outbuf2,smb_com) = CVAL(inbuf2,smb_com);
+
+ memcpy(outbuf2+4,inbuf2+4,4);
+ CVAL(outbuf2,smb_rcls) = SMB_SUCCESS;
+ CVAL(outbuf2,smb_reh) = 0;
+ CVAL(outbuf2,smb_flg) = 0x80 | (CVAL(inbuf2,smb_flg) & 0x8); /* bit 7 set
+ means a reply */
+ SSVAL(outbuf2,smb_flg2,1); /* say we support long filenames */
+ SSVAL(outbuf2,smb_err,SMB_SUCCESS);
+ SSVAL(outbuf2,smb_tid,SVAL(inbuf2,smb_tid));
+ SSVAL(outbuf2,smb_pid,SVAL(inbuf2,smb_pid));
+ SSVAL(outbuf2,smb_uid,SVAL(inbuf2,smb_uid));
+ SSVAL(outbuf2,smb_mid,SVAL(inbuf2,smb_mid));
+
+ DEBUG(3,("Chained message\n"));
+ show_msg(inbuf2);
+
+ /* process the request */
+ outsize2 = switch_message(smb_com2,inbuf2,outbuf2,size-chain_size,
+ bufsize-chain_size);
+
+ /* copy the new reply and request headers over the old ones, but
+ preserve the smb_com field */
+ memmove(orig_outbuf,outbuf2,smb_wct);
+ CVAL(orig_outbuf,smb_com) = smb_com1;
+
+ /* restore the saved data, being careful not to overwrite any
+ data from the reply header */
+ memcpy(inbuf2,inbuf_saved,smb_wct);
+ {
+ int ofs = smb_wct - PTR_DIFF(outbuf2,orig_outbuf);
+ if (ofs < 0) ofs = 0;
+ memmove(outbuf2+ofs,outbuf_saved+ofs,smb_wct-ofs);
+ }
+
+ return outsize2;
+}
+
+
+/****************************************************************************
+ Helper function for contruct_reply.
+****************************************************************************/
+
+void construct_reply_common(char *inbuf,char *outbuf)
+{
+ bzero(outbuf,smb_size);
+
+ CVAL(outbuf,smb_com) = CVAL(inbuf,smb_com);
+ set_message(outbuf,0,0,True);
+
+ memcpy(outbuf+4,inbuf+4,4);
+ CVAL(outbuf,smb_rcls) = SMB_SUCCESS;
+ CVAL(outbuf,smb_reh) = 0;
+ CVAL(outbuf,smb_flg) = 0x80 | (CVAL(inbuf,smb_flg) & 0x8); /* bit 7 set
+ means a reply */
+ SSVAL(outbuf,smb_flg2,1); /* say we support long filenames */
+ SSVAL(outbuf,smb_err,SMB_SUCCESS);
+ SSVAL(outbuf,smb_tid,SVAL(inbuf,smb_tid));
+ SSVAL(outbuf,smb_pid,SVAL(inbuf,smb_pid));
+ SSVAL(outbuf,smb_uid,SVAL(inbuf,smb_uid));
+ SSVAL(outbuf,smb_mid,SVAL(inbuf,smb_mid));
+}
+
+/****************************************************************************
+ construct a reply to the incoming packet
+****************************************************************************/
+int construct_reply(char *inbuf,char *outbuf,int size,int bufsize)
+{
+ int type = CVAL(inbuf,smb_com);
+ int outsize = 0;
+ int msg_type = CVAL(inbuf,0);
+ extern int chain_size;
+
+ smb_last_time = time(NULL);
+
+ chain_size = 0;
+ file_chain_reset();
+ reset_chain_p();
+
+ if (msg_type != 0)
+ return(reply_special(inbuf,outbuf));
+
+ construct_reply_common(inbuf, outbuf);
+
+ outsize = switch_message(type,inbuf,outbuf,size,bufsize);
+
+ outsize += chain_size;
+
+ if(outsize > 4)
+ smb_setlen(outbuf,outsize - 4);
+ return(outsize);
+}
+
+/****************************************************************************
+ process commands from the client
+****************************************************************************/
+void smbd_process(void)
+{
+ extern int Client;
+
+ InBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+ OutBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+ if ((InBuffer == NULL) || (OutBuffer == NULL))
+ return;
+
+ InBuffer += SMB_ALIGNMENT;
+ OutBuffer += SMB_ALIGNMENT;
+
+#if PRIME_NMBD
+ DEBUG(3,("priming nmbd\n"));
+ {
+ struct in_addr ip;
+ ip = *interpret_addr2("localhost");
+ if (zero_ip(ip)) ip = *interpret_addr2("127.0.0.1");
+ *OutBuffer = 0;
+ send_one_packet(OutBuffer,1,ip,NMB_PORT,SOCK_DGRAM);
+ }
+#endif
+
+ /* re-initialise the timezone */
+ TimeInit();
+
+ while (True)
+ {
+ int deadtime = lp_deadtime()*60;
+ int counter;
+ int last_keepalive=0;
+ int service_load_counter = 0;
+ BOOL got_smb = False;
+
+ if (deadtime <= 0)
+ deadtime = DEFAULT_SMBD_TIMEOUT;
+
+#if USE_READ_PREDICTION
+ if (lp_readprediction())
+ do_read_prediction();
+#endif
+
+ errno = 0;
+
+ for (counter=SMBD_SELECT_LOOP;
+ !receive_message_or_smb(Client,oplock_sock,
+ InBuffer,BUFFER_SIZE,SMBD_SELECT_LOOP*1000,&got_smb);
+ counter += SMBD_SELECT_LOOP)
+ {
+ time_t t;
+ BOOL allidle = True;
+ extern int keepalive;
+
+ if (counter > 365 * 3600) /* big number of seconds. */
+ {
+ counter = 0;
+ service_load_counter = 0;
+ }
+
+ if (smb_read_error == READ_EOF)
+ {
+ DEBUG(3,("end of file from client\n"));
+ return;
+ }
+
+ if (smb_read_error == READ_ERROR)
+ {
+ DEBUG(3,("receive_smb error (%s) exiting\n",
+ strerror(errno)));
+ return;
+ }
+
+ t = time(NULL);
+
+ /* become root again if waiting */
+ unbecome_user();
+
+ /* check for smb.conf reload */
+ if (counter >= service_load_counter + SMBD_RELOAD_CHECK)
+ {
+ service_load_counter = counter;
+
+ /* reload services, if files have changed. */
+ reload_services(True);
+ }
+
+ /*
+ * If reload_after_sighup == True then we got a SIGHUP
+ * and are being asked to reload. Fix from <branko.cibej@hermes.si>
+ */
+
+ if (reload_after_sighup)
+ {
+ DEBUG(0,("Reloading services after SIGHUP\n"));
+ reload_services(False);
+ reload_after_sighup = False;
+ }
+
+ /* automatic timeout if all connections are closed */
+ if (conn_num_open()==0 && counter >= IDLE_CLOSED_TIMEOUT)
+ {
+ DEBUG( 2, ( "Closing idle connection\n" ) );
+ return;
+ }
+
+ if (keepalive && (counter-last_keepalive)>keepalive)
+ {
+ struct cli_state *cli = server_client();
+ if (!send_keepalive(Client)) {
+ DEBUG( 2, ( "Keepalive failed - exiting.\n" ) );
+ return;
+ }
+ /* also send a keepalive to the password server if its still
+ connected */
+ if (cli && cli->initialised)
+ send_keepalive(cli->fd);
+ last_keepalive = counter;
+ }
+
+ /* check for connection timeouts */
+ allidle = conn_idle_all(t, deadtime);
+
+ if (allidle && conn_num_open()>0) {
+ DEBUG(2,("Closing idle connection 2.\n"));
+ return;
+ }
+
+ if(global_machine_pasword_needs_changing)
+ {
+ unsigned char trust_passwd_hash[16];
+ time_t lct;
+ pstring remote_machine_list;
+
+ /*
+ * We're in domain level security, and the code that
+ * read the machine password flagged that the machine
+ * password needs changing.
+ */
+
+ /*
+ * First, open the machine password file with an exclusive lock.
+ */
+
+ if(!trust_password_lock( global_myworkgroup, global_myname, True)) {
+ DEBUG(0,("process: unable to open the machine account password file for \
+machine %s in domain %s.\n", global_myname, global_myworkgroup ));
+ continue;
+ }
+
+ if(!get_trust_account_password( trust_passwd_hash, &lct)) {
+ DEBUG(0,("process: unable to read the machine account password for \
+machine %s in domain %s.\n", global_myname, global_myworkgroup ));
+ trust_password_unlock();
+ continue;
+ }
+
+ /*
+ * Make sure someone else hasn't already done this.
+ */
+
+ if(t < lct + lp_machine_password_timeout()) {
+ trust_password_unlock();
+ global_machine_pasword_needs_changing = False;
+ continue;
+ }
+
+ pstrcpy(remote_machine_list, lp_passwordserver());
+
+ change_trust_account_password( global_myworkgroup, remote_machine_list);
+ trust_password_unlock();
+ global_machine_pasword_needs_changing = False;
+ }
+
+ /*
+ * Check to see if we have any change notifies
+ * outstanding on the queue.
+ */
+ process_pending_change_notify_queue(t);
+ }
+
+ if(got_smb)
+ process_smb(InBuffer, OutBuffer);
+ else
+ process_local_message(oplock_sock, InBuffer, BUFFER_SIZE);
+ }
+}