Merge my current work done on the kd++ branch:
[reactos.git] / reactos / 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 static
252 err_t
253 InternalAcceptEventHandler(void *arg, PTCP_PCB newpcb, const err_t err)
254 {
255 /* Make sure the socket didn't get closed */
256 if (!arg)
257 return ERR_ABRT;
258
259 TCPAcceptEventHandler(arg, newpcb);
260
261 /* Set in LibTCPAccept (called from TCPAcceptEventHandler) */
262 if (newpcb->callback_arg)
263 return ERR_OK;
264 else
265 return ERR_ABRT;
266 }
267
268 static
269 err_t
270 InternalConnectEventHandler(void *arg, PTCP_PCB pcb, const err_t err)
271 {
272 /* Make sure the socket didn't get closed */
273 if (!arg)
274 return ERR_OK;
275
276 TCPConnectEventHandler(arg, err);
277
278 return ERR_OK;
279 }
280
281 static
282 void
283 InternalErrorEventHandler(void *arg, const err_t err)
284 {
285 PCONNECTION_ENDPOINT Connection = arg;
286 KIRQL OldIrql;
287
288 /* Make sure the socket didn't get closed */
289 if (!arg) return;
290
291 /* Check if data is left to be read */
292 LockObject(Connection, &OldIrql);
293 if (IsListEmpty(&Connection->PacketQueue))
294 {
295 UnlockObject(Connection, OldIrql);
296
297 /* Deliver the error now */
298 TCPFinEventHandler(arg, err);
299 }
300 else
301 {
302 UnlockObject(Connection, OldIrql);
303
304 /* Defer the error delivery until all data is gone */
305 Connection->ReceiveShutdown = TRUE;
306 Connection->ReceiveShutdownStatus = TCPTranslateError(err);
307
308 TCPRecvEventHandler(arg);
309 }
310 }
311
312 static
313 void
314 LibTCPSocketCallback(void *arg)
315 {
316 struct lwip_callback_msg *msg = arg;
317
318 ASSERT(msg);
319
320 msg->Output.Socket.NewPcb = tcp_new();
321
322 if (msg->Output.Socket.NewPcb)
323 {
324 tcp_arg(msg->Output.Socket.NewPcb, msg->Input.Socket.Arg);
325 tcp_err(msg->Output.Socket.NewPcb, InternalErrorEventHandler);
326 }
327
328 KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
329 }
330
331 struct tcp_pcb *
332 LibTCPSocket(void *arg)
333 {
334 struct lwip_callback_msg *msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
335 struct tcp_pcb *ret;
336
337 if (msg)
338 {
339 KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
340 msg->Input.Socket.Arg = arg;
341
342 tcpip_callback_with_block(LibTCPSocketCallback, msg, 1);
343
344 if (WaitForEventSafely(&msg->Event))
345 ret = msg->Output.Socket.NewPcb;
346 else
347 ret = NULL;
348
349 ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
350
351 return ret;
352 }
353
354 return NULL;
355 }
356
357 static
358 void
359 LibTCPBindCallback(void *arg)
360 {
361 struct lwip_callback_msg *msg = arg;
362 PTCP_PCB pcb = msg->Input.Bind.Connection->SocketContext;
363
364 ASSERT(msg);
365
366 if (!msg->Input.Bind.Connection->SocketContext)
367 {
368 msg->Output.Bind.Error = ERR_CLSD;
369 goto done;
370 }
371
372 /* We're guaranteed that the local address is valid to bind at this point */
373 pcb->so_options |= SOF_REUSEADDR;
374
375 msg->Output.Bind.Error = tcp_bind(pcb,
376 msg->Input.Bind.IpAddress,
377 ntohs(msg->Input.Bind.Port));
378
379 done:
380 KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
381 }
382
383 err_t
384 LibTCPBind(PCONNECTION_ENDPOINT Connection, struct ip_addr *const ipaddr, const u16_t port)
385 {
386 struct lwip_callback_msg *msg;
387 err_t ret;
388
389 msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
390 if (msg)
391 {
392 KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
393 msg->Input.Bind.Connection = Connection;
394 msg->Input.Bind.IpAddress = ipaddr;
395 msg->Input.Bind.Port = port;
396
397 tcpip_callback_with_block(LibTCPBindCallback, msg, 1);
398
399 if (WaitForEventSafely(&msg->Event))
400 ret = msg->Output.Bind.Error;
401 else
402 ret = ERR_CLSD;
403
404 ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
405
406 return ret;
407 }
408
409 return ERR_MEM;
410 }
411
412 static
413 void
414 LibTCPListenCallback(void *arg)
415 {
416 struct lwip_callback_msg *msg = arg;
417
418 ASSERT(msg);
419
420 if (!msg->Input.Listen.Connection->SocketContext)
421 {
422 msg->Output.Listen.NewPcb = NULL;
423 goto done;
424 }
425
426 msg->Output.Listen.NewPcb = tcp_listen_with_backlog((PTCP_PCB)msg->Input.Listen.Connection->SocketContext, msg->Input.Listen.Backlog);
427
428 if (msg->Output.Listen.NewPcb)
429 {
430 tcp_accept(msg->Output.Listen.NewPcb, InternalAcceptEventHandler);
431 }
432
433 done:
434 KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
435 }
436
437 PTCP_PCB
438 LibTCPListen(PCONNECTION_ENDPOINT Connection, const u8_t backlog)
439 {
440 struct lwip_callback_msg *msg;
441 PTCP_PCB ret;
442
443 msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
444 if (msg)
445 {
446 KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
447 msg->Input.Listen.Connection = Connection;
448 msg->Input.Listen.Backlog = backlog;
449
450 tcpip_callback_with_block(LibTCPListenCallback, msg, 1);
451
452 if (WaitForEventSafely(&msg->Event))
453 ret = msg->Output.Listen.NewPcb;
454 else
455 ret = NULL;
456
457 ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
458
459 return ret;
460 }
461
462 return NULL;
463 }
464
465 static
466 void
467 LibTCPSendCallback(void *arg)
468 {
469 struct lwip_callback_msg *msg = arg;
470 PTCP_PCB pcb = msg->Input.Send.Connection->SocketContext;
471 ULONG SendLength;
472 UCHAR SendFlags;
473
474 ASSERT(msg);
475
476 if (!msg->Input.Send.Connection->SocketContext)
477 {
478 msg->Output.Send.Error = ERR_CLSD;
479 goto done;
480 }
481
482 if (msg->Input.Send.Connection->SendShutdown)
483 {
484 msg->Output.Send.Error = ERR_CLSD;
485 goto done;
486 }
487
488 SendFlags = TCP_WRITE_FLAG_COPY;
489 SendLength = msg->Input.Send.DataLength;
490 if (tcp_sndbuf(pcb) == 0)
491 {
492 /* No buffer space so return pending */
493 msg->Output.Send.Error = ERR_INPROGRESS;
494 goto done;
495 }
496 else if (tcp_sndbuf(pcb) < SendLength)
497 {
498 /* We've got some room so let's send what we can */
499 SendLength = tcp_sndbuf(pcb);
500
501 /* Don't set the push flag */
502 SendFlags |= TCP_WRITE_FLAG_MORE;
503 }
504
505 msg->Output.Send.Error = tcp_write(pcb,
506 msg->Input.Send.Data,
507 SendLength,
508 SendFlags);
509 if (msg->Output.Send.Error == ERR_OK)
510 {
511 /* Queued successfully so try to send it */
512 tcp_output((PTCP_PCB)msg->Input.Send.Connection->SocketContext);
513 msg->Output.Send.Information = SendLength;
514 }
515 else if (msg->Output.Send.Error == ERR_MEM)
516 {
517 /* The queue is too long */
518 msg->Output.Send.Error = ERR_INPROGRESS;
519 }
520
521 done:
522 KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
523 }
524
525 err_t
526 LibTCPSend(PCONNECTION_ENDPOINT Connection, void *const dataptr, const u16_t len, u32_t *sent, const int safe)
527 {
528 err_t ret;
529 struct lwip_callback_msg *msg;
530
531 msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
532 if (msg)
533 {
534 KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
535 msg->Input.Send.Connection = Connection;
536 msg->Input.Send.Data = dataptr;
537 msg->Input.Send.DataLength = len;
538
539 if (safe)
540 LibTCPSendCallback(msg);
541 else
542 tcpip_callback_with_block(LibTCPSendCallback, msg, 1);
543
544 if (WaitForEventSafely(&msg->Event))
545 ret = msg->Output.Send.Error;
546 else
547 ret = ERR_CLSD;
548
549 if (ret == ERR_OK)
550 *sent = msg->Output.Send.Information;
551 else
552 *sent = 0;
553
554 ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
555
556 return ret;
557 }
558
559 return ERR_MEM;
560 }
561
562 static
563 void
564 LibTCPConnectCallback(void *arg)
565 {
566 struct lwip_callback_msg *msg = arg;
567 err_t Error;
568
569 ASSERT(arg);
570
571 if (!msg->Input.Connect.Connection->SocketContext)
572 {
573 msg->Output.Connect.Error = ERR_CLSD;
574 goto done;
575 }
576
577 tcp_recv((PTCP_PCB)msg->Input.Connect.Connection->SocketContext, InternalRecvEventHandler);
578 tcp_sent((PTCP_PCB)msg->Input.Connect.Connection->SocketContext, InternalSendEventHandler);
579
580 Error = tcp_connect((PTCP_PCB)msg->Input.Connect.Connection->SocketContext,
581 msg->Input.Connect.IpAddress, ntohs(msg->Input.Connect.Port),
582 InternalConnectEventHandler);
583
584 msg->Output.Connect.Error = Error == ERR_OK ? ERR_INPROGRESS : Error;
585
586 done:
587 KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
588 }
589
590 err_t
591 LibTCPConnect(PCONNECTION_ENDPOINT Connection, struct ip_addr *const ipaddr, const u16_t port)
592 {
593 struct lwip_callback_msg *msg;
594 err_t ret;
595
596 msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
597 if (msg)
598 {
599 KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
600 msg->Input.Connect.Connection = Connection;
601 msg->Input.Connect.IpAddress = ipaddr;
602 msg->Input.Connect.Port = port;
603
604 tcpip_callback_with_block(LibTCPConnectCallback, msg, 1);
605
606 if (WaitForEventSafely(&msg->Event))
607 {
608 ret = msg->Output.Connect.Error;
609 }
610 else
611 ret = ERR_CLSD;
612
613 ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
614
615 return ret;
616 }
617
618 return ERR_MEM;
619 }
620
621 static
622 void
623 LibTCPShutdownCallback(void *arg)
624 {
625 struct lwip_callback_msg *msg = arg;
626 PTCP_PCB pcb = msg->Input.Shutdown.Connection->SocketContext;
627
628 if (!msg->Input.Shutdown.Connection->SocketContext)
629 {
630 msg->Output.Shutdown.Error = ERR_CLSD;
631 goto done;
632 }
633
634 /* These need to be called separately, otherwise we get a tcp_close() */
635 if (msg->Input.Shutdown.shut_rx) {
636 msg->Output.Shutdown.Error = tcp_shutdown(pcb, TRUE, FALSE);
637 }
638 if (msg->Input.Shutdown.shut_tx) {
639 msg->Output.Shutdown.Error = tcp_shutdown(pcb, FALSE, TRUE);
640 }
641
642 if (!msg->Output.Shutdown.Error)
643 {
644 if (msg->Input.Shutdown.shut_rx)
645 {
646 msg->Input.Shutdown.Connection->ReceiveShutdown = TRUE;
647 msg->Input.Shutdown.Connection->ReceiveShutdownStatus = STATUS_FILE_CLOSED;
648 }
649
650 if (msg->Input.Shutdown.shut_tx)
651 msg->Input.Shutdown.Connection->SendShutdown = TRUE;
652 }
653
654 done:
655 KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
656 }
657
658 err_t
659 LibTCPShutdown(PCONNECTION_ENDPOINT Connection, const int shut_rx, const int shut_tx)
660 {
661 struct lwip_callback_msg *msg;
662 err_t ret;
663
664 msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
665 if (msg)
666 {
667 KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
668
669 msg->Input.Shutdown.Connection = Connection;
670 msg->Input.Shutdown.shut_rx = shut_rx;
671 msg->Input.Shutdown.shut_tx = shut_tx;
672
673 tcpip_callback_with_block(LibTCPShutdownCallback, msg, 1);
674
675 if (WaitForEventSafely(&msg->Event))
676 ret = msg->Output.Shutdown.Error;
677 else
678 ret = ERR_CLSD;
679
680 ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
681
682 return ret;
683 }
684
685 return ERR_MEM;
686 }
687
688 static
689 void
690 LibTCPCloseCallback(void *arg)
691 {
692 struct lwip_callback_msg *msg = arg;
693 PTCP_PCB pcb = msg->Input.Close.Connection->SocketContext;
694
695 /* Empty the queue even if we're already "closed" */
696 LibTCPEmptyQueue(msg->Input.Close.Connection);
697
698 if (!msg->Input.Close.Connection->SocketContext)
699 {
700 msg->Output.Close.Error = ERR_OK;
701 goto done;
702 }
703
704 /* Clear the PCB pointer */
705 msg->Input.Close.Connection->SocketContext = NULL;
706
707 switch (pcb->state)
708 {
709 case CLOSED:
710 case LISTEN:
711 case SYN_SENT:
712 msg->Output.Close.Error = tcp_close(pcb);
713
714 if (!msg->Output.Close.Error && msg->Input.Close.Callback)
715 TCPFinEventHandler(msg->Input.Close.Connection, ERR_CLSD);
716 break;
717
718 default:
719 /* Abort the socket */
720 tcp_abort(pcb);
721 msg->Output.Close.Error = ERR_OK;
722 break;
723 }
724
725 if (msg->Output.Close.Error)
726 {
727 /* Restore the PCB pointer */
728 msg->Input.Close.Connection->SocketContext = pcb;
729 }
730
731 done:
732 KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
733 }
734
735 err_t
736 LibTCPClose(PCONNECTION_ENDPOINT Connection, const int safe, const int callback)
737 {
738 err_t ret;
739 struct lwip_callback_msg *msg;
740
741 msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
742 if (msg)
743 {
744 KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
745
746 msg->Input.Close.Connection = Connection;
747 msg->Input.Close.Callback = callback;
748
749 if (safe)
750 LibTCPCloseCallback(msg);
751 else
752 tcpip_callback_with_block(LibTCPCloseCallback, msg, 1);
753
754 if (WaitForEventSafely(&msg->Event))
755 ret = msg->Output.Close.Error;
756 else
757 ret = ERR_CLSD;
758
759 ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
760
761 return ret;
762 }
763
764 return ERR_MEM;
765 }
766
767 void
768 LibTCPAccept(PTCP_PCB pcb, struct tcp_pcb *listen_pcb, void *arg)
769 {
770 ASSERT(arg);
771
772 tcp_arg(pcb, NULL);
773 tcp_recv(pcb, InternalRecvEventHandler);
774 tcp_sent(pcb, InternalSendEventHandler);
775 tcp_err(pcb, InternalErrorEventHandler);
776 tcp_arg(pcb, arg);
777
778 tcp_accepted(listen_pcb);
779 }
780
781 err_t
782 LibTCPGetHostName(PTCP_PCB pcb, struct ip_addr *const ipaddr, u16_t *const port)
783 {
784 if (!pcb)
785 return ERR_CLSD;
786
787 *ipaddr = pcb->local_ip;
788 *port = pcb->local_port;
789
790 return ERR_OK;
791 }
792
793 err_t
794 LibTCPGetPeerName(PTCP_PCB pcb, struct ip_addr * const ipaddr, u16_t * const port)
795 {
796 if (!pcb)
797 return ERR_CLSD;
798
799 *ipaddr = pcb->remote_ip;
800 *port = pcb->remote_port;
801
802 return ERR_OK;
803 }