- Fix broken handling of partial receives

svn path=/trunk/; revision=57169
This commit is contained in:
Cameron Gutman 2012-08-26 22:24:49 +00:00
parent 44356bb773
commit aa6611286c
2 changed files with 50 additions and 31 deletions

View file

@ -15,6 +15,7 @@ typedef struct tcp_pcb* PTCP_PCB;
typedef struct _QUEUE_ENTRY typedef struct _QUEUE_ENTRY
{ {
struct pbuf *p; struct pbuf *p;
ULONG Offset;
LIST_ENTRY ListEntry; LIST_ENTRY ListEntry;
} QUEUE_ENTRY, *PQUEUE_ENTRY; } QUEUE_ENTRY, *PQUEUE_ENTRY;

View file

@ -60,6 +60,7 @@ void LibTCPEnqueuePacket(PCONNECTION_ENDPOINT Connection, struct pbuf *p)
qp = (PQUEUE_ENTRY)ExAllocateFromNPagedLookasideList(&QueueEntryLookasideList); qp = (PQUEUE_ENTRY)ExAllocateFromNPagedLookasideList(&QueueEntryLookasideList);
qp->p = p; qp->p = p;
qp->Offset = 0;
ExInterlockedInsertTailList(&Connection->PacketQueue, &qp->ListEntry, &Connection->Lock); ExInterlockedInsertTailList(&Connection->PacketQueue, &qp->ListEntry, &Connection->Lock);
} }
@ -82,9 +83,10 @@ NTSTATUS LibTCPGetDataFromConnectionQueue(PCONNECTION_ENDPOINT Connection, PUCHA
{ {
PQUEUE_ENTRY qp; PQUEUE_ENTRY qp;
struct pbuf* p; struct pbuf* p;
NTSTATUS Status = STATUS_PENDING; NTSTATUS Status;
UINT ReadLength, ExistingDataLength; UINT ReadLength, PayloadLength;
KIRQL OldIrql; KIRQL OldIrql;
PUCHAR Payload;
(*Received) = 0; (*Received) = 0;
@ -95,50 +97,54 @@ NTSTATUS LibTCPGetDataFromConnectionQueue(PCONNECTION_ENDPOINT Connection, PUCHA
while ((qp = LibTCPDequeuePacket(Connection)) != NULL) while ((qp = LibTCPDequeuePacket(Connection)) != NULL)
{ {
p = qp->p; p = qp->p;
ExistingDataLength = (*Received);
Status = STATUS_SUCCESS; /* Calculate the payload first */
Payload = p->payload;
Payload += qp->Offset;
PayloadLength = p->len;
PayloadLength -= qp->Offset;
ReadLength = MIN(p->tot_len, RecvLen); /* Check if we're reading the whole buffer */
if (ReadLength != p->tot_len) ReadLength = MIN(PayloadLength, RecvLen);
if (ReadLength != PayloadLength)
{ {
if (ExistingDataLength) /* Save this one for later */
{ qp->Offset += ReadLength;
/* The packet was too big but we used some data already so give it another shot later */
InsertHeadList(&Connection->PacketQueue, &qp->ListEntry); InsertHeadList(&Connection->PacketQueue, &qp->ListEntry);
break; qp = NULL;
}
else
{
/* The packet is just too big to fit fully in our buffer, even when empty so
* return an informative status but still copy all the data we can fit.
*/
Status = STATUS_BUFFER_OVERFLOW;
}
} }
UnlockObject(Connection, OldIrql); UnlockObject(Connection, OldIrql);
/* Return to a lower IRQL because the receive buffer may be pageable memory */ /* Return to a lower IRQL because the receive buffer may be pageable memory */
for (; (*Received) < ReadLength + ExistingDataLength; (*Received) += p->len, p = p->next) RtlCopyMemory(RecvBuffer,
{ Payload,
RtlCopyMemory(RecvBuffer + (*Received), p->payload, p->len); ReadLength);
}
LockObject(Connection, &OldIrql); LockObject(Connection, &OldIrql);
/* Update trackers */
RecvLen -= ReadLength; RecvLen -= ReadLength;
RecvBuffer += ReadLength;
(*Received) += ReadLength;
if (qp != NULL)
{
/* Use this special pbuf free callback function because we're outside tcpip thread */ /* Use this special pbuf free callback function because we're outside tcpip thread */
pbuf_free_callback(qp->p); pbuf_free_callback(qp->p);
ExFreeToNPagedLookasideList(&QueueEntryLookasideList, qp); ExFreeToNPagedLookasideList(&QueueEntryLookasideList, qp);
}
else
{
/* If we get here, it means we've filled the buffer */
ASSERT(RecvLen == 0);
}
Status = STATUS_SUCCESS;
if (!RecvLen) if (!RecvLen)
break; break;
if (Status != STATUS_SUCCESS)
break;
} }
} }
else else
@ -196,6 +202,8 @@ err_t
InternalRecvEventHandler(void *arg, PTCP_PCB pcb, struct pbuf *p, const err_t err) InternalRecvEventHandler(void *arg, PTCP_PCB pcb, struct pbuf *p, const err_t err)
{ {
PCONNECTION_ENDPOINT Connection = arg; PCONNECTION_ENDPOINT Connection = arg;
struct pbuf *pb;
ULONG RecvLen;
/* Make sure the socket didn't get closed */ /* Make sure the socket didn't get closed */
if (!arg) if (!arg)
@ -208,9 +216,19 @@ InternalRecvEventHandler(void *arg, PTCP_PCB pcb, struct pbuf *p, const err_t er
if (p) if (p)
{ {
LibTCPEnqueuePacket(Connection, p); pb = p;
RecvLen = 0;
while (pb != NULL)
{
/* Enqueue this buffer */
LibTCPEnqueuePacket(Connection, pb);
RecvLen += pb->len;
tcp_recved(pcb, p->tot_len); /* Advance and unchain the buffer */
pb = pbuf_dechain(pb);;
}
tcp_recved(pcb, RecvLen);
TCPRecvEventHandler(arg); TCPRecvEventHandler(arg);
} }