static void construct_reply_common(struct smb_request *req, const char *inbuf,
char *outbuf);
+bool smbd_lock_socket(struct smbd_server_connection *sconn)
+{
+ bool ok;
+
+ if (smbd_server_conn->smb1.echo_handler.socket_lock_fd == -1) {
+ return true;
+ }
+
+ DEBUG(10,("pid[%d] wait for socket lock\n", (int)sys_getpid()));
+
+ ok = fcntl_lock(smbd_server_conn->smb1.echo_handler.socket_lock_fd,
+ SMB_F_SETLKW, 0, 0, F_WRLCK);
+ if (!ok) {
+ return false;
+ }
+
+ DEBUG(10,("pid[%d] got for socket lock\n", (int)sys_getpid()));
+
+ return true;
+}
+
+bool smbd_unlock_socket(struct smbd_server_connection *sconn)
+{
+ bool ok;
+
+ if (smbd_server_conn->smb1.echo_handler.socket_lock_fd == -1) {
+ return true;
+ }
+
+ ok = fcntl_lock(smbd_server_conn->smb1.echo_handler.socket_lock_fd,
+ SMB_F_SETLKW, 0, 0, F_UNLCK);
+ if (!ok) {
+ return false;
+ }
+
+ DEBUG(10,("pid[%d] unlocked socket\n", (int)sys_getpid()));
+
+ return true;
+}
+
/* Accessor function for smb_read_error for smbd functions. */
/****************************************************************************
Send an smb to a fd.
****************************************************************************/
-bool srv_send_smb(int fd, char *buffer, bool do_encrypt)
+bool srv_send_smb(int fd, char *buffer,
+ bool do_signing, uint32_t seqnum,
+ bool do_encrypt,
+ struct smb_perfcount_data *pcd)
{
- size_t len;
+ size_t len = 0;
size_t nwritten=0;
ssize_t ret;
char *buf_out = buffer;
+ bool ok;
- /* Sign the outgoing packet if required. */
- srv_calculate_sign_mac(buf_out);
+ ok = smbd_lock_socket(smbd_server_conn);
+ if (!ok) {
+ exit_server_cleanly("failed to lock socket");
+ }
+
+ if (do_signing) {
+ /* Sign the outgoing packet if required. */
+ srv_calculate_sign_mac(smbd_server_conn, buf_out, seqnum);
+ }
if (do_encrypt) {
NTSTATUS status = srv_encrypt_buffer(buffer, &buf_out);
DEBUG(0, ("send_smb: SMB encryption failed "
"on outgoing packet! Error %s\n",
nt_errstr(status) ));
- return false;
+ goto out;
}
}
while (nwritten < len) {
ret = write_data(fd,buf_out+nwritten,len - nwritten);
if (ret <= 0) {
- DEBUG(0,("Error writing %d bytes to client. %d. (%s)\n",
- (int)len,(int)ret, strerror(errno) ));
+ DEBUG(0,("pid[%d] Error writing %d bytes to client. %d. (%s)\n",
+ (int)sys_getpid(), (int)len,(int)ret, strerror(errno) ));
srv_free_enc_buffer(buf_out);
- return false;
+ goto out;
}
nwritten += ret;
}
+ SMB_PERFCOUNT_SET_MSGLEN_OUT(pcd, len);
srv_free_enc_buffer(buf_out);
+out:
+ SMB_PERFCOUNT_END(pcd);
+
+ ok = smbd_unlock_socket(smbd_server_conn);
+ if (!ok) {
+ exit_server_cleanly("failed to unlock socket");
+ }
+
return true;
}
return NT_STATUS_OK;
}
- return read_socket_with_timeout(fd, buffer, len, len, timeout, NULL);
+ return read_fd_with_timeout(fd, buffer, len, len, timeout, NULL);
}
/****************************************************************************
memcpy(writeX_header, lenbuf, 4);
- status = read_socket_with_timeout(
+ status = read_fd_with_timeout(
fd, writeX_header + 4,
STANDARD_WRITE_AND_X_HEADER_SIZE,
STANDARD_WRITE_AND_X_HEADER_SIZE,
return status;
}
- if (CVAL(lenbuf,0) == 0 &&
- min_recv_size &&
- smb_len_large(lenbuf) > (min_recv_size + STANDARD_WRITE_AND_X_HEADER_SIZE) && /* Could be a UNIX large writeX. */
- !srv_is_signing_active()) {
+ if (CVAL(lenbuf,0) == 0 && min_recv_size &&
+ (smb_len_large(lenbuf) > /* Could be a UNIX large writeX. */
+ (min_recv_size + STANDARD_WRITE_AND_X_HEADER_SIZE)) &&
+ !srv_is_signing_active(smbd_server_conn) &&
+ smbd_server_conn->smb1.echo_handler.trusted_fde == NULL) {
return receive_smb_raw_talloc_partial_read(
mem_ctx, lenbuf, fd, buffer, timeout, p_unread, plen);
static NTSTATUS receive_smb_talloc(TALLOC_CTX *mem_ctx, int fd,
char **buffer, unsigned int timeout,
size_t *p_unread, bool *p_encrypted,
- size_t *p_len)
+ size_t *p_len,
+ uint32_t *seqnum,
+ bool trusted_channel)
{
size_t len = 0;
NTSTATUS status;
}
/* Check the incoming SMB signature. */
- if (!srv_check_sign_mac(*buffer, true)) {
+ if (!srv_check_sign_mac(smbd_server_conn, *buffer, seqnum, trusted_channel)) {
DEBUG(0, ("receive_smb: SMB Signature verification failed on "
"incoming packet!\n"));
return NT_STATUS_INVALID_NETWORK_RESPONSE;
req->flags2 = SVAL(inbuf, smb_flg2);
req->smbpid = SVAL(inbuf, smb_pid);
req->mid = SVAL(inbuf, smb_mid);
+ req->seqnum = 0;
req->vuid = SVAL(inbuf, smb_uid);
req->tid = SVAL(inbuf, smb_tid);
req->wct = CVAL(inbuf, smb_wct);
req->conn = conn_find(req->tid);
req->chain_fsp = NULL;
req->chain_outbuf = NULL;
+ req->done = false;
+ smb_init_perfcount_data(&req->pcd);
/* Ensure we have at least wct words and 2 bytes of bcc. */
if (smb_size + req->wct*2 > req_size) {
(unsigned int)req_size));
exit_server_cleanly("Invalid SMB request");
}
+
req->outbuf = NULL;
}
static void process_smb(struct smbd_server_connection *conn,
uint8_t *inbuf, size_t nread, size_t unread_bytes,
- bool encrypted);
+ uint32_t seqnum, bool encrypted,
+ struct smb_perfcount_data *deferred_pcd);
static void smbd_deferred_open_timer(struct event_context *ev,
struct timed_event *te,
struct pending_message_list *msg = talloc_get_type(private_data,
struct pending_message_list);
TALLOC_CTX *mem_ctx = talloc_tos();
+ uint16_t mid = SVAL(msg->buf.data,smb_mid);
uint8_t *inbuf;
inbuf = (uint8_t *)talloc_memdup(mem_ctx, msg->buf.data,
/* We leave this message on the queue so the open code can
know this is a retry. */
DEBUG(5,("smbd_deferred_open_timer: trigger mid %u.\n",
- (unsigned int)SVAL(msg->buf.data,smb_mid)));
+ (unsigned int)mid));
+
+ /* Mark the message as processed so this is not
+ * re-processed in error. */
+ msg->processed = true;
process_smb(smbd_server_conn, inbuf,
msg->buf.length, 0,
- msg->encrypted);
+ msg->seqnum, msg->encrypted, &msg->pcd);
+
+ /* If it's still there and was processed, remove it. */
+ msg = get_open_deferred_message(mid);
+ if (msg && msg->processed) {
+ remove_deferred_open_smb_message(mid);
+ }
}
/****************************************************************************
}
msg->request_time = request_time;
+ msg->seqnum = req->seqnum;
msg->encrypted = req->encrypted;
+ msg->processed = false;
+ SMB_PERFCOUNT_DEFER_OP(&req->pcd, &msg->pcd);
if (private_data) {
msg->private_data = data_blob_talloc(msg, private_data,
for (pml = deferred_open_queue; pml; pml = pml->next) {
if (mid == SVAL(pml->buf.data,smb_mid)) {
- DEBUG(10,("remove_sharing_violation_open_smb_message: "
+ DEBUG(10,("remove_deferred_open_smb_message: "
"deleting mid %u len %u\n",
(unsigned int)mid,
(unsigned int)pml->buf.length ));
if (mid == msg_mid) {
struct timed_event *te;
+ if (pml->processed) {
+ /* A processed message should not be
+ * rescheduled. */
+ DEBUG(0,("schedule_deferred_open_smb_message: LOGIC ERROR "
+ "message mid %u was already processed\n",
+ msg_mid ));
+ continue;
+ }
+
DEBUG(10,("schedule_deferred_open_smb_message: scheduling mid %u\n",
mid ));
}
/****************************************************************************
- Return true if this mid is on the deferred queue.
+ Return true if this mid is on the deferred queue and was not yet processed.
****************************************************************************/
bool open_was_deferred(uint16 mid)
struct pending_message_list *pml;
for (pml = deferred_open_queue; pml; pml = pml->next) {
- if (SVAL(pml->buf.data,smb_mid) == mid) {
+ if (SVAL(pml->buf.data,smb_mid) == mid && !pml->processed) {
return True;
}
}
}
}
-/****************************************************************************
- Do all async processing in here. This includes kernel oplock messages, change
- notify events etc.
-****************************************************************************/
-
-static void async_processing(void)
-{
- DEBUG(10,("async_processing: Doing async processing.\n"));
-
- process_aio_queue();
-
- process_kernel_oplocks(smbd_messaging_context());
-
- /* Do the aio check again after receive_local_message as it does a
- select and may have eaten our signal. */
- /* Is this till true? -- vl */
- process_aio_queue();
-}
-
-/****************************************************************************
- Do a select on an two fd's - with timeout.
-
- If a local udp message has been pushed onto the
- queue (this can only happen during oplock break
- processing) call async_processing()
-
- If a pending smb message has been pushed onto the
- queue (this can only happen during oplock break
- processing) return this next.
-
- If the first smbfd is ready then read an smb from it.
- if the second (loopback UDP) fd is ready then read a message
- from it and setup the buffer header to identify the length
- and from address.
- Returns False on timeout or error.
- Else returns True.
-
-The timeout is in milliseconds
-****************************************************************************/
-
static NTSTATUS smbd_server_connection_loop_once(struct smbd_server_connection *conn)
{
fd_set r_fds, w_fds;
FD_ZERO(&r_fds);
FD_ZERO(&w_fds);
- /*
- * Ensure we process oplock break messages by preference.
- * We have to do this before the select, after the select
- * and if the select returns EINTR. This is due to the fact
- * that the selects called from async_processing can eat an EINTR
- * caused by a signal (we can't take the break message there).
- * This is hideously complex - *MUST* be simplified for 3.0 ! JRA.
- */
-
- if (oplock_message_waiting()) {
- DEBUG(10,("receive_message_or_smb: oplock_message is waiting.\n"));
- async_processing();
- /*
- * After async processing we must go and do the select again, as
- * the state of the flag in fds for the server file descriptor is
- * indeterminate - we may have done I/O on it in the oplock processing. JRA.
- */
- return NT_STATUS_RETRY;
- }
-
/*
* Are there any timed events waiting ? If so, ensure we don't
* select for longer than it would take to wait for them.
errno = sav;
}
- if (run_events(smbd_event_context(), selrtn, &r_fds, &w_fds)) {
- return NT_STATUS_RETRY;
- }
-
- /* if we get EINTR then maybe we have received an oplock
- signal - treat this as select returning 1. This is ugly, but
- is the best we can do until the oplock code knows more about
- signals */
- if (selrtn == -1 && errno == EINTR) {
- async_processing();
+ if ((conn->smb1.echo_handler.trusted_fd != -1)
+ && FD_ISSET(smbd_server_fd(), &r_fds)
+ && FD_ISSET(conn->smb1.echo_handler.trusted_fd, &r_fds)) {
/*
- * After async processing we must go and do the select again, as
- * the state of the flag in fds for the server file descriptor is
- * indeterminate - we may have done I/O on it in the oplock processing. JRA.
+ * Prefer to read pending requests from the echo handler. To
+ * quote Jeremy (da70f8ab1): This is a hack of monstrous
+ * proportions...
*/
+ FD_CLR(smbd_server_fd(), &r_fds);
+ }
+
+ if (run_events(smbd_event_context(), selrtn, &r_fds, &w_fds)) {
return NT_STATUS_RETRY;
}
/* 0x30 */ { NULL, NULL, 0 },
/* 0x31 */ { NULL, NULL, 0 },
/* 0x32 */ { "SMBtrans2",reply_trans2, AS_USER | CAN_IPC },
-/* 0x33 */ { "SMBtranss2",reply_transs2, AS_USER},
+/* 0x33 */ { "SMBtranss2",reply_transs2, AS_USER | CAN_IPC },
/* 0x34 */ { "SMBfindclose",reply_findclose,AS_USER},
/* 0x35 */ { "SMBfindnclose",reply_findnclose,AS_USER},
/* 0x36 */ { NULL, NULL, 0 },
if (!change_to_user(conn,session_tag)) {
reply_nterror(req, NT_STATUS_DOS(ERRSRV, ERRbaduid));
- remove_deferred_open_smb_message(req->mid);
return conn;
}
Construct a reply to the incoming packet.
****************************************************************************/
-static void construct_reply(char *inbuf, int size, size_t unread_bytes, bool encrypted)
+static void construct_reply(char *inbuf, int size, size_t unread_bytes,
+ uint32_t seqnum, bool encrypted,
+ struct smb_perfcount_data *deferred_pcd)
{
connection_struct *conn;
struct smb_request *req;
- chain_size = 0;
-
if (!(req = talloc(talloc_tos(), struct smb_request))) {
smb_panic("could not allocate smb_request");
}
+
init_smb_request(req, (uint8 *)inbuf, unread_bytes, encrypted);
req->inbuf = (uint8_t *)talloc_move(req, &inbuf);
+ req->seqnum = seqnum;
+
+ /* we popped this message off the queue - keep original perf data */
+ if (deferred_pcd)
+ req->pcd = *deferred_pcd;
+ else {
+ SMB_PERFCOUNT_START(&req->pcd);
+ SMB_PERFCOUNT_SET_OP(&req->pcd, req->cmd);
+ SMB_PERFCOUNT_SET_MSGLEN_IN(&req->pcd, size);
+ }
conn = switch_message(req->cmd, req, size);
req->unread_bytes = 0;
}
+ if (req->done) {
+ TALLOC_FREE(req);
+ return;
+ }
+
if (req->outbuf == NULL) {
return;
}
if (!srv_send_smb(smbd_server_fd(),
(char *)req->outbuf,
- IS_CONN_ENCRYPTED(conn)||req->encrypted)) {
+ true, req->seqnum+1,
+ IS_CONN_ENCRYPTED(conn)||req->encrypted,
+ &req->pcd)) {
exit_server_cleanly("construct_reply: srv_send_smb failed.");
}
/****************************************************************************
Process an smb from the client
****************************************************************************/
-
static void process_smb(struct smbd_server_connection *conn,
uint8_t *inbuf, size_t nread, size_t unread_bytes,
- bool encrypted)
+ uint32_t seqnum, bool encrypted,
+ struct smb_perfcount_data *deferred_pcd)
{
int msg_type = CVAL(inbuf,0);
show_msg((char *)inbuf);
- construct_reply((char *)inbuf,nread,unread_bytes,encrypted);
-
+ construct_reply((char *)inbuf,nread,unread_bytes,seqnum,encrypted,deferred_pcd);
trans_num++;
done:
}
req->outbuf = NULL;
} else {
+ /*
+ * Update smb headers where subsequent chained commands
+ * may have updated them.
+ */
+ SCVAL(req->chain_outbuf, smb_tid, CVAL(req->outbuf, smb_tid));
+ SCVAL(req->chain_outbuf, smb_uid, CVAL(req->outbuf, smb_uid));
+
if (!smb_splice_chain(&req->chain_outbuf,
CVAL(req->outbuf, smb_com),
CVAL(req->outbuf, smb_wct),
*/
smb_setlen((char *)(req->chain_outbuf),
talloc_get_size(req->chain_outbuf) - 4);
+
if (!srv_send_smb(smbd_server_fd(), (char *)req->chain_outbuf,
+ true, req->seqnum+1,
IS_CONN_ENCRYPTED(req->conn)
- ||req->encrypted)) {
+ ||req->encrypted,
+ &req->pcd)) {
exit_server_cleanly("chain_reply: srv_send_smb "
"failed.");
}
+ TALLOC_FREE(req->chain_outbuf);
+ req->done = true;
return;
}
+ /* add a new perfcounter for this element of chain */
+ SMB_PERFCOUNT_ADD(&req->pcd);
+ SMB_PERFCOUNT_SET_OP(&req->pcd, chain_cmd);
+ SMB_PERFCOUNT_SET_MSGLEN_IN(&req->pcd, smblen);
+
/*
* Check if the client tries to fool us. The request so far uses the
* space to the end of the byte buffer in the request just
show_msg((char *)(req->chain_outbuf));
if (!srv_send_smb(smbd_server_fd(), (char *)req->chain_outbuf,
- IS_CONN_ENCRYPTED(req->conn)||req->encrypted)) {
+ true, req->seqnum+1,
+ IS_CONN_ENCRYPTED(req->conn)||req->encrypted,
+ &req->pcd)) {
exit_server_cleanly("construct_reply: srv_send_smb failed.");
}
+ TALLOC_FREE(req->chain_outbuf);
+ req->done = true;
}
/****************************************************************************
}
}
+static bool fd_is_readable(int fd)
+{
+ fd_set fds;
+ struct timeval timeout = {0, };
+ int ret;
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+
+ ret = sys_select(fd+1, &fds, NULL, NULL, &timeout);
+ if (ret == -1) {
+ return false;
+ }
+ return FD_ISSET(fd, &fds);
+}
+
static void smbd_server_connection_write_handler(struct smbd_server_connection *conn)
{
/* TODO: make write nonblocking */
}
-static void smbd_server_connection_read_handler(struct smbd_server_connection *conn)
+static void smbd_server_connection_read_handler(
+ struct smbd_server_connection *conn, int fd)
{
uint8_t *inbuf = NULL;
size_t inbuf_len = 0;
bool encrypted = false;
TALLOC_CTX *mem_ctx = talloc_tos();
NTSTATUS status;
+ uint32_t seqnum;
- /* TODO: make this completely nonblocking */
+ bool ok;
+
+ bool from_client = (smbd_server_fd() == fd)?true:false;
+
+ if (from_client) {
+ ok = smbd_lock_socket(conn);
+ if (!ok) {
+ exit_server_cleanly("failed to lock socket");
+ }
+
+ if (!fd_is_readable(smbd_server_fd())) {
+ DEBUG(10,("the echo listener was faster\n"));
+ ok = smbd_unlock_socket(conn);
+ if (!ok) {
+ exit_server_cleanly("failed to unlock");
+ }
+ return;
+ }
+
+ /* TODO: make this completely nonblocking */
+ status = receive_smb_talloc(mem_ctx, fd,
+ (char **)(void *)&inbuf,
+ 0, /* timeout */
+ &unread_bytes,
+ &encrypted,
+ &inbuf_len, &seqnum,
+ false /* trusted channel */);
+ ok = smbd_unlock_socket(conn);
+ if (!ok) {
+ exit_server_cleanly("failed to unlock");
+ }
+ } else {
+ /* TODO: make this completely nonblocking */
+ status = receive_smb_talloc(mem_ctx, fd,
+ (char **)(void *)&inbuf,
+ 0, /* timeout */
+ &unread_bytes,
+ &encrypted,
+ &inbuf_len, &seqnum,
+ true /* trusted channel */);
+ }
- status = receive_smb_talloc(mem_ctx, smbd_server_fd(),
- (char **)(void *)&inbuf,
- 0, /* timeout */
- &unread_bytes,
- &encrypted,
- &inbuf_len);
if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
goto process;
}
}
process:
- process_smb(conn, inbuf, inbuf_len, unread_bytes, encrypted);
+ process_smb(conn, inbuf, inbuf_len, unread_bytes,
+ seqnum, encrypted, NULL);
}
static void smbd_server_connection_handler(struct event_context *ev,
if (flags & EVENT_FD_WRITE) {
smbd_server_connection_write_handler(conn);
} else if (flags & EVENT_FD_READ) {
- smbd_server_connection_read_handler(conn);
+ smbd_server_connection_read_handler(conn, smbd_server_fd());
}
}
+static void smbd_server_echo_handler(struct event_context *ev,
+ struct fd_event *fde,
+ uint16_t flags,
+ void *private_data)
+{
+ struct smbd_server_connection *conn = talloc_get_type(private_data,
+ struct smbd_server_connection);
+
+ if (flags & EVENT_FD_WRITE) {
+ smbd_server_connection_write_handler(conn);
+ } else if (flags & EVENT_FD_READ) {
+ smbd_server_connection_read_handler(
+ conn, conn->smb1.echo_handler.trusted_fd);
+ }
+}
/****************************************************************************
received when we should release a specific IP
static void release_ip(const char *ip, void *priv)
{
char addr[INET6_ADDRSTRLEN];
+ char *p = addr;
+
+ client_socket_addr(get_client_fd(),addr,sizeof(addr));
- if (strcmp(client_socket_addr(get_client_fd(),addr,sizeof(addr)), ip) == 0) {
+ if (strncmp("::ffff:", addr, 7) == 0) {
+ p = addr + 7;
+ }
+
+ if ((strcmp(p, ip) == 0) || ((p != addr) && strcmp(addr, ip) == 0)) {
/* we can't afford to do a clean exit - that involves
database writes, which would potentially mean we
are still running after the failover has finished -
*/
static bool keepalive_fn(const struct timeval *now, void *private_data)
{
- if (!send_keepalive(smbd_server_fd())) {
+ bool ok;
+ bool ret;
+
+ ok = smbd_lock_socket(smbd_server_conn);
+ if (!ok) {
+ exit_server_cleanly("failed to lock socket");
+ }
+
+ ret = send_keepalive(smbd_server_fd());
+
+ ok = smbd_unlock_socket(smbd_server_conn);
+ if (!ok) {
+ exit_server_cleanly("failed to unlock socket");
+ }
+
+ if (!ret) {
DEBUG( 2, ( "Keepalive failed - exiting.\n" ) );
return False;
}
return true;
}
+static int create_unlink_tmp(const char *dir)
+{
+ char *fname;
+ int fd;
+
+ fname = talloc_asprintf(talloc_tos(), "%s/listenerlock_XXXXXX", dir);
+ if (fname == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+ fd = mkstemp(fname);
+ if (fd == -1) {
+ TALLOC_FREE(fname);
+ return -1;
+ }
+ if (unlink(fname) == -1) {
+ int sys_errno = errno;
+ close(fd);
+ TALLOC_FREE(fname);
+ errno = sys_errno;
+ return -1;
+ }
+ TALLOC_FREE(fname);
+ return fd;
+}
+
+struct smbd_echo_state {
+ struct tevent_context *ev;
+ struct iovec *pending;
+ struct smbd_server_connection *sconn;
+ int parent_pipe;
+
+ struct tevent_fd *parent_fde;
+
+ struct tevent_fd *read_fde;
+ struct tevent_req *write_req;
+};
+
+static void smbd_echo_writer_done(struct tevent_req *req);
+
+static void smbd_echo_activate_writer(struct smbd_echo_state *state)
+{
+ int num_pending;
+
+ if (state->write_req != NULL) {
+ return;
+ }
+
+ num_pending = talloc_array_length(state->pending);
+ if (num_pending == 0) {
+ return;
+ }
+
+ state->write_req = writev_send(state, state->ev, NULL,
+ state->parent_pipe,
+ state->pending, num_pending);
+ if (state->write_req == NULL) {
+ DEBUG(1, ("writev_send failed\n"));
+ exit(1);
+ }
+
+ talloc_steal(state->write_req, state->pending);
+ state->pending = NULL;
+
+ tevent_req_set_callback(state->write_req, smbd_echo_writer_done,
+ state);
+}
+
+static void smbd_echo_writer_done(struct tevent_req *req)
+{
+ struct smbd_echo_state *state = tevent_req_callback_data(
+ req, struct smbd_echo_state);
+ ssize_t written;
+ int err;
+
+ written = writev_recv(req, &err);
+ TALLOC_FREE(req);
+ state->write_req = NULL;
+ if (written == -1) {
+ DEBUG(1, ("writev to parent failed: %s\n", strerror(err)));
+ exit(1);
+ }
+ DEBUG(10,("echo_handler[%d]: forwarded pdu to main\n", (int)sys_getpid()));
+ smbd_echo_activate_writer(state);
+}
+
+static bool smbd_echo_reply(int fd,
+ uint8_t *inbuf, size_t inbuf_len,
+ uint32_t seqnum)
+{
+ struct smb_request req;
+ uint16_t num_replies;
+ size_t out_len;
+ char *outbuf;
+ bool ok;
+
+ if ((inbuf_len == 4) && (CVAL(inbuf, 0) == SMBkeepalive)) {
+ DEBUG(10, ("Got netbios keepalive\n"));
+ /*
+ * Just swallow it
+ */
+ return true;
+ }
+
+ if (inbuf_len < smb_size) {
+ DEBUG(10, ("Got short packet: %d bytes\n", (int)inbuf_len));
+ return false;
+ }
+ if (!valid_smb_header(inbuf)) {
+ DEBUG(10, ("Got invalid SMB header\n"));
+ return false;
+ }
+
+ init_smb_request(&req, inbuf, 0, false);
+ req.inbuf = inbuf;
+ req.seqnum = seqnum;
+
+ DEBUG(10, ("smbecho handler got cmd %d (%s)\n", (int)req.cmd,
+ smb_messages[req.cmd].name
+ ? smb_messages[req.cmd].name : "unknown"));
+
+ if (req.cmd != SMBecho) {
+ return false;
+ }
+ if (req.wct < 1) {
+ return false;
+ }
+
+ num_replies = SVAL(req.vwv+0, 0);
+ if (num_replies != 1) {
+ /* Not a Windows "Hey, you're still there?" request */
+ return false;
+ }
+
+ if (!create_outbuf(talloc_tos(), &req, (char *)req.inbuf, &outbuf,
+ 1, req.buflen)) {
+ DEBUG(10, ("create_outbuf failed\n"));
+ return false;
+ }
+ req.outbuf = (uint8_t *)outbuf;
+
+ SSVAL(req.outbuf, smb_vwv0, num_replies);
+
+ if (req.buflen > 0) {
+ memcpy(smb_buf(req.outbuf), req.buf, req.buflen);
+ }
+
+ out_len = smb_len(req.outbuf) + 4;
+
+ ok = srv_send_smb(smbd_server_fd(),
+ (char *)outbuf,
+ true, seqnum+1,
+ false, &req.pcd);
+ TALLOC_FREE(outbuf);
+ if (!ok) {
+ exit(1);
+ }
+
+ return true;
+}
+
+static void smbd_echo_exit(struct tevent_context *ev,
+ struct tevent_fd *fde, uint16_t flags,
+ void *private_data)
+{
+ DEBUG(2, ("smbd_echo_exit: lost connection to parent\n"));
+ exit(0);
+}
+
+static void smbd_echo_reader(struct tevent_context *ev,
+ struct tevent_fd *fde, uint16_t flags,
+ void *private_data)
+{
+ struct smbd_echo_state *state = talloc_get_type_abort(
+ private_data, struct smbd_echo_state);
+ struct smbd_server_connection *sconn = state->sconn;
+ size_t unread, num_pending;
+ NTSTATUS status;
+ struct iovec *tmp;
+ uint32_t seqnum = 0;
+ bool reply;
+ bool ok;
+ bool encrypted = false;
+
+ ok = smbd_lock_socket(sconn);
+ if (!ok) {
+ DEBUG(0, ("%s: failed to lock socket\n",
+ __location__));
+ exit(1);
+ }
+
+ if (!fd_is_readable(smbd_server_fd())) {
+ DEBUG(10,("echo_handler[%d] the parent smbd was faster\n",
+ (int)sys_getpid()));
+ ok = smbd_unlock_socket(sconn);
+ if (!ok) {
+ DEBUG(1, ("%s: failed to unlock socket in\n",
+ __location__));
+ exit(1);
+ }
+ return;
+ }
+
+ num_pending = talloc_array_length(state->pending);
+ tmp = talloc_realloc(state, state->pending, struct iovec,
+ num_pending+1);
+ if (tmp == NULL) {
+ DEBUG(1, ("talloc_realloc failed\n"));
+ exit(1);
+ }
+ state->pending = tmp;
+
+ DEBUG(10,("echo_handler[%d]: reading pdu\n", (int)sys_getpid()));
+
+ status = receive_smb_talloc(state->pending, smbd_server_fd(),
+ (char **)(void *)&state->pending[num_pending].iov_base,
+ 0 /* timeout */,
+ &unread,
+ &encrypted,
+ &state->pending[num_pending].iov_len,
+ &seqnum,
+ false /* trusted_channel*/);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("echo_handler[%d]: receive_smb_raw_talloc failed: %s\n",
+ (int)sys_getpid(), nt_errstr(status)));
+ exit(1);
+ }
+
+ ok = smbd_unlock_socket(sconn);
+ if (!ok) {
+ DEBUG(1, ("%s: failed to unlock socket in\n",
+ __location__));
+ exit(1);
+ }
+
+ reply = smbd_echo_reply(smbd_server_fd(),
+ (uint8_t *)state->pending[num_pending].iov_base,
+ state->pending[num_pending].iov_len,
+ seqnum);
+ if (reply) {
+ DEBUG(10,("echo_handler[%d]: replied to client\n", (int)sys_getpid()));
+ /* no check, shrinking by some bytes does not fail */
+ state->pending = talloc_realloc(state, state->pending,
+ struct iovec,
+ num_pending);
+ return;
+ }
+
+ if (state->pending[num_pending].iov_len >= smb_size) {
+ /*
+ * place the seqnum in the packet so that the main process
+ * can reply with signing
+ */
+ SIVAL((uint8_t *)state->pending[num_pending].iov_base,
+ smb_ss_field, seqnum);
+ SIVAL((uint8_t *)state->pending[num_pending].iov_base,
+ smb_ss_field+4, NT_STATUS_V(NT_STATUS_OK));
+ }
+
+ DEBUG(10,("echo_handler[%d]: forward to main\n", (int)sys_getpid()));
+ smbd_echo_activate_writer(state);
+}
+
+static void smbd_echo_loop(struct smbd_server_connection *sconn,
+ int parent_pipe)
+{
+ struct smbd_echo_state *state;
+
+ state = talloc_zero(sconn, struct smbd_echo_state);
+ if (state == NULL) {
+ DEBUG(1, ("talloc failed\n"));
+ return;
+ }
+ state->sconn = sconn;
+ state->parent_pipe = parent_pipe;
+ state->ev = s3_tevent_context_init(state);
+ if (state->ev == NULL) {
+ DEBUG(1, ("tevent_context_init failed\n"));
+ TALLOC_FREE(state);
+ return;
+ }
+ state->parent_fde = tevent_add_fd(state->ev, state, parent_pipe,
+ TEVENT_FD_READ, smbd_echo_exit,
+ state);
+ if (state->parent_fde == NULL) {
+ DEBUG(1, ("tevent_add_fd failed\n"));
+ TALLOC_FREE(state);
+ return;
+ }
+ state->read_fde = tevent_add_fd(state->ev, state, smbd_server_fd(),
+ TEVENT_FD_READ, smbd_echo_reader,
+ state);
+ if (state->read_fde == NULL) {
+ DEBUG(1, ("tevent_add_fd failed\n"));
+ TALLOC_FREE(state);
+ return;
+ }
+
+ while (true) {
+ if (tevent_loop_once(state->ev) == -1) {
+ DEBUG(1, ("tevent_loop_once failed: %s\n",
+ strerror(errno)));
+ break;
+ }
+ }
+ TALLOC_FREE(state);
+}
+
+/*
+ * Handle SMBecho requests in a forked child process
+ */
+static bool fork_echo_handler(struct smbd_server_connection *sconn)
+{
+ int listener_pipe[2];
+ int res;
+ pid_t child;
+
+ res = pipe(listener_pipe);
+ if (res == -1) {
+ DEBUG(1, ("pipe() failed: %s\n", strerror(errno)));
+ return false;
+ }
+ sconn->smb1.echo_handler.socket_lock_fd = create_unlink_tmp(lp_lockdir());
+ if (sconn->smb1.echo_handler.socket_lock_fd == -1) {
+ DEBUG(1, ("Could not create lock fd: %s\n", strerror(errno)));
+ goto fail;
+ }
+
+ child = sys_fork();
+ if (child == 0) {
+ NTSTATUS status;
+
+ close(listener_pipe[0]);
+
+ status = reinit_after_fork(smbd_messaging_context(),
+ smbd_event_context(), false);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("reinit_after_fork failed: %s\n",
+ nt_errstr(status)));
+ exit(1);
+ }
+ smbd_echo_loop(sconn, listener_pipe[1]);
+ exit(0);
+ }
+ close(listener_pipe[1]);
+ listener_pipe[1] = -1;
+ sconn->smb1.echo_handler.trusted_fd = listener_pipe[0];
+
+ DEBUG(10,("fork_echo_handler: main[%d] echo_child[%d]\n", (int)sys_getpid(), child));
+
+ /*
+ * Without smb signing this is the same as the normal smbd
+ * listener. This needs to change once signing comes in.
+ */
+ sconn->smb1.echo_handler.trusted_fde = event_add_fd(smbd_event_context(),
+ sconn,
+ sconn->smb1.echo_handler.trusted_fd,
+ EVENT_FD_READ,
+ smbd_server_echo_handler,
+ sconn);
+ if (sconn->smb1.echo_handler.trusted_fde == NULL) {
+ DEBUG(1, ("event_add_fd failed\n"));
+ goto fail;
+ }
+
+ return true;
+
+fail:
+ if (listener_pipe[0] != -1) {
+ close(listener_pipe[0]);
+ }
+ if (listener_pipe[1] != -1) {
+ close(listener_pipe[1]);
+ }
+ sconn->smb1.echo_handler.trusted_fd = -1;
+ if (sconn->smb1.echo_handler.socket_lock_fd != -1) {
+ close(sconn->smb1.echo_handler.socket_lock_fd);
+ }
+ sconn->smb1.echo_handler.trusted_fd = -1;
+ sconn->smb1.echo_handler.socket_lock_fd = -1;
+ return false;
+}
+
/****************************************************************************
Process commands from the client
****************************************************************************/
exit_server("failed to create smbd_server_connection");
}
+ smbd_server_conn->smb1.echo_handler.socket_lock_fd = -1;
+ smbd_server_conn->smb1.echo_handler.trusted_fd = -1;
+
/* Ensure child is set to blocking mode */
set_blocking(smbd_server_fd(),True);
unsigned char buf[5] = {0x83, 0, 0, 1, 0x81};
DEBUG( 1, ("Connection denied from %s\n",
client_addr(get_client_fd(),addr,sizeof(addr)) ) );
- (void)srv_send_smb(smbd_server_fd(),(char *)buf,false);
+ (void)srv_send_smb(smbd_server_fd(),(char *)buf, false,
+ 0, false, NULL);
exit_server_cleanly("connection denied");
}
init_modules();
+ smb_perfcount_init();
+
if (!init_account_policy()) {
exit_server("Could not open account policy tdb.\n");
}
if (*lp_rootdir()) {
if (chroot(lp_rootdir()) != 0) {
- DEBUG(0,("Failed changed root to %s\n", lp_rootdir()));
+ DEBUG(0,("Failed to change root to %s\n", lp_rootdir()));
+ exit_server("Failed to chroot()");
+ }
+ if (chdir("/") == -1) {
+ DEBUG(0,("Failed to chdir to / on chroot to %s\n", lp_rootdir()));
exit_server("Failed to chroot()");
}
DEBUG(0,("Changed root to %s\n", lp_rootdir()));
}
+ if (!srv_init_signing(smbd_server_conn)) {
+ exit_server("Failed to init smb_signing");
+ }
+
+ if (lp_async_smb_echo_handler() && !fork_echo_handler(smbd_server_conn)) {
+ exit_server("Failed to fork echo handler");
+ }
+
/* Setup oplocks */
if (!init_oplocks(smbd_messaging_context()))
exit_server("Failed to init oplocks");
exit_server_cleanly(NULL);
}
+
+bool req_is_in_chain(struct smb_request *req)
+{
+ if (req->vwv != (uint16_t *)(req->inbuf+smb_vwv)) {
+ /*
+ * We're right now handling a subsequent request, so we must
+ * be in a chain
+ */
+ return true;
+ }
+
+ if (!is_andx_req(req->cmd)) {
+ return false;
+ }
+
+ if (req->wct < 2) {
+ /*
+ * Okay, an illegal request, but definitely not chained :-)
+ */
+ return false;
+ }
+
+ return (CVAL(req->vwv+0, 0) != 0xFF);
+}