1 #include <debug.h> 2 #include <lwip/tcpip.h> 3 4 #include "lwip_glue.h" 5 6 static const char * const tcp_state_str[] = { 7 "CLOSED", 8 "LISTEN", 9 "SYN_SENT", 10 "SYN_RCVD", 11 "ESTABLISHED", 12 "FIN_WAIT_1", 13 "FIN_WAIT_2", 14 "CLOSE_WAIT", 15 "CLOSING", 16 "LAST_ACK", 17 "TIME_WAIT" 18 }; 19 20 /* The way that lwIP does multi-threading is really not ideal for our purposes but 21 * we best go along with it unless we want another unstable TCP library. lwIP uses 22 * a thread called the "tcpip thread" which is the only one allowed to call raw API 23 * functions. Since this is the case, for each of our LibTCP* functions, we queue a request 24 * for a callback to "tcpip thread" which calls our LibTCP*Callback functions. Yes, this is 25 * a lot of unnecessary thread swapping and it could definitely be faster, but I don't want 26 * to going messing around in lwIP because I have no desire to create another mess like oskittcp */ 27 28 extern KEVENT TerminationEvent; 29 extern NPAGED_LOOKASIDE_LIST MessageLookasideList; 30 extern NPAGED_LOOKASIDE_LIST QueueEntryLookasideList; 31 32 /* Required for ERR_T to NTSTATUS translation in receive error handling */ 33 NTSTATUS TCPTranslateError(const err_t err); 34 35 void 36 LibTCPDumpPcb(PVOID SocketContext) 37 { 38 struct tcp_pcb *pcb = (struct tcp_pcb*)SocketContext; 39 unsigned int addr = ntohl(pcb->remote_ip.addr); 40 41 DbgPrint("\tState: %s\n", tcp_state_str[pcb->state]); 42 DbgPrint("\tRemote: (%d.%d.%d.%d, %d)\n", 43 (addr >> 24) & 0xFF, 44 (addr >> 16) & 0xFF, 45 (addr >> 8) & 0xFF, 46 addr & 0xFF, 47 pcb->remote_port); 48 } 49 50 static 51 void 52 LibTCPEmptyQueue(PCONNECTION_ENDPOINT Connection) 53 { 54 PLIST_ENTRY Entry; 55 PQUEUE_ENTRY qp = NULL; 56 57 ReferenceObject(Connection); 58 59 while (!IsListEmpty(&Connection->PacketQueue)) 60 { 61 Entry = RemoveHeadList(&Connection->PacketQueue); 62 qp = CONTAINING_RECORD(Entry, QUEUE_ENTRY, ListEntry); 63 64 /* We're in the tcpip thread here so this is safe */ 65 pbuf_free(qp->p); 66 67 ExFreeToNPagedLookasideList(&QueueEntryLookasideList, qp); 68 } 69 70 DereferenceObject(Connection); 71 } 72 73 void LibTCPEnqueuePacket(PCONNECTION_ENDPOINT Connection, struct pbuf *p) 74 { 75 PQUEUE_ENTRY qp; 76 77 qp = (PQUEUE_ENTRY)ExAllocateFromNPagedLookasideList(&QueueEntryLookasideList); 78 qp->p = p; 79 qp->Offset = 0; 80 81 LockObject(Connection); 82 InsertTailList(&Connection->PacketQueue, &qp->ListEntry); 83 UnlockObject(Connection); 84 } 85 86 PQUEUE_ENTRY LibTCPDequeuePacket(PCONNECTION_ENDPOINT Connection) 87 { 88 PLIST_ENTRY Entry; 89 PQUEUE_ENTRY qp = NULL; 90 91 if (IsListEmpty(&Connection->PacketQueue)) return NULL; 92 93 Entry = RemoveHeadList(&Connection->PacketQueue); 94 95 qp = CONTAINING_RECORD(Entry, QUEUE_ENTRY, ListEntry); 96 97 return qp; 98 } 99 100 NTSTATUS LibTCPGetDataFromConnectionQueue(PCONNECTION_ENDPOINT Connection, PUCHAR RecvBuffer, UINT RecvLen, UINT *Received) 101 { 102 PQUEUE_ENTRY qp; 103 struct pbuf* p; 104 NTSTATUS Status; 105 UINT ReadLength, PayloadLength, Offset, Copied; 106 107 (*Received) = 0; 108 109 LockObject(Connection); 110 111 if (!IsListEmpty(&Connection->PacketQueue)) 112 { 113 while ((qp = LibTCPDequeuePacket(Connection)) != NULL) 114 { 115 p = qp->p; 116 117 /* Calculate the payload length first */ 118 PayloadLength = p->tot_len; 119 PayloadLength -= qp->Offset; 120 Offset = qp->Offset; 121 122 /* Check if we're reading the whole buffer */ 123 ReadLength = MIN(PayloadLength, RecvLen); 124 ASSERT(ReadLength != 0); 125 if (ReadLength != PayloadLength) 126 { 127 /* Save this one for later */ 128 qp->Offset += ReadLength; 129 InsertHeadList(&Connection->PacketQueue, &qp->ListEntry); 130 qp = NULL; 131 } 132 133 Copied = pbuf_copy_partial(p, RecvBuffer, ReadLength, Offset); 134 ASSERT(Copied == ReadLength); 135 136 /* Update trackers */ 137 RecvLen -= ReadLength; 138 RecvBuffer += ReadLength; 139 (*Received) += ReadLength; 140 141 if (qp != NULL) 142 { 143 /* Use this special pbuf free callback function because we're outside tcpip thread */ 144 pbuf_free_callback(qp->p); 145 146 ExFreeToNPagedLookasideList(&QueueEntryLookasideList, qp); 147 } 148 else 149 { 150 /* If we get here, it means we've filled the buffer */ 151 ASSERT(RecvLen == 0); 152 } 153 154 ASSERT((*Received) != 0); 155 Status = STATUS_SUCCESS; 156 157 if (!RecvLen) 158 break; 159 } 160 } 161 else 162 { 163 if (Connection->ReceiveShutdown) 164 Status = Connection->ReceiveShutdownStatus; 165 else 166 Status = STATUS_PENDING; 167 } 168 169 UnlockObject(Connection); 170 171 return Status; 172 } 173 174 static 175 BOOLEAN 176 WaitForEventSafely(PRKEVENT Event) 177 { 178 PVOID WaitObjects[] = {Event, &TerminationEvent}; 179 180 if (KeWaitForMultipleObjects(2, 181 WaitObjects, 182 WaitAny, 183 Executive, 184 KernelMode, 185 FALSE, 186 NULL, 187 NULL) == STATUS_WAIT_0) 188 { 189 /* Signalled by the caller's event */ 190 return TRUE; 191 } 192 else /* if KeWaitForMultipleObjects() == STATUS_WAIT_1 */ 193 { 194 /* Signalled by our termination event */ 195 return FALSE; 196 } 197 } 198 199 static 200 err_t 201 InternalSendEventHandler(void *arg, PTCP_PCB pcb, const u16_t space) 202 { 203 /* Make sure the socket didn't get closed */ 204 if (!arg) return ERR_OK; 205 206 TCPSendEventHandler(arg, space); 207 208 return ERR_OK; 209 } 210 211 static 212 err_t 213 InternalRecvEventHandler(void *arg, PTCP_PCB pcb, struct pbuf *p, const err_t err) 214 { 215 PCONNECTION_ENDPOINT Connection = arg; 216 217 /* Make sure the socket didn't get closed */ 218 if (!arg) 219 { 220 if (p) 221 pbuf_free(p); 222 223 return ERR_OK; 224 } 225 226 if (p) 227 { 228 LibTCPEnqueuePacket(Connection, p); 229 230 tcp_recved(pcb, p->tot_len); 231 232 TCPRecvEventHandler(arg); 233 } 234 else if (err == ERR_OK) 235 { 236 /* Complete pending reads with 0 bytes to indicate a graceful closure, 237 * but note that send is still possible in this state so we don't close the 238 * whole socket here (by calling tcp_close()) as that would violate TCP specs 239 */ 240 Connection->ReceiveShutdown = TRUE; 241 Connection->ReceiveShutdownStatus = STATUS_SUCCESS; 242 243 /* If we already did a send shutdown, we're in TIME_WAIT so we can't use this PCB anymore */ 244 if (Connection->SendShutdown) 245 { 246 Connection->SocketContext = NULL; 247 tcp_arg(pcb, NULL); 248 } 249 250 /* Indicate the graceful close event */ 251 TCPRecvEventHandler(arg); 252 253 /* If the PCB is gone, clean up the connection */ 254 if (Connection->SendShutdown) 255 { 256 TCPFinEventHandler(Connection, ERR_CLSD); 257 } 258 } 259 260 return ERR_OK; 261 } 262 263 /* This function MUST return an error value that is not ERR_ABRT or ERR_OK if the connection 264 * is not accepted to avoid leaking the new PCB */ 265 static 266 err_t 267 InternalAcceptEventHandler(void *arg, PTCP_PCB newpcb, const err_t err) 268 { 269 /* Make sure the socket didn't get closed */ 270 if (!arg) 271 return ERR_CLSD; 272 273 TCPAcceptEventHandler(arg, newpcb); 274 275 /* Set in LibTCPAccept (called from TCPAcceptEventHandler) */ 276 if (newpcb->callback_arg) 277 return ERR_OK; 278 else 279 return ERR_CLSD; 280 } 281 282 static 283 err_t 284 InternalConnectEventHandler(void *arg, PTCP_PCB pcb, const err_t err) 285 { 286 /* Make sure the socket didn't get closed */ 287 if (!arg) 288 return ERR_OK; 289 290 TCPConnectEventHandler(arg, err); 291 292 return ERR_OK; 293 } 294 295 static 296 void 297 InternalErrorEventHandler(void *arg, const err_t err) 298 { 299 PCONNECTION_ENDPOINT Connection = arg; 300 301 /* Make sure the socket didn't get closed */ 302 if (!arg || Connection->SocketContext == NULL) return; 303 304 /* The PCB is dead now */ 305 Connection->SocketContext = NULL; 306 307 /* Give them one shot to receive the remaining data */ 308 Connection->ReceiveShutdown = TRUE; 309 Connection->ReceiveShutdownStatus = TCPTranslateError(err); 310 TCPRecvEventHandler(Connection); 311 312 /* Terminate the connection */ 313 TCPFinEventHandler(Connection, err); 314 } 315 316 static 317 void 318 LibTCPSocketCallback(void *arg) 319 { 320 struct lwip_callback_msg *msg = arg; 321 322 ASSERT(msg); 323 324 msg->Output.Socket.NewPcb = tcp_new(); 325 326 if (msg->Output.Socket.NewPcb) 327 { 328 tcp_arg(msg->Output.Socket.NewPcb, msg->Input.Socket.Arg); 329 tcp_err(msg->Output.Socket.NewPcb, InternalErrorEventHandler); 330 } 331 332 KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE); 333 } 334 335 struct tcp_pcb * 336 LibTCPSocket(void *arg) 337 { 338 struct lwip_callback_msg *msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList); 339 struct tcp_pcb *ret; 340 341 if (msg) 342 { 343 KeInitializeEvent(&msg->Event, NotificationEvent, FALSE); 344 msg->Input.Socket.Arg = arg; 345 346 tcpip_callback_with_block(LibTCPSocketCallback, msg, 1); 347 348 if (WaitForEventSafely(&msg->Event)) 349 ret = msg->Output.Socket.NewPcb; 350 else 351 ret = NULL; 352 353 ExFreeToNPagedLookasideList(&MessageLookasideList, msg); 354 355 return ret; 356 } 357 358 return NULL; 359 } 360 361 static 362 void 363 LibTCPFreeSocketCallback(void *arg) 364 { 365 struct lwip_callback_msg *msg = arg; 366 367 ASSERT(msg); 368 369 /* Calling tcp_close will free it */ 370 tcp_close(msg->Input.FreeSocket.pcb); 371 372 KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE); 373 } 374 375 void LibTCPFreeSocket(PTCP_PCB pcb) 376 { 377 struct lwip_callback_msg msg; 378 379 KeInitializeEvent(&msg.Event, NotificationEvent, FALSE); 380 msg.Input.FreeSocket.pcb = pcb; 381 382 tcpip_callback_with_block(LibTCPFreeSocketCallback, &msg, 1); 383 384 WaitForEventSafely(&msg.Event); 385 } 386 387 388 static 389 void 390 LibTCPBindCallback(void *arg) 391 { 392 struct lwip_callback_msg *msg = arg; 393 PTCP_PCB pcb = msg->Input.Bind.Connection->SocketContext; 394 395 ASSERT(msg); 396 397 if (!msg->Input.Bind.Connection->SocketContext) 398 { 399 msg->Output.Bind.Error = ERR_CLSD; 400 goto done; 401 } 402 403 /* We're guaranteed that the local address is valid to bind at this point */ 404 pcb->so_options |= SOF_REUSEADDR; 405 406 msg->Output.Bind.Error = tcp_bind(pcb, 407 msg->Input.Bind.IpAddress, 408 ntohs(msg->Input.Bind.Port)); 409 410 done: 411 KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE); 412 } 413 414 err_t 415 LibTCPBind(PCONNECTION_ENDPOINT Connection, struct ip_addr *const ipaddr, const u16_t port) 416 { 417 struct lwip_callback_msg *msg; 418 err_t ret; 419 420 msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList); 421 if (msg) 422 { 423 KeInitializeEvent(&msg->Event, NotificationEvent, FALSE); 424 msg->Input.Bind.Connection = Connection; 425 msg->Input.Bind.IpAddress = ipaddr; 426 msg->Input.Bind.Port = port; 427 428 tcpip_callback_with_block(LibTCPBindCallback, msg, 1); 429 430 if (WaitForEventSafely(&msg->Event)) 431 ret = msg->Output.Bind.Error; 432 else 433 ret = ERR_CLSD; 434 435 ExFreeToNPagedLookasideList(&MessageLookasideList, msg); 436 437 return ret; 438 } 439 440 return ERR_MEM; 441 } 442 443 static 444 void 445 LibTCPListenCallback(void *arg) 446 { 447 struct lwip_callback_msg *msg = arg; 448 449 ASSERT(msg); 450 451 if (!msg->Input.Listen.Connection->SocketContext) 452 { 453 msg->Output.Listen.NewPcb = NULL; 454 goto done; 455 } 456 457 msg->Output.Listen.NewPcb = tcp_listen_with_backlog((PTCP_PCB)msg->Input.Listen.Connection->SocketContext, msg->Input.Listen.Backlog); 458 459 if (msg->Output.Listen.NewPcb) 460 { 461 tcp_accept(msg->Output.Listen.NewPcb, InternalAcceptEventHandler); 462 } 463 464 done: 465 KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE); 466 } 467 468 PTCP_PCB 469 LibTCPListen(PCONNECTION_ENDPOINT Connection, const u8_t backlog) 470 { 471 struct lwip_callback_msg *msg; 472 PTCP_PCB ret; 473 474 msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList); 475 if (msg) 476 { 477 KeInitializeEvent(&msg->Event, NotificationEvent, FALSE); 478 msg->Input.Listen.Connection = Connection; 479 msg->Input.Listen.Backlog = backlog; 480 481 tcpip_callback_with_block(LibTCPListenCallback, msg, 1); 482 483 if (WaitForEventSafely(&msg->Event)) 484 ret = msg->Output.Listen.NewPcb; 485 else 486 ret = NULL; 487 488 ExFreeToNPagedLookasideList(&MessageLookasideList, msg); 489 490 return ret; 491 } 492 493 return NULL; 494 } 495 496 static 497 void 498 LibTCPSendCallback(void *arg) 499 { 500 struct lwip_callback_msg *msg = arg; 501 PTCP_PCB pcb = msg->Input.Send.Connection->SocketContext; 502 ULONG SendLength; 503 UCHAR SendFlags; 504 505 ASSERT(msg); 506 507 if (!msg->Input.Send.Connection->SocketContext) 508 { 509 msg->Output.Send.Error = ERR_CLSD; 510 goto done; 511 } 512 513 if (msg->Input.Send.Connection->SendShutdown) 514 { 515 msg->Output.Send.Error = ERR_CLSD; 516 goto done; 517 } 518 519 SendFlags = TCP_WRITE_FLAG_COPY; 520 SendLength = msg->Input.Send.DataLength; 521 if (tcp_sndbuf(pcb) == 0) 522 { 523 /* No buffer space so return pending */ 524 msg->Output.Send.Error = ERR_INPROGRESS; 525 goto done; 526 } 527 else if (tcp_sndbuf(pcb) < SendLength) 528 { 529 /* We've got some room so let's send what we can */ 530 SendLength = tcp_sndbuf(pcb); 531 532 /* Don't set the push flag */ 533 SendFlags |= TCP_WRITE_FLAG_MORE; 534 } 535 536 msg->Output.Send.Error = tcp_write(pcb, 537 msg->Input.Send.Data, 538 SendLength, 539 SendFlags); 540 if (msg->Output.Send.Error == ERR_OK) 541 { 542 /* Queued successfully so try to send it */ 543 tcp_output((PTCP_PCB)msg->Input.Send.Connection->SocketContext); 544 msg->Output.Send.Information = SendLength; 545 } 546 else if (msg->Output.Send.Error == ERR_MEM) 547 { 548 /* The queue is too long */ 549 msg->Output.Send.Error = ERR_INPROGRESS; 550 } 551 552 done: 553 KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE); 554 } 555 556 err_t 557 LibTCPSend(PCONNECTION_ENDPOINT Connection, void *const dataptr, const u16_t len, u32_t *sent, const int safe) 558 { 559 err_t ret; 560 struct lwip_callback_msg *msg; 561 562 msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList); 563 if (msg) 564 { 565 KeInitializeEvent(&msg->Event, NotificationEvent, FALSE); 566 msg->Input.Send.Connection = Connection; 567 msg->Input.Send.Data = dataptr; 568 msg->Input.Send.DataLength = len; 569 570 if (safe) 571 LibTCPSendCallback(msg); 572 else 573 tcpip_callback_with_block(LibTCPSendCallback, msg, 1); 574 575 if (WaitForEventSafely(&msg->Event)) 576 ret = msg->Output.Send.Error; 577 else 578 ret = ERR_CLSD; 579 580 if (ret == ERR_OK) 581 *sent = msg->Output.Send.Information; 582 else 583 *sent = 0; 584 585 ExFreeToNPagedLookasideList(&MessageLookasideList, msg); 586 587 return ret; 588 } 589 590 return ERR_MEM; 591 } 592 593 static 594 void 595 LibTCPConnectCallback(void *arg) 596 { 597 struct lwip_callback_msg *msg = arg; 598 err_t Error; 599 600 ASSERT(arg); 601 602 if (!msg->Input.Connect.Connection->SocketContext) 603 { 604 msg->Output.Connect.Error = ERR_CLSD; 605 goto done; 606 } 607 608 tcp_recv((PTCP_PCB)msg->Input.Connect.Connection->SocketContext, InternalRecvEventHandler); 609 tcp_sent((PTCP_PCB)msg->Input.Connect.Connection->SocketContext, InternalSendEventHandler); 610 611 Error = tcp_connect((PTCP_PCB)msg->Input.Connect.Connection->SocketContext, 612 msg->Input.Connect.IpAddress, ntohs(msg->Input.Connect.Port), 613 InternalConnectEventHandler); 614 615 msg->Output.Connect.Error = Error == ERR_OK ? ERR_INPROGRESS : Error; 616 617 done: 618 KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE); 619 } 620 621 err_t 622 LibTCPConnect(PCONNECTION_ENDPOINT Connection, struct ip_addr *const ipaddr, const u16_t port) 623 { 624 struct lwip_callback_msg *msg; 625 err_t ret; 626 627 msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList); 628 if (msg) 629 { 630 KeInitializeEvent(&msg->Event, NotificationEvent, FALSE); 631 msg->Input.Connect.Connection = Connection; 632 msg->Input.Connect.IpAddress = ipaddr; 633 msg->Input.Connect.Port = port; 634 635 tcpip_callback_with_block(LibTCPConnectCallback, msg, 1); 636 637 if (WaitForEventSafely(&msg->Event)) 638 { 639 ret = msg->Output.Connect.Error; 640 } 641 else 642 ret = ERR_CLSD; 643 644 ExFreeToNPagedLookasideList(&MessageLookasideList, msg); 645 646 return ret; 647 } 648 649 return ERR_MEM; 650 } 651 652 static 653 void 654 LibTCPShutdownCallback(void *arg) 655 { 656 struct lwip_callback_msg *msg = arg; 657 PTCP_PCB pcb = msg->Input.Shutdown.Connection->SocketContext; 658 659 if (!msg->Input.Shutdown.Connection->SocketContext) 660 { 661 msg->Output.Shutdown.Error = ERR_CLSD; 662 goto done; 663 } 664 665 /* LwIP makes the (questionable) assumption that SHUTDOWN_RDWR is equivalent to tcp_close(). 666 * This assumption holds even if the shutdown calls are done separately (even through multiple 667 * WinSock shutdown() calls). This assumption means that lwIP has the right to deallocate our 668 * PCB without telling us if we shutdown TX and RX. To avoid these problems, we'll clear the 669 * socket context if we have called shutdown for TX and RX. 670 */ 671 if (msg->Input.Shutdown.shut_rx != msg->Input.Shutdown.shut_tx) { 672 if (msg->Input.Shutdown.shut_rx) { 673 msg->Output.Shutdown.Error = tcp_shutdown(pcb, TRUE, FALSE); 674 } 675 if (msg->Input.Shutdown.shut_tx) { 676 msg->Output.Shutdown.Error = tcp_shutdown(pcb, FALSE, TRUE); 677 } 678 } 679 else if (msg->Input.Shutdown.shut_rx) { 680 /* We received both RX and TX requests, which seems to mean closing connection from TDI. 681 * So call tcp_close, otherwise we risk to be put in TCP_WAIT_* states, which makes further 682 * attempts to close the socket to fail in this state. 683 */ 684 msg->Output.Shutdown.Error = tcp_close(pcb); 685 } 686 else { 687 /* This case shouldn't happen */ 688 DbgPrint("Requested socket shutdown(0, 0) !\n"); 689 } 690 691 if (!msg->Output.Shutdown.Error) 692 { 693 if (msg->Input.Shutdown.shut_rx) 694 { 695 msg->Input.Shutdown.Connection->ReceiveShutdown = TRUE; 696 msg->Input.Shutdown.Connection->ReceiveShutdownStatus = STATUS_FILE_CLOSED; 697 } 698 699 if (msg->Input.Shutdown.shut_tx) 700 msg->Input.Shutdown.Connection->SendShutdown = TRUE; 701 702 if (msg->Input.Shutdown.Connection->ReceiveShutdown && 703 msg->Input.Shutdown.Connection->SendShutdown) 704 { 705 /* The PCB is not ours anymore */ 706 msg->Input.Shutdown.Connection->SocketContext = NULL; 707 tcp_arg(pcb, NULL); 708 TCPFinEventHandler(msg->Input.Shutdown.Connection, ERR_CLSD); 709 } 710 } 711 712 done: 713 KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE); 714 } 715 716 err_t 717 LibTCPShutdown(PCONNECTION_ENDPOINT Connection, const int shut_rx, const int shut_tx) 718 { 719 struct lwip_callback_msg *msg; 720 err_t ret; 721 722 msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList); 723 if (msg) 724 { 725 KeInitializeEvent(&msg->Event, NotificationEvent, FALSE); 726 727 msg->Input.Shutdown.Connection = Connection; 728 msg->Input.Shutdown.shut_rx = shut_rx; 729 msg->Input.Shutdown.shut_tx = shut_tx; 730 731 tcpip_callback_with_block(LibTCPShutdownCallback, msg, 1); 732 733 if (WaitForEventSafely(&msg->Event)) 734 ret = msg->Output.Shutdown.Error; 735 else 736 ret = ERR_CLSD; 737 738 ExFreeToNPagedLookasideList(&MessageLookasideList, msg); 739 740 return ret; 741 } 742 743 return ERR_MEM; 744 } 745 746 static 747 void 748 LibTCPCloseCallback(void *arg) 749 { 750 struct lwip_callback_msg *msg = arg; 751 PTCP_PCB pcb = msg->Input.Close.Connection->SocketContext; 752 753 /* Empty the queue even if we're already "closed" */ 754 LibTCPEmptyQueue(msg->Input.Close.Connection); 755 756 /* Check if we've already been closed */ 757 if (msg->Input.Close.Connection->Closing) 758 { 759 msg->Output.Close.Error = ERR_OK; 760 goto done; 761 } 762 763 /* Enter "closing" mode if we're doing a normal close */ 764 if (msg->Input.Close.Callback) 765 msg->Input.Close.Connection->Closing = TRUE; 766 767 /* Check if the PCB was already "closed" but the client doesn't know it yet */ 768 if (!msg->Input.Close.Connection->SocketContext) 769 { 770 msg->Output.Close.Error = ERR_OK; 771 goto done; 772 } 773 774 /* Clear the PCB pointer and stop callbacks */ 775 msg->Input.Close.Connection->SocketContext = NULL; 776 tcp_arg(pcb, NULL); 777 778 /* This may generate additional callbacks but we don't care, 779 * because they're too inconsistent to rely on */ 780 msg->Output.Close.Error = tcp_close(pcb); 781 782 if (msg->Output.Close.Error) 783 { 784 /* Restore the PCB pointer */ 785 msg->Input.Close.Connection->SocketContext = pcb; 786 msg->Input.Close.Connection->Closing = FALSE; 787 } 788 else if (msg->Input.Close.Callback) 789 { 790 TCPFinEventHandler(msg->Input.Close.Connection, ERR_CLSD); 791 } 792 793 done: 794 KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE); 795 } 796 797 err_t 798 LibTCPClose(PCONNECTION_ENDPOINT Connection, const int safe, const int callback) 799 { 800 err_t ret; 801 struct lwip_callback_msg *msg; 802 803 msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList); 804 if (msg) 805 { 806 KeInitializeEvent(&msg->Event, NotificationEvent, FALSE); 807 808 msg->Input.Close.Connection = Connection; 809 msg->Input.Close.Callback = callback; 810 811 if (safe) 812 LibTCPCloseCallback(msg); 813 else 814 tcpip_callback_with_block(LibTCPCloseCallback, msg, 1); 815 816 if (WaitForEventSafely(&msg->Event)) 817 ret = msg->Output.Close.Error; 818 else 819 ret = ERR_CLSD; 820 821 ExFreeToNPagedLookasideList(&MessageLookasideList, msg); 822 823 return ret; 824 } 825 826 return ERR_MEM; 827 } 828 829 void 830 LibTCPAccept(PTCP_PCB pcb, struct tcp_pcb *listen_pcb, void *arg) 831 { 832 ASSERT(arg); 833 834 tcp_arg(pcb, NULL); 835 tcp_recv(pcb, InternalRecvEventHandler); 836 tcp_sent(pcb, InternalSendEventHandler); 837 tcp_err(pcb, InternalErrorEventHandler); 838 tcp_arg(pcb, arg); 839 840 tcp_accepted(listen_pcb); 841 } 842 843 err_t 844 LibTCPGetHostName(PTCP_PCB pcb, struct ip_addr *const ipaddr, u16_t *const port) 845 { 846 if (!pcb) 847 return ERR_CLSD; 848 849 *ipaddr = pcb->local_ip; 850 *port = pcb->local_port; 851 852 return ERR_OK; 853 } 854 855 err_t 856 LibTCPGetPeerName(PTCP_PCB pcb, struct ip_addr * const ipaddr, u16_t * const port) 857 { 858 if (!pcb) 859 return ERR_CLSD; 860 861 *ipaddr = pcb->remote_ip; 862 *port = pcb->remote_port; 863 864 return ERR_OK; 865 } 866 867 void 868 LibTCPSetNoDelay( 869 PTCP_PCB pcb, 870 BOOLEAN Set) 871 { 872 if (Set) 873 pcb->flags |= TF_NODELAY; 874 else 875 pcb->flags &= ~TF_NODELAY; 876 } 877 878 void 879 LibTCPGetSocketStatus( 880 PTCP_PCB pcb, 881 PULONG State) 882 { 883 /* Translate state from enum tcp_state -> MIB_TCP_STATE */ 884 *State = pcb->state + 1; 885 } 886