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