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