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
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
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.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
-#include "smb.h"
-#include "dlinklist.h"
+#include "../lib/util/dlinklist.h"
#include "lib/events/events.h"
#include "lib/socket/socket.h"
#include "lib/stream/packet.h"
+#include "libcli/raw/smb.h"
struct packet_context {
packet_callback_fn_t callback;
uint32_t num_read;
uint32_t initial_read;
struct socket_context *sock;
- struct event_context *ev;
+ struct tevent_context *ev;
size_t packet_size;
- void *private;
- struct fd_event *fde;
- BOOL serialise;
+ void *private_data;
+ struct tevent_fd *fde;
+ bool serialise;
int processing;
- BOOL recv_disable;
- BOOL nofree;
+ bool recv_disable;
+ bool recv_need_enable;
+ bool nofree;
- BOOL busy;
- BOOL destructor_called;
+ bool busy;
+ bool destructor_called;
+
+ bool unreliable_select;
struct send_element {
struct send_element *next, *prev;
static int packet_destructor(struct packet_context *pc)
{
if (pc->busy) {
- pc->destructor_called = True;
+ pc->destructor_called = true;
/* now we refuse the talloc_free() request. The free will
happen again in the packet_recv() code */
return -1;
/*
set the private pointer passed to the callback functions
*/
-_PUBLIC_ void packet_set_private(struct packet_context *pc, void *private)
+_PUBLIC_ void packet_set_private(struct packet_context *pc, void *private_data)
{
- pc->private = private;
+ pc->private_data = private_data;
}
/*
time on a socket. This can matter for code that relies on not
getting more than one packet per event
*/
-_PUBLIC_ void packet_set_event_context(struct packet_context *pc, struct event_context *ev)
+_PUBLIC_ void packet_set_event_context(struct packet_context *pc, struct tevent_context *ev)
{
pc->ev = ev;
}
/*
tell the packet layer the fde for the socket
*/
-_PUBLIC_ void packet_set_fde(struct packet_context *pc, struct fd_event *fde)
+_PUBLIC_ void packet_set_fde(struct packet_context *pc, struct tevent_fd *fde)
{
pc->fde = fde;
}
*/
_PUBLIC_ void packet_set_serialise(struct packet_context *pc)
{
- pc->serialise = True;
+ pc->serialise = true;
}
/*
*/
_PUBLIC_ void packet_set_nofree(struct packet_context *pc)
{
- pc->nofree = True;
+ pc->nofree = true;
}
+/*
+ tell the packet system that select/poll/epoll on the underlying
+ socket may not be a reliable way to determine if data is available
+ for receive. This happens with underlying socket systems such as the
+ one implemented on top of GNUTLS, where there may be data in
+ encryption/compression buffers that could be received by
+ socket_recv(), while there is no data waiting at the real socket
+ level as seen by select/poll/epoll. The GNUTLS library is supposed
+ to cope with this by always leaving some data sitting in the socket
+ buffer, but it does not seem to be reliable.
+ */
+_PUBLIC_ void packet_set_unreliable_select(struct packet_context *pc)
+{
+ pc->unreliable_select = true;
+}
/*
tell the caller we have an error
{
pc->sock = NULL;
if (pc->error_handler) {
- pc->error_handler(pc->private, status);
+ pc->error_handler(pc->private_data, status);
return;
}
/* default error handler is to free the callers private pointer */
if (!NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
DEBUG(0,("packet_error on %s - %s\n",
- talloc_get_name(pc->private), nt_errstr(status)));
+ talloc_get_name(pc->private_data), nt_errstr(status)));
}
- talloc_free(pc->private);
+ talloc_free(pc->private_data);
return;
}
/*
used to put packets on event boundaries
*/
-static void packet_next_event(struct event_context *ev, struct timed_event *te,
- struct timeval t, void *private)
+static void packet_next_event(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
{
- struct packet_context *pc = talloc_get_type(private, struct packet_context);
+ struct packet_context *pc = talloc_get_type(private_data, struct packet_context);
if (pc->num_read != 0 && pc->packet_size != 0 &&
pc->packet_size <= pc->num_read) {
packet_recv(pc);
NTSTATUS status;
size_t nread = 0;
DATA_BLOB blob;
+ bool recv_retry = false;
if (pc->processing) {
- EVENT_FD_NOT_READABLE(pc->fde);
+ TEVENT_FD_NOT_READABLE(pc->fde);
pc->processing++;
return;
}
if (pc->recv_disable) {
- EVENT_FD_NOT_READABLE(pc->fde);
+ pc->recv_need_enable = true;
+ TEVENT_FD_NOT_READABLE(pc->fde);
return;
}
return;
}
+again:
+
+ if (npending + pc->num_read < npending) {
+ packet_error(pc, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ if (npending + pc->num_read < pc->num_read) {
+ packet_error(pc, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
/* possibly expand the partial packet buffer */
if (npending + pc->num_read > pc->partial.length) {
- status = data_blob_realloc(pc, &pc->partial, npending+pc->num_read);
- if (!NT_STATUS_IS_OK(status)) {
- packet_error(pc, status);
+ if (!data_blob_realloc(pc, &pc->partial, npending+pc->num_read)) {
+ packet_error(pc, NT_STATUS_NO_MEMORY);
return;
}
}
+ if (pc->partial.length < pc->num_read + npending) {
+ packet_error(pc, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ if ((uint8_t *)pc->partial.data + pc->num_read < (uint8_t *)pc->partial.data) {
+ packet_error(pc, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ if ((uint8_t *)pc->partial.data + pc->num_read + npending < (uint8_t *)pc->partial.data) {
+ packet_error(pc, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
status = socket_recv(pc->sock, pc->partial.data + pc->num_read,
npending, &nread);
packet_error(pc, status);
return;
}
+ if (recv_retry && NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+ nread = 0;
+ status = NT_STATUS_OK;
+ }
if (!NT_STATUS_IS_OK(status)) {
return;
}
- if (nread == 0) {
+ if (nread == 0 && !recv_retry) {
packet_eof(pc);
return;
}
pc->num_read += nread;
-next_partial:
- if (pc->partial.length != pc->num_read) {
- status = data_blob_realloc(pc, &pc->partial, pc->num_read);
+ if (pc->unreliable_select && nread != 0) {
+ recv_retry = true;
+ status = socket_pending(pc->sock, &npending);
if (!NT_STATUS_IS_OK(status)) {
packet_error(pc, status);
return;
}
+ if (npending != 0) {
+ goto again;
+ }
+ }
+
+next_partial:
+ if (pc->partial.length != pc->num_read) {
+ if (!data_blob_realloc(pc, &pc->partial, pc->num_read)) {
+ packet_error(pc, NT_STATUS_NO_MEMORY);
+ return;
+ }
}
/* see if its a full request */
blob = pc->partial;
blob.length = pc->num_read;
- status = pc->full_request(pc->private, blob, &pc->packet_size);
+ status = pc->full_request(pc->private_data, blob, &pc->packet_size);
if (NT_STATUS_IS_ERR(status)) {
packet_error(pc, status);
return;
packet_error(pc, NT_STATUS_NO_MEMORY);
return;
}
- status = data_blob_realloc(pc, &blob, pc->packet_size);
- if (!NT_STATUS_IS_OK(status)) {
- packet_error(pc, status);
+ /* Trunate the blob sent to the caller to only the packet length */
+ if (!data_blob_realloc(pc, &blob, pc->packet_size)) {
+ packet_error(pc, NT_STATUS_NO_MEMORY);
return;
}
} else {
pc->processing = 1;
}
- pc->busy = True;
+ pc->busy = true;
- status = pc->callback(pc->private, blob);
+ status = pc->callback(pc->private_data, blob);
- pc->busy = False;
+ pc->busy = false;
if (pc->destructor_called) {
talloc_free(pc);
if (pc->processing) {
if (pc->processing > 1) {
- EVENT_FD_READABLE(pc->fde);
+ TEVENT_FD_READABLE(pc->fde);
}
pc->processing = 0;
}
blob = pc->partial;
blob.length = pc->num_read;
- status = pc->full_request(pc->private, blob, &pc->packet_size);
+ status = pc->full_request(pc->private_data, blob, &pc->packet_size);
if (NT_STATUS_IS_ERR(status)) {
packet_error(pc, status);
return;
return;
}
- event_add_timed(pc->ev, pc, timeval_zero(), packet_next_event, pc);
+ tevent_add_timer(pc->ev, pc, timeval_zero(), packet_next_event, pc);
}
*/
_PUBLIC_ void packet_recv_disable(struct packet_context *pc)
{
- EVENT_FD_NOT_READABLE(pc->fde);
- pc->recv_disable = True;
+ pc->recv_disable = true;
}
/*
*/
_PUBLIC_ void packet_recv_enable(struct packet_context *pc)
{
- EVENT_FD_READABLE(pc->fde);
- pc->recv_disable = False;
+ if (pc->recv_need_enable) {
+ pc->recv_need_enable = false;
+ TEVENT_FD_READABLE(pc->fde);
+ }
+ pc->recv_disable = false;
if (pc->num_read != 0 && pc->packet_size >= pc->num_read) {
- event_add_timed(pc->ev, pc, timeval_zero(), packet_next_event, pc);
+ tevent_add_timer(pc->ev, pc, timeval_zero(), packet_next_event, pc);
}
}
if (el->nsent == el->blob.length) {
DLIST_REMOVE(pc->send_queue, el);
if (el->send_callback) {
+ pc->busy = true;
el->send_callback(el->send_callback_private);
+ pc->busy = false;
+ if (pc->destructor_called) {
+ talloc_free(pc);
+ return;
+ }
}
talloc_free(el);
}
/* we're out of requests to send, so don't wait for write
events any more */
- EVENT_FD_NOT_WRITEABLE(pc->fde);
+ TEVENT_FD_NOT_WRITEABLE(pc->fde);
}
/*
put a packet in the send queue. When the packet is actually sent,
call send_callback.
- Useful for operations that must occour after sending a message, such
+ Useful for operations that must occur after sending a message, such
as the switch to SASL encryption after as sucessful LDAP bind relpy.
*/
_PUBLIC_ NTSTATUS packet_send_callback(struct packet_context *pc, DATA_BLOB blob,
packet_send_callback_fn_t send_callback,
- void *private)
+ void *private_data)
{
struct send_element *el;
el = talloc(pc, struct send_element);
NT_STATUS_HAVE_NO_MEMORY(el);
- DLIST_ADD_END(pc->send_queue, el, struct send_element *);
+ DLIST_ADD_END(pc->send_queue, el);
el->blob = blob;
el->nsent = 0;
el->send_callback = send_callback;
- el->send_callback_private = private;
+ el->send_callback_private = private_data;
/* if we aren't going to free the packet then we must reference it
to ensure it doesn't disappear before going out */
talloc_steal(el, blob.data);
}
- if (private && !talloc_reference(el, private)) {
+ if (private_data && !talloc_reference(el, private_data)) {
return NT_STATUS_NO_MEMORY;
}
- EVENT_FD_WRITEABLE(pc->fde);
+ TEVENT_FD_WRITEABLE(pc->fde);
return NT_STATUS_OK;
}
/*
a full request checker for NBT formatted packets (first 3 bytes are length)
*/
-_PUBLIC_ NTSTATUS packet_full_request_nbt(void *private, DATA_BLOB blob, size_t *size)
+_PUBLIC_ NTSTATUS packet_full_request_nbt(void *private_data, DATA_BLOB blob, size_t *size)
{
if (blob.length < 4) {
return STATUS_MORE_ENTRIES;
}
- *size = 4 + smb_len(blob.data);
+ /*
+ * Note: that we use smb_len_tcp() instead
+ * of smb_len_nbt() as this function is not
+ * used for nbt and the source4 copy
+ * of smb_len() was smb_len_tcp()
+ */
+ *size = 4 + smb_len_tcp(blob.data);
if (*size > blob.length) {
return STATUS_MORE_ENTRIES;
}
work out if a packet is complete for protocols that use a 32 bit network byte
order length
*/
-_PUBLIC_ NTSTATUS packet_full_request_u32(void *private, DATA_BLOB blob, size_t *size)
+_PUBLIC_ NTSTATUS packet_full_request_u32(void *private_data, DATA_BLOB blob, size_t *size)
{
if (blob.length < 4) {
return STATUS_MORE_ENTRIES;
}
return NT_STATUS_OK;
}
+
+_PUBLIC_ NTSTATUS packet_full_request_u16(void *private_data, DATA_BLOB blob, size_t *size)
+{
+ if (blob.length < 2) {
+ return STATUS_MORE_ENTRIES;
+ }
+ *size = 2 + RSVAL(blob.data, 0);
+ if (*size > blob.length) {
+ return STATUS_MORE_ENTRIES;
+ }
+ return NT_STATUS_OK;
+}