[IP]
[reactos.git] / lib / drivers / ip / transport / tcp / tcp.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS TCP/IP protocol driver
4 * FILE: transport/tcp/tcp.c
5 * PURPOSE: Transmission Control Protocol
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * Art Yerkes (arty@users.sf.net)
8 * REVISIONS:
9 * CSH 01/08-2000 Created
10 * arty 12/21/2004 Added accept
11 */
12
13 #include "precomp.h"
14
15 LONG TCP_IPIdentification = 0;
16 static BOOLEAN TCPInitialized = FALSE;
17 static NPAGED_LOOKASIDE_LIST TCPSegmentList;
18 PORT_SET TCPPorts;
19 CLIENT_DATA ClientInfo;
20
21 static VOID
22 ProcessCompletions(PCONNECTION_ENDPOINT Connection)
23 {
24 PLIST_ENTRY CurrentEntry;
25 PTDI_BUCKET Bucket;
26 PTCP_COMPLETION_ROUTINE Complete;
27
28 while ((CurrentEntry = ExInterlockedRemoveHeadList(&Connection->CompletionQueue,
29 &Connection->Lock)))
30 {
31 Bucket = CONTAINING_RECORD(CurrentEntry, TDI_BUCKET, Entry);
32 Complete = Bucket->Request.RequestNotifyObject;
33
34 Complete(Bucket->Request.RequestContext, Bucket->Status, Bucket->Information);
35
36 ExFreePoolWithTag(Bucket, TDI_BUCKET_TAG);
37 }
38
39 if (!Connection->SocketContext)
40 TCPFreeConnectionEndpoint(Connection);
41 }
42
43 VOID HandleSignalledConnection(PCONNECTION_ENDPOINT Connection)
44 {
45 PTDI_BUCKET Bucket;
46 PLIST_ENTRY Entry;
47 NTSTATUS Status;
48 PIRP Irp;
49 PMDL Mdl;
50 ULONG SocketError = 0;
51
52 TI_DbgPrint(MID_TRACE,("Handling signalled state on %x (%x)\n",
53 Connection, Connection->SocketContext));
54
55 if( !Connection->SocketContext || Connection->SignalState & SEL_FIN ) {
56 TI_DbgPrint(DEBUG_TCP, ("EOF From socket\n"));
57
58 /* If OskitTCP initiated the disconnect, try to read the socket error that occurred */
59 if (Connection->SocketContext)
60 SocketError = TCPTranslateError(OskitTCPGetSocketError(Connection->SocketContext));
61
62 /* Default to STATUS_CANCELLED if we initiated the disconnect or no socket error was reported */
63 if (!Connection->SocketContext || !SocketError)
64 SocketError = STATUS_CANCELLED;
65
66 while (!IsListEmpty(&Connection->ReceiveRequest))
67 {
68 Entry = RemoveHeadList( &Connection->ReceiveRequest );
69
70 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
71
72 Bucket->Status = SocketError;
73 Bucket->Information = 0;
74
75 InsertTailList(&Connection->CompletionQueue, &Bucket->Entry);
76 }
77
78 while (!IsListEmpty(&Connection->SendRequest))
79 {
80 Entry = RemoveHeadList( &Connection->SendRequest );
81
82 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
83
84 Bucket->Status = SocketError;
85 Bucket->Information = 0;
86
87 InsertTailList(&Connection->CompletionQueue, &Bucket->Entry);
88 }
89
90 while (!IsListEmpty(&Connection->ListenRequest))
91 {
92 Entry = RemoveHeadList( &Connection->ListenRequest );
93
94 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
95
96 Bucket->Status = SocketError;
97 Bucket->Information = 0;
98
99 InsertTailList(&Connection->CompletionQueue, &Bucket->Entry);
100 }
101
102 while (!IsListEmpty(&Connection->ConnectRequest))
103 {
104 Entry = RemoveHeadList( &Connection->ConnectRequest );
105
106 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
107
108 Bucket->Status = SocketError;
109 Bucket->Information = 0;
110
111 InsertTailList(&Connection->CompletionQueue, &Bucket->Entry);
112 }
113
114 Connection->SignalState = 0;
115 }
116
117 /* Things that can happen when we try the initial connection */
118 if( Connection->SignalState & SEL_CONNECT ) {
119 while (!IsListEmpty(&Connection->ConnectRequest)) {
120 Entry = RemoveHeadList( &Connection->ConnectRequest );
121
122 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
123
124 Bucket->Status = STATUS_SUCCESS;
125 Bucket->Information = 0;
126
127 InsertTailList(&Connection->CompletionQueue, &Bucket->Entry);
128 }
129 }
130
131 if( Connection->SignalState & SEL_ACCEPT ) {
132 /* Handle readable on a listening socket --
133 * TODO: Implement filtering
134 */
135 TI_DbgPrint(DEBUG_TCP,("Accepting new connection on %x (Queue: %s)\n",
136 Connection,
137 IsListEmpty(&Connection->ListenRequest) ?
138 "empty" : "nonempty"));
139
140 while (!IsListEmpty(&Connection->ListenRequest)) {
141 PIO_STACK_LOCATION IrpSp;
142
143 Entry = RemoveHeadList( &Connection->ListenRequest );
144
145 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
146
147 Irp = Bucket->Request.RequestContext;
148 IrpSp = IoGetCurrentIrpStackLocation( Irp );
149
150 TI_DbgPrint(DEBUG_TCP,("Getting the socket\n"));
151
152 Status = TCPServiceListeningSocket
153 ( Connection->AddressFile->Listener,
154 Bucket->AssociatedEndpoint,
155 (PTDI_REQUEST_KERNEL)&IrpSp->Parameters );
156
157 TI_DbgPrint(DEBUG_TCP,("Socket: Status: %x\n"));
158
159 if( Status == STATUS_PENDING ) {
160 InsertHeadList( &Connection->ListenRequest, &Bucket->Entry );
161 break;
162 } else {
163 Bucket->Status = Status;
164 Bucket->Information = 0;
165
166 InsertTailList(&Connection->CompletionQueue, &Bucket->Entry);
167 }
168 }
169 }
170
171 /* Things that happen after we're connected */
172 if( Connection->SignalState & SEL_READ ) {
173 TI_DbgPrint(DEBUG_TCP,("Readable: irp list %s\n",
174 IsListEmpty(&Connection->ReceiveRequest) ?
175 "empty" : "nonempty"));
176
177 while (!IsListEmpty(&Connection->ReceiveRequest)) {
178 OSK_UINT RecvLen = 0, Received = 0;
179 PVOID RecvBuffer = 0;
180
181 Entry = RemoveHeadList( &Connection->ReceiveRequest );
182
183 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
184
185 Irp = Bucket->Request.RequestContext;
186 Mdl = Irp->MdlAddress;
187
188 TI_DbgPrint(DEBUG_TCP,
189 ("Getting the user buffer from %x\n", Mdl));
190
191 NdisQueryBuffer( Mdl, &RecvBuffer, &RecvLen );
192
193 TI_DbgPrint(DEBUG_TCP,
194 ("Reading %d bytes to %x\n", RecvLen, RecvBuffer));
195
196 TI_DbgPrint(DEBUG_TCP, ("Connection: %x\n", Connection));
197 TI_DbgPrint
198 (DEBUG_TCP,
199 ("Connection->SocketContext: %x\n",
200 Connection->SocketContext));
201 TI_DbgPrint(DEBUG_TCP, ("RecvBuffer: %x\n", RecvBuffer));
202
203 Status = TCPTranslateError
204 ( OskitTCPRecv( Connection->SocketContext,
205 RecvBuffer,
206 RecvLen,
207 &Received,
208 0 ) );
209
210 TI_DbgPrint(DEBUG_TCP,("TCP Bytes: %d\n", Received));
211
212 if( Status == STATUS_PENDING ) {
213 InsertHeadList( &Connection->ReceiveRequest, &Bucket->Entry );
214 break;
215 } else {
216 TI_DbgPrint(DEBUG_TCP,
217 ("Completing Receive request: %x %x\n",
218 Bucket->Request, Status));
219
220 Bucket->Status = Status;
221 Bucket->Information = (Status == STATUS_SUCCESS) ? Received : 0;
222
223 InsertTailList(&Connection->CompletionQueue, &Bucket->Entry);
224 }
225 }
226 }
227 if( Connection->SignalState & SEL_WRITE ) {
228 TI_DbgPrint(DEBUG_TCP,("Writeable: irp list %s\n",
229 IsListEmpty(&Connection->SendRequest) ?
230 "empty" : "nonempty"));
231
232 while (!IsListEmpty(&Connection->SendRequest)) {
233 OSK_UINT SendLen = 0, Sent = 0;
234 PVOID SendBuffer = 0;
235
236 Entry = RemoveHeadList( &Connection->SendRequest );
237
238 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
239
240 Irp = Bucket->Request.RequestContext;
241 Mdl = Irp->MdlAddress;
242
243 TI_DbgPrint(DEBUG_TCP,
244 ("Getting the user buffer from %x\n", Mdl));
245
246 NdisQueryBuffer( Mdl, &SendBuffer, &SendLen );
247
248 TI_DbgPrint(DEBUG_TCP,
249 ("Writing %d bytes to %x\n", SendLen, SendBuffer));
250
251 TI_DbgPrint(DEBUG_TCP, ("Connection: %x\n", Connection));
252 TI_DbgPrint
253 (DEBUG_TCP,
254 ("Connection->SocketContext: %x\n",
255 Connection->SocketContext));
256
257 Status = TCPTranslateError
258 ( OskitTCPSend( Connection->SocketContext,
259 SendBuffer,
260 SendLen,
261 &Sent,
262 0 ) );
263
264 TI_DbgPrint(DEBUG_TCP,("TCP Bytes: %d\n", Sent));
265
266 if( Status == STATUS_PENDING ) {
267 InsertHeadList( &Connection->SendRequest, &Bucket->Entry );
268 break;
269 } else {
270 TI_DbgPrint(DEBUG_TCP,
271 ("Completing Send request: %x %x\n",
272 Bucket->Request, Status));
273
274 Bucket->Status = Status;
275 Bucket->Information = (Status == STATUS_SUCCESS) ? Sent : 0;
276
277 InsertTailList(&Connection->CompletionQueue, &Bucket->Entry);
278 }
279 }
280 }
281 }
282
283 static
284 VOID DrainSignals(VOID) {
285 PCONNECTION_ENDPOINT Connection;
286 PLIST_ENTRY CurrentEntry;
287 KIRQL OldIrql;
288
289 KeAcquireSpinLock(&ConnectionEndpointListLock, &OldIrql);
290 CurrentEntry = ConnectionEndpointListHead.Flink;
291 while (CurrentEntry != &ConnectionEndpointListHead)
292 {
293 Connection = CONTAINING_RECORD( CurrentEntry, CONNECTION_ENDPOINT,
294 ListEntry );
295 CurrentEntry = CurrentEntry->Flink;
296 KeReleaseSpinLock(&ConnectionEndpointListLock, OldIrql);
297
298 KeAcquireSpinLock(&Connection->Lock, &OldIrql);
299 if (Connection->SocketContext)
300 {
301 HandleSignalledConnection(Connection);
302 KeReleaseSpinLock(&Connection->Lock, OldIrql);
303
304 ProcessCompletions(Connection);
305 }
306 else
307 {
308 KeReleaseSpinLock(&Connection->Lock, OldIrql);
309 }
310
311 KeAcquireSpinLock(&ConnectionEndpointListLock, &OldIrql);
312 }
313 KeReleaseSpinLock(&ConnectionEndpointListLock, OldIrql);
314 }
315
316 PCONNECTION_ENDPOINT TCPAllocateConnectionEndpoint( PVOID ClientContext ) {
317 PCONNECTION_ENDPOINT Connection =
318 ExAllocatePoolWithTag(NonPagedPool, sizeof(CONNECTION_ENDPOINT),
319 CONN_ENDPT_TAG);
320 if (!Connection)
321 return Connection;
322
323 TI_DbgPrint(DEBUG_CPOINT, ("Connection point file object allocated at (0x%X).\n", Connection));
324
325 RtlZeroMemory(Connection, sizeof(CONNECTION_ENDPOINT));
326
327 /* Initialize spin lock that protects the connection endpoint file object */
328 KeInitializeSpinLock(&Connection->Lock);
329 InitializeListHead(&Connection->ConnectRequest);
330 InitializeListHead(&Connection->ListenRequest);
331 InitializeListHead(&Connection->ReceiveRequest);
332 InitializeListHead(&Connection->SendRequest);
333 InitializeListHead(&Connection->CompletionQueue);
334
335 /* Save client context pointer */
336 Connection->ClientContext = ClientContext;
337
338 /* Add connection endpoint to global list */
339 ExInterlockedInsertTailList(&ConnectionEndpointListHead,
340 &Connection->ListEntry,
341 &ConnectionEndpointListLock);
342
343 return Connection;
344 }
345
346 VOID TCPFreeConnectionEndpoint( PCONNECTION_ENDPOINT Connection ) {
347 KIRQL OldIrql;
348
349 TI_DbgPrint(DEBUG_TCP, ("Freeing TCP Endpoint\n"));
350
351 TcpipAcquireSpinLock(&ConnectionEndpointListLock, &OldIrql);
352 RemoveEntryList(&Connection->ListEntry);
353 TcpipReleaseSpinLock(&ConnectionEndpointListLock, OldIrql);
354
355 ExFreePoolWithTag( Connection, CONN_ENDPT_TAG );
356 }
357
358 NTSTATUS TCPSocket( PCONNECTION_ENDPOINT Connection,
359 UINT Family, UINT Type, UINT Proto ) {
360 NTSTATUS Status;
361 KIRQL OldIrql;
362
363 KeAcquireSpinLock(&Connection->Lock, &OldIrql);
364
365 TI_DbgPrint(DEBUG_TCP,("Called: Connection %x, Family %d, Type %d, "
366 "Proto %d\n",
367 Connection, Family, Type, Proto));
368
369 Status = TCPTranslateError( OskitTCPSocket( Connection,
370 &Connection->SocketContext,
371 Family,
372 Type,
373 Proto ) );
374
375 ASSERT_KM_POINTER(Connection->SocketContext);
376
377 TI_DbgPrint(DEBUG_TCP,("Connection->SocketContext %x\n",
378 Connection->SocketContext));
379
380 KeReleaseSpinLock(&Connection->Lock, OldIrql);
381
382 return Status;
383 }
384
385 VOID TCPReceive(PIP_INTERFACE Interface, PIP_PACKET IPPacket)
386 /*
387 * FUNCTION: Receives and queues TCP data
388 * ARGUMENTS:
389 * IPPacket = Pointer to an IP packet that was received
390 * NOTES:
391 * This is the low level interface for receiving TCP data
392 */
393 {
394 KIRQL OldIrql;
395
396 TI_DbgPrint(DEBUG_TCP,("Sending packet %d (%d) to oskit\n",
397 IPPacket->TotalSize,
398 IPPacket->HeaderSize));
399
400 KeAcquireSpinLock(&ClientInfo.Lock, &OldIrql);
401 ClientInfo.Unlocked = TRUE;
402
403 OskitTCPReceiveDatagram( IPPacket->Header,
404 IPPacket->TotalSize,
405 IPPacket->HeaderSize );
406
407 ClientInfo.Unlocked = FALSE;
408 KeReleaseSpinLock(&ClientInfo.Lock, OldIrql);
409 }
410
411 /* event.c */
412 int TCPSocketState( void *ClientData,
413 void *WhichSocket,
414 void *WhichConnection,
415 OSK_UINT NewState );
416
417 int TCPPacketSend( void *ClientData,
418 OSK_PCHAR Data,
419 OSK_UINT Len );
420
421 POSK_IFADDR TCPFindInterface( void *ClientData,
422 OSK_UINT AddrType,
423 OSK_UINT FindType,
424 OSK_SOCKADDR *ReqAddr );
425
426 NTSTATUS TCPMemStartup( void );
427 void *TCPMalloc( void *ClientData,
428 OSK_UINT bytes, OSK_PCHAR file, OSK_UINT line );
429 void TCPFree( void *ClientData,
430 void *data, OSK_PCHAR file, OSK_UINT line );
431 void TCPMemShutdown( void );
432
433 OSKITTCP_EVENT_HANDLERS EventHandlers = {
434 NULL, /* Client Data */
435 TCPSocketState, /* SocketState */
436 TCPPacketSend, /* PacketSend */
437 TCPFindInterface, /* FindInterface */
438 TCPMalloc, /* Malloc */
439 TCPFree, /* Free */
440 NULL, /* Sleep */
441 NULL, /* Wakeup */
442 };
443
444 static KEVENT TimerLoopEvent;
445 static HANDLE TimerThreadHandle;
446
447 /*
448 * We are running 2 timers here, one with a 200ms interval (fast) and the other
449 * with a 500ms interval (slow). So we need to time out at 200, 400, 500, 600,
450 * 800, 1000 and process the "fast" events at 200, 400, 600, 800, 1000 and the
451 * "slow" events at 500 and 1000.
452 */
453 static VOID NTAPI
454 TimerThread(PVOID Context)
455 {
456 LARGE_INTEGER Timeout;
457 NTSTATUS Status;
458 unsigned Current, NextFast, NextSlow, Next;
459
460 Current = 0;
461 Next = 0;
462 NextFast = 0;
463 NextSlow = 0;
464 while ( 1 ) {
465 if (Next == NextFast) {
466 NextFast += 2;
467 }
468 if (Next == NextSlow) {
469 NextSlow += 5;
470 }
471 Next = min(NextFast, NextSlow);
472 Timeout.QuadPart = (LONGLONG) (Next - Current) * -1000000; /* 100 ms */
473 Status = KeWaitForSingleObject(&TimerLoopEvent, Executive, KernelMode,
474 FALSE, &Timeout);
475 if (Status != STATUS_TIMEOUT) {
476 PsTerminateSystemThread(Status);
477 }
478
479 TimerOskitTCP( Next == NextFast, Next == NextSlow );
480 DrainSignals();
481
482 Current = Next;
483 if (10 <= Current) {
484 Current = 0;
485 Next = 0;
486 NextFast = 0;
487 NextSlow = 0;
488 }
489 }
490 }
491
492 static VOID
493 StartTimer(VOID)
494 {
495 KeInitializeEvent(&TimerLoopEvent, NotificationEvent, FALSE);
496 PsCreateSystemThread(&TimerThreadHandle, THREAD_ALL_ACCESS, 0, 0, 0,
497 TimerThread, NULL);
498 }
499
500 NTSTATUS TCPStartup(VOID)
501 /*
502 * FUNCTION: Initializes the TCP subsystem
503 * RETURNS:
504 * Status of operation
505 */
506 {
507 NTSTATUS Status;
508
509 Status = TCPMemStartup();
510 if ( ! NT_SUCCESS(Status) ) {
511 return Status;
512 }
513
514 Status = PortsStartup( &TCPPorts, 1, 0xfffe );
515 if( !NT_SUCCESS(Status) ) {
516 TCPMemShutdown();
517 return Status;
518 }
519
520 KeInitializeSpinLock(&ClientInfo.Lock);
521 ClientInfo.Unlocked = FALSE;
522
523 RegisterOskitTCPEventHandlers( &EventHandlers );
524 InitOskitTCP();
525
526 /* Register this protocol with IP layer */
527 IPRegisterProtocol(IPPROTO_TCP, TCPReceive);
528
529 ExInitializeNPagedLookasideList(
530 &TCPSegmentList, /* Lookaside list */
531 NULL, /* Allocate routine */
532 NULL, /* Free routine */
533 0, /* Flags */
534 sizeof(TCP_SEGMENT), /* Size of each entry */
535 'SPCT', /* Tag */
536 0); /* Depth */
537
538 StartTimer();
539
540 TCPInitialized = TRUE;
541
542 return STATUS_SUCCESS;
543 }
544
545
546 NTSTATUS TCPShutdown(VOID)
547 /*
548 * FUNCTION: Shuts down the TCP subsystem
549 * RETURNS:
550 * Status of operation
551 */
552 {
553 LARGE_INTEGER WaitForThread;
554
555 if (!TCPInitialized)
556 return STATUS_SUCCESS;
557
558 WaitForThread.QuadPart = -2500000; /* 250 ms */
559 KeSetEvent(&TimerLoopEvent, IO_NO_INCREMENT, FALSE);
560 ZwWaitForSingleObject(TimerThreadHandle, FALSE, &WaitForThread);
561
562 /* Deregister this protocol with IP layer */
563 IPRegisterProtocol(IPPROTO_TCP, NULL);
564
565 ExDeleteNPagedLookasideList(&TCPSegmentList);
566
567 TCPInitialized = FALSE;
568
569 DeinitOskitTCP();
570
571 PortsShutdown( &TCPPorts );
572
573 TCPMemShutdown();
574
575 return STATUS_SUCCESS;
576 }
577
578 NTSTATUS TCPTranslateError( int OskitError ) {
579 NTSTATUS Status;
580
581 switch( OskitError ) {
582 case 0: Status = STATUS_SUCCESS; break;
583 case OSK_EADDRNOTAVAIL: Status = STATUS_INVALID_ADDRESS; break;
584 case OSK_EAFNOSUPPORT: Status = STATUS_INVALID_CONNECTION; break;
585 case OSK_ECONNREFUSED: Status = STATUS_REMOTE_NOT_LISTENING; break;
586 case OSK_ECONNRESET:
587 case OSK_ECONNABORTED: Status = STATUS_REMOTE_DISCONNECT; break;
588 case OSK_EWOULDBLOCK:
589 case OSK_EINPROGRESS: Status = STATUS_PENDING; break;
590 case OSK_EINVAL: Status = STATUS_INVALID_PARAMETER; break;
591 case OSK_ENOMEM:
592 case OSK_ENOBUFS: Status = STATUS_INSUFFICIENT_RESOURCES; break;
593 case OSK_ESHUTDOWN: Status = STATUS_FILE_CLOSED; break;
594 case OSK_EMSGSIZE: Status = STATUS_BUFFER_TOO_SMALL; break;
595 case OSK_ETIMEDOUT: Status = STATUS_TIMEOUT; break;
596 case OSK_ENETUNREACH: Status = STATUS_NETWORK_UNREACHABLE; break;
597 case OSK_EFAULT: Status = STATUS_ACCESS_VIOLATION; break;
598 default:
599 DbgPrint("OskitTCP returned unhandled error code: %d\n", OskitError);
600 Status = STATUS_INVALID_CONNECTION;
601 break;
602 }
603
604 TI_DbgPrint(DEBUG_TCP,("Error %d -> %x\n", OskitError, Status));
605 return Status;
606 }
607
608 NTSTATUS TCPConnect
609 ( PCONNECTION_ENDPOINT Connection,
610 PTDI_CONNECTION_INFORMATION ConnInfo,
611 PTDI_CONNECTION_INFORMATION ReturnInfo,
612 PTCP_COMPLETION_ROUTINE Complete,
613 PVOID Context ) {
614 NTSTATUS Status;
615 SOCKADDR_IN AddressToConnect = { 0 }, AddressToBind = { 0 };
616 IP_ADDRESS RemoteAddress;
617 USHORT RemotePort;
618 PTDI_BUCKET Bucket;
619 PNEIGHBOR_CACHE_ENTRY NCE;
620 KIRQL OldIrql;
621
622 TI_DbgPrint(DEBUG_TCP,("TCPConnect: Called\n"));
623
624 Status = AddrBuildAddress
625 ((PTRANSPORT_ADDRESS)ConnInfo->RemoteAddress,
626 &RemoteAddress,
627 &RemotePort);
628
629 if (!NT_SUCCESS(Status)) {
630 TI_DbgPrint(DEBUG_TCP, ("Could not AddrBuildAddress in TCPConnect\n"));
631 return Status;
632 }
633
634 /* Freed in TCPSocketState */
635 TI_DbgPrint(DEBUG_TCP,
636 ("Connecting to address %x:%x\n",
637 RemoteAddress.Address.IPv4Address,
638 RemotePort));
639
640 AddressToConnect.sin_family = AF_INET;
641 AddressToBind = AddressToConnect;
642
643 KeAcquireSpinLock(&Connection->Lock, &OldIrql);
644
645 if (!Connection->AddressFile)
646 {
647 KeReleaseSpinLock(&Connection->Lock, OldIrql);
648 return STATUS_INVALID_PARAMETER;
649 }
650
651 if (AddrIsUnspecified(&Connection->AddressFile->Address))
652 {
653 if (!(NCE = RouteGetRouteToDestination(&RemoteAddress)))
654 {
655 KeReleaseSpinLock(&Connection->Lock, OldIrql);
656 return STATUS_NETWORK_UNREACHABLE;
657 }
658
659 AddressToBind.sin_addr.s_addr = NCE->Interface->Unicast.Address.IPv4Address;
660 }
661 else
662 {
663 AddressToBind.sin_addr.s_addr = Connection->AddressFile->Address.Address.IPv4Address;
664 }
665
666 Status = TCPTranslateError
667 ( OskitTCPBind( Connection->SocketContext,
668 &AddressToBind,
669 sizeof(AddressToBind) ) );
670
671 if (NT_SUCCESS(Status)) {
672 memcpy( &AddressToConnect.sin_addr,
673 &RemoteAddress.Address.IPv4Address,
674 sizeof(AddressToConnect.sin_addr) );
675 AddressToConnect.sin_port = RemotePort;
676
677 Status = TCPTranslateError
678 ( OskitTCPConnect( Connection->SocketContext,
679 &AddressToConnect,
680 sizeof(AddressToConnect) ) );
681
682 KeReleaseSpinLock(&Connection->Lock, OldIrql);
683
684 if (Status == STATUS_PENDING)
685 {
686 Bucket = ExAllocatePoolWithTag( NonPagedPool, sizeof(*Bucket), TDI_BUCKET_TAG );
687 if( !Bucket )
688 {
689 return STATUS_NO_MEMORY;
690 }
691
692 Bucket->Request.RequestNotifyObject = (PVOID)Complete;
693 Bucket->Request.RequestContext = Context;
694
695 ExInterlockedInsertTailList( &Connection->ConnectRequest, &Bucket->Entry,
696 &Connection->Lock );
697 }
698 } else {
699 KeReleaseSpinLock(&Connection->Lock, OldIrql);
700 }
701
702 return Status;
703 }
704
705 NTSTATUS TCPDisconnect
706 ( PCONNECTION_ENDPOINT Connection,
707 UINT Flags,
708 PTDI_CONNECTION_INFORMATION ConnInfo,
709 PTDI_CONNECTION_INFORMATION ReturnInfo,
710 PTCP_COMPLETION_ROUTINE Complete,
711 PVOID Context ) {
712 NTSTATUS Status = STATUS_INVALID_PARAMETER;
713 KIRQL OldIrql;
714
715 TI_DbgPrint(DEBUG_TCP,("started\n"));
716
717 KeAcquireSpinLock(&Connection->Lock, &OldIrql);
718
719 if (Flags & TDI_DISCONNECT_RELEASE)
720 Status = TCPTranslateError(OskitTCPDisconnect(Connection->SocketContext));
721
722 if ((Flags & TDI_DISCONNECT_ABORT) || !Flags)
723 Status = TCPTranslateError(OskitTCPShutdown(Connection->SocketContext, FWRITE | FREAD));
724
725 KeReleaseSpinLock(&Connection->Lock, OldIrql);
726
727 TI_DbgPrint(DEBUG_TCP,("finished %x\n", Status));
728
729 return Status;
730 }
731
732 NTSTATUS TCPClose
733 ( PCONNECTION_ENDPOINT Connection ) {
734 NTSTATUS Status;
735 KIRQL OldIrql;
736 PVOID Socket;
737
738 TI_DbgPrint(DEBUG_TCP,("TCPClose started\n"));
739
740 KeAcquireSpinLock(&Connection->Lock, &OldIrql);
741 Socket = Connection->SocketContext;
742 Connection->SocketContext = NULL;
743 Status = TCPTranslateError( OskitTCPClose( Socket ) );
744 if (!NT_SUCCESS(Status))
745 {
746 Connection->SocketContext = Socket;
747 }
748 KeReleaseSpinLock(&Connection->Lock, OldIrql);
749
750 TI_DbgPrint(DEBUG_TCP,("TCPClose finished %x\n", Status));
751
752 return Status;
753 }
754
755 NTSTATUS TCPReceiveData
756 ( PCONNECTION_ENDPOINT Connection,
757 PNDIS_BUFFER Buffer,
758 ULONG ReceiveLength,
759 PULONG BytesReceived,
760 ULONG ReceiveFlags,
761 PTCP_COMPLETION_ROUTINE Complete,
762 PVOID Context ) {
763 PVOID DataBuffer;
764 UINT DataLen, Received = 0;
765 NTSTATUS Status;
766 PTDI_BUCKET Bucket;
767 KIRQL OldIrql;
768
769 TI_DbgPrint(DEBUG_TCP,("Called for %d bytes (on socket %x)\n",
770 ReceiveLength, Connection->SocketContext));
771
772 NdisQueryBuffer( Buffer, &DataBuffer, &DataLen );
773
774 TI_DbgPrint(DEBUG_TCP,("TCP>|< Got an MDL %x (%x:%d)\n", Buffer, DataBuffer, DataLen));
775
776 KeAcquireSpinLock(&Connection->Lock, &OldIrql);
777
778 ASSERT_KM_POINTER(Connection->SocketContext);
779
780 Status = TCPTranslateError
781 ( OskitTCPRecv
782 ( Connection->SocketContext,
783 DataBuffer,
784 DataLen,
785 &Received,
786 ReceiveFlags ) );
787
788 KeReleaseSpinLock(&Connection->Lock, OldIrql);
789
790 TI_DbgPrint(DEBUG_TCP,("OskitTCPReceive: %x, %d\n", Status, Received));
791
792 /* Keep this request around ... there was no data yet */
793 if( Status == STATUS_PENDING ) {
794 /* Freed in TCPSocketState */
795 Bucket = ExAllocatePoolWithTag( NonPagedPool, sizeof(*Bucket), TDI_BUCKET_TAG );
796 if( !Bucket ) {
797 TI_DbgPrint(DEBUG_TCP,("Failed to allocate bucket\n"));
798 return STATUS_NO_MEMORY;
799 }
800
801 Bucket->Request.RequestNotifyObject = Complete;
802 Bucket->Request.RequestContext = Context;
803 *BytesReceived = 0;
804
805 ExInterlockedInsertTailList( &Connection->ReceiveRequest, &Bucket->Entry,
806 &Connection->Lock );
807 TI_DbgPrint(DEBUG_TCP,("Queued read irp\n"));
808 } else {
809 TI_DbgPrint(DEBUG_TCP,("Got status %x, bytes %d\n", Status, Received));
810 *BytesReceived = Received;
811 }
812
813 TI_DbgPrint(DEBUG_TCP,("Status %x\n", Status));
814
815 return Status;
816 }
817
818 NTSTATUS TCPSendData
819 ( PCONNECTION_ENDPOINT Connection,
820 PCHAR BufferData,
821 ULONG SendLength,
822 PULONG BytesSent,
823 ULONG Flags,
824 PTCP_COMPLETION_ROUTINE Complete,
825 PVOID Context ) {
826 UINT Sent = 0;
827 NTSTATUS Status;
828 PTDI_BUCKET Bucket;
829 KIRQL OldIrql;
830
831 KeAcquireSpinLock(&Connection->Lock, &OldIrql);
832
833 TI_DbgPrint(DEBUG_TCP,("Called for %d bytes (on socket %x)\n",
834 SendLength, Connection->SocketContext));
835
836 ASSERT_KM_POINTER(Connection->SocketContext);
837
838 TI_DbgPrint(DEBUG_TCP,("Connection = %x\n", Connection));
839 TI_DbgPrint(DEBUG_TCP,("Connection->SocketContext = %x\n",
840 Connection->SocketContext));
841
842 Status = TCPTranslateError
843 ( OskitTCPSend( Connection->SocketContext,
844 (OSK_PCHAR)BufferData, SendLength,
845 &Sent, 0 ) );
846
847 KeReleaseSpinLock(&Connection->Lock, OldIrql);
848
849 TI_DbgPrint(DEBUG_TCP,("OskitTCPSend: %x, %d\n", Status, Sent));
850
851 /* Keep this request around ... there was no data yet */
852 if( Status == STATUS_PENDING ) {
853 /* Freed in TCPSocketState */
854 Bucket = ExAllocatePoolWithTag( NonPagedPool, sizeof(*Bucket), TDI_BUCKET_TAG );
855 if( !Bucket ) {
856 TI_DbgPrint(DEBUG_TCP,("Failed to allocate bucket\n"));
857 return STATUS_NO_MEMORY;
858 }
859
860 Bucket->Request.RequestNotifyObject = Complete;
861 Bucket->Request.RequestContext = Context;
862 *BytesSent = 0;
863
864 ExInterlockedInsertTailList( &Connection->SendRequest, &Bucket->Entry,
865 &Connection->Lock );
866 TI_DbgPrint(DEBUG_TCP,("Queued write irp\n"));
867 } else {
868 TI_DbgPrint(DEBUG_TCP,("Got status %x, bytes %d\n", Status, Sent));
869 *BytesSent = Sent;
870 }
871
872 TI_DbgPrint(DEBUG_TCP,("Status %x\n", Status));
873
874 return Status;
875 }
876
877 UINT TCPAllocatePort( UINT HintPort ) {
878 if( HintPort ) {
879 if( AllocatePort( &TCPPorts, HintPort ) ) return HintPort;
880 else {
881 TI_DbgPrint
882 (MID_TRACE,("We got a hint port but couldn't allocate it\n"));
883 return (UINT)-1;
884 }
885 } else return AllocatePortFromRange( &TCPPorts, 1024, 5000 );
886 }
887
888 VOID TCPFreePort( UINT Port ) {
889 DeallocatePort( &TCPPorts, Port );
890 }
891
892 NTSTATUS TCPGetSockAddress
893 ( PCONNECTION_ENDPOINT Connection,
894 PTRANSPORT_ADDRESS Address,
895 BOOLEAN GetRemote ) {
896 OSK_UINT LocalAddress, RemoteAddress;
897 OSK_UI16 LocalPort, RemotePort;
898 PTA_IP_ADDRESS AddressIP = (PTA_IP_ADDRESS)Address;
899 NTSTATUS Status;
900 KIRQL OldIrql;
901
902 KeAcquireSpinLock(&Connection->Lock, &OldIrql);
903
904 Status = TCPTranslateError(OskitTCPGetAddress(Connection->SocketContext,
905 &LocalAddress, &LocalPort,
906 &RemoteAddress, &RemotePort));
907
908 KeReleaseSpinLock(&Connection->Lock, OldIrql);
909
910 if (!NT_SUCCESS(Status))
911 return Status;
912
913 AddressIP->TAAddressCount = 1;
914 AddressIP->Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP;
915 AddressIP->Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
916 AddressIP->Address[0].Address[0].sin_port = GetRemote ? RemotePort : LocalPort;
917 AddressIP->Address[0].Address[0].in_addr = GetRemote ? RemoteAddress : LocalAddress;
918
919 return Status;
920 }
921
922 BOOLEAN TCPRemoveIRP( PCONNECTION_ENDPOINT Endpoint, PIRP Irp ) {
923 PLIST_ENTRY Entry;
924 PLIST_ENTRY ListHead[4];
925 KIRQL OldIrql;
926 PTDI_BUCKET Bucket;
927 UINT i = 0;
928 BOOLEAN Found = FALSE;
929
930 ListHead[0] = &Endpoint->SendRequest;
931 ListHead[1] = &Endpoint->ReceiveRequest;
932 ListHead[2] = &Endpoint->ConnectRequest;
933 ListHead[3] = &Endpoint->ListenRequest;
934
935 TcpipAcquireSpinLock( &Endpoint->Lock, &OldIrql );
936
937 for( i = 0; i < 4; i++ )
938 {
939 for( Entry = ListHead[i]->Flink;
940 Entry != ListHead[i];
941 Entry = Entry->Flink )
942 {
943 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
944 if( Bucket->Request.RequestContext == Irp )
945 {
946 RemoveEntryList( &Bucket->Entry );
947 ExFreePoolWithTag( Bucket, TDI_BUCKET_TAG );
948 Found = TRUE;
949 break;
950 }
951 }
952 }
953
954 TcpipReleaseSpinLock( &Endpoint->Lock, OldIrql );
955
956 return Found;
957 }
958
959 /* EOF */