static void InternalErrorEventHandler(void *arg, const err_t err) { PCONNECTION_ENDPOINT Connection = arg; KIRQL OldIrql; /* Make sure the socket didn't get closed */ if (!arg) return; /* Check if data is left to be read */ LockObject(Connection, &OldIrql); if (IsListEmpty(&Connection->PacketQueue)) { UnlockObject(Connection, OldIrql); /* Deliver the error now */ TCPFinEventHandler(arg, err); } else { UnlockObject(Connection, OldIrql); /* Defer the error delivery until all data is gone */ Connection->ReceiveShutdown = TRUE; Connection->ReceiveShutdownStatus = TCPTranslateError(err); TCPRecvEventHandler(arg); } }
static err_t InternalRecvEventHandler(void *arg, PTCP_PCB pcb, struct pbuf *p, const err_t err) { PCONNECTION_ENDPOINT Connection = arg; /* Make sure the socket didn't get closed */ if (!arg) { if (p) pbuf_free(p); return ERR_OK; } if (p) { LibTCPEnqueuePacket(Connection, p); tcp_recved(pcb, p->tot_len); TCPRecvEventHandler(arg); } else if (err == ERR_OK) { /* Complete pending reads with 0 bytes to indicate a graceful closure, * but note that send is still possible in this state so we don't close the * whole socket here (by calling tcp_close()) as that would violate TCP specs */ Connection->ReceiveShutdown = TRUE; Connection->ReceiveShutdownStatus = STATUS_SUCCESS; /* If we already did a send shutdown, we're in TIME_WAIT so we can't use this PCB anymore */ if (Connection->SendShutdown) { Connection->SocketContext = NULL; tcp_arg(pcb, NULL); } /* Indicate the graceful close event */ TCPRecvEventHandler(arg); /* If the PCB is gone, clean up the connection */ if (Connection->SendShutdown) { TCPFinEventHandler(Connection, ERR_CLSD); } } return ERR_OK; }
static void LibTCPCloseCallback(void *arg) { struct lwip_callback_msg *msg = arg; PTCP_PCB pcb = msg->Input.Close.Connection->SocketContext; /* Empty the queue even if we're already "closed" */ LibTCPEmptyQueue(msg->Input.Close.Connection); /* Check if we've already been closed */ if (msg->Input.Close.Connection->Closing) { msg->Output.Close.Error = ERR_OK; goto done; } /* Enter "closing" mode if we're doing a normal close */ if (msg->Input.Close.Callback) msg->Input.Close.Connection->Closing = TRUE; /* Check if the PCB was already "closed" but the client doesn't know it yet */ if (!msg->Input.Close.Connection->SocketContext) { msg->Output.Close.Error = ERR_OK; goto done; } /* Clear the PCB pointer and stop callbacks */ msg->Input.Close.Connection->SocketContext = NULL; tcp_arg(pcb, NULL); /* This may generate additional callbacks but we don't care, * because they're too inconsistent to rely on */ msg->Output.Close.Error = tcp_close(pcb); if (msg->Output.Close.Error) { /* Restore the PCB pointer */ msg->Input.Close.Connection->SocketContext = pcb; msg->Input.Close.Connection->Closing = FALSE; } else if (msg->Input.Close.Callback) { TCPFinEventHandler(msg->Input.Close.Connection, ERR_CLSD); } done: KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE); }
static void LibTCPShutdownCallback(void *arg) { struct lwip_callback_msg *msg = arg; PTCP_PCB pcb = msg->Input.Shutdown.Connection->SocketContext; if (!msg->Input.Shutdown.Connection->SocketContext) { msg->Output.Shutdown.Error = ERR_CLSD; goto done; } /* LwIP makes the (questionable) assumption that SHUTDOWN_RDWR is equivalent to tcp_close(). * This assumption holds even if the shutdown calls are done separately (even through multiple * WinSock shutdown() calls). This assumption means that lwIP has the right to deallocate our * PCB without telling us if we shutdown TX and RX. To avoid these problems, we'll clear the * socket context if we have called shutdown for TX and RX. */ if (msg->Input.Shutdown.shut_rx) { msg->Output.Shutdown.Error = tcp_shutdown(pcb, TRUE, FALSE); } if (msg->Input.Shutdown.shut_tx) { msg->Output.Shutdown.Error = tcp_shutdown(pcb, FALSE, TRUE); } if (!msg->Output.Shutdown.Error) { if (msg->Input.Shutdown.shut_rx) { msg->Input.Shutdown.Connection->ReceiveShutdown = TRUE; msg->Input.Shutdown.Connection->ReceiveShutdownStatus = STATUS_FILE_CLOSED; } if (msg->Input.Shutdown.shut_tx) msg->Input.Shutdown.Connection->SendShutdown = TRUE; if (msg->Input.Shutdown.Connection->ReceiveShutdown && msg->Input.Shutdown.Connection->SendShutdown) { /* The PCB is not ours anymore */ msg->Input.Shutdown.Connection->SocketContext = NULL; tcp_arg(pcb, NULL); TCPFinEventHandler(msg->Input.Shutdown.Connection, ERR_CLSD); } } done: KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE); }
static err_t InternalRecvEventHandler(void *arg, PTCP_PCB pcb, struct pbuf *p, const err_t err) { PCONNECTION_ENDPOINT Connection = arg; /* Make sure the socket didn't get closed */ if (!arg) { if (p) pbuf_free(p); return ERR_OK; } if (p) { LibTCPEnqueuePacket(Connection, p); tcp_recved(pcb, p->tot_len); TCPRecvEventHandler(arg); } else if (err == ERR_OK) { /* Complete pending reads with 0 bytes to indicate a graceful closure, * but note that send is still possible in this state so we don't close the * whole socket here (by calling tcp_close()) as that would violate TCP specs */ Connection->ReceiveShutdown = TRUE; Connection->ReceiveShutdownStatus = STATUS_SUCCESS; /* This code path executes for both remotely and locally initiated closures, * and we need to distinguish between them */ if (Connection->SocketContext) { /* Remotely initiated close */ TCPRecvEventHandler(arg); } else { /* Locally initated close */ TCPFinEventHandler(arg, ERR_CLSD); } } return ERR_OK; }
static void LibTCPCloseCallback(void *arg) { struct lwip_callback_msg *msg = arg; PTCP_PCB pcb = msg->Input.Close.Connection->SocketContext; /* Empty the queue even if we're already "closed" */ LibTCPEmptyQueue(msg->Input.Close.Connection); if (!msg->Input.Close.Connection->SocketContext) { msg->Output.Close.Error = ERR_OK; goto done; } /* Clear the PCB pointer */ msg->Input.Close.Connection->SocketContext = NULL; switch (pcb->state) { case CLOSED: case LISTEN: case SYN_SENT: msg->Output.Close.Error = tcp_close(pcb); if (!msg->Output.Close.Error && msg->Input.Close.Callback) TCPFinEventHandler(msg->Input.Close.Connection, ERR_CLSD); break; default: /* Abort the socket */ tcp_abort(pcb); msg->Output.Close.Error = ERR_OK; break; } if (msg->Output.Close.Error) { /* Restore the PCB pointer */ msg->Input.Close.Connection->SocketContext = pcb; } done: KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE); }
static void InternalErrorEventHandler(void *arg, const err_t err) { PCONNECTION_ENDPOINT Connection = arg; /* Make sure the socket didn't get closed */ if (!arg || Connection->SocketContext == NULL) return; /* The PCB is dead now */ Connection->SocketContext = NULL; /* Give them one shot to receive the remaining data */ Connection->ReceiveShutdown = TRUE; Connection->ReceiveShutdownStatus = TCPTranslateError(err); TCPRecvEventHandler(Connection); /* Terminate the connection */ TCPFinEventHandler(Connection, err); }