[SERVMAN]
[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
22 CompleteBucketWorker(PVOID Context)
23 {
24 PTDI_BUCKET Bucket = Context;
25 PCONNECTION_ENDPOINT Connection;
26 PTCP_COMPLETION_ROUTINE Complete;
27
28 ASSERT(Bucket);
29
30 Connection = Bucket->AssociatedEndpoint;
31 Complete = Bucket->Request.RequestNotifyObject;
32
33 Complete(Bucket->Request.RequestContext, Bucket->Status, Bucket->Information);
34
35 ExFreePoolWithTag(Bucket, TDI_BUCKET_TAG);
36
37 DereferenceObject(Connection);
38 }
39
40 VOID
41 CompleteBucket(PCONNECTION_ENDPOINT Connection, PTDI_BUCKET Bucket)
42 {
43 Bucket->AssociatedEndpoint = Connection;
44 ReferenceObject(Connection);
45 ChewCreate(CompleteBucketWorker, Bucket);
46 }
47
48 VOID HandleSignalledConnection(PCONNECTION_ENDPOINT Connection)
49 {
50 PTDI_BUCKET Bucket;
51 PLIST_ENTRY Entry;
52 NTSTATUS Status;
53 PIRP Irp;
54 PMDL Mdl;
55
56 if (ClientInfo.Unlocked)
57 LockObjectAtDpcLevel(Connection);
58
59 TI_DbgPrint(MID_TRACE,("Handling signalled state on %x\n",
60 Connection));
61
62 /* Things that can happen when we try the initial connection */
63 if( Connection->SignalState & (SEL_CONNECT | SEL_FIN | SEL_ERROR) ) {
64 while (!IsListEmpty(&Connection->ConnectRequest)) {
65 Entry = RemoveHeadList( &Connection->ConnectRequest );
66
67 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
68
69 if (Connection->SignalState & SEL_ERROR)
70 {
71 Bucket->Status = TCPTranslateError(OskitTCPGetSocketError(Connection));
72 }
73 else if (Connection->SignalState & SEL_FIN)
74 {
75 Bucket->Status = STATUS_CANCELLED;
76 }
77 else
78 {
79 Bucket->Status = STATUS_SUCCESS;
80 }
81 Bucket->Information = 0;
82
83 CompleteBucket(Connection, Bucket);
84 }
85 }
86
87 if( Connection->SignalState & (SEL_ACCEPT | SEL_FIN | SEL_ERROR) ) {
88 /* Handle readable on a listening socket --
89 * TODO: Implement filtering
90 */
91 TI_DbgPrint(DEBUG_TCP,("Accepting new connection on %x (Queue: %s)\n",
92 Connection,
93 IsListEmpty(&Connection->ListenRequest) ?
94 "empty" : "nonempty"));
95
96 while (!IsListEmpty(&Connection->ListenRequest)) {
97 PIO_STACK_LOCATION IrpSp;
98
99 Entry = RemoveHeadList( &Connection->ListenRequest );
100
101 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
102
103 Irp = Bucket->Request.RequestContext;
104 IrpSp = IoGetCurrentIrpStackLocation( Irp );
105
106 TI_DbgPrint(DEBUG_TCP,("Getting the socket\n"));
107
108 if (Connection->SignalState & SEL_ERROR)
109 {
110 Status = TCPTranslateError(OskitTCPGetSocketError(Connection));
111 }
112 else if (Connection->SignalState & SEL_FIN)
113 {
114 Status = STATUS_CANCELLED;
115 }
116 else
117 {
118 Status = TCPServiceListeningSocket(Connection->AddressFile->Listener,
119 Bucket->AssociatedEndpoint,
120 (PTDI_REQUEST_KERNEL)&IrpSp->Parameters);
121 }
122
123 TI_DbgPrint(DEBUG_TCP,("Socket: Status: %x\n"));
124
125 if( Status == STATUS_PENDING ) {
126 InsertHeadList( &Connection->ListenRequest, &Bucket->Entry );
127 break;
128 } else {
129 Bucket->Status = Status;
130 Bucket->Information = 0;
131 DereferenceObject(Bucket->AssociatedEndpoint);
132
133 CompleteBucket(Connection, Bucket);
134 }
135 }
136 }
137
138 /* Things that happen after we're connected */
139 if( Connection->SignalState & (SEL_READ | SEL_FIN | SEL_ERROR) ) {
140 TI_DbgPrint(DEBUG_TCP,("Readable: irp list %s\n",
141 IsListEmpty(&Connection->ReceiveRequest) ?
142 "empty" : "nonempty"));
143
144 while (!IsListEmpty(&Connection->ReceiveRequest)) {
145 OSK_UINT RecvLen = 0, Received = 0;
146 PVOID RecvBuffer = 0;
147
148 Entry = RemoveHeadList( &Connection->ReceiveRequest );
149
150 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
151
152 Irp = Bucket->Request.RequestContext;
153 Mdl = Irp->MdlAddress;
154
155 TI_DbgPrint(DEBUG_TCP,
156 ("Getting the user buffer from %x\n", Mdl));
157
158 NdisQueryBuffer( Mdl, &RecvBuffer, &RecvLen );
159
160 TI_DbgPrint(DEBUG_TCP,
161 ("Reading %d bytes to %x\n", RecvLen, RecvBuffer));
162
163 TI_DbgPrint(DEBUG_TCP, ("Connection: %x\n", Connection));
164 TI_DbgPrint(DEBUG_TCP, ("RecvBuffer: %x\n", RecvBuffer));
165
166 if (Connection->SignalState & SEL_ERROR)
167 {
168 Status = TCPTranslateError(OskitTCPGetSocketError(Connection));
169 }
170 else if (Connection->SignalState & SEL_FIN)
171 {
172 /* We got here because of a SEL_FIN event */
173 Status = STATUS_CANCELLED;
174 }
175 else
176 {
177 Status = TCPTranslateError(OskitTCPRecv(Connection,
178 RecvBuffer,
179 RecvLen,
180 &Received,
181 0));
182 }
183
184 TI_DbgPrint(DEBUG_TCP,("TCP Bytes: %d\n", Received));
185
186 if( Status == STATUS_PENDING ) {
187 InsertHeadList( &Connection->ReceiveRequest, &Bucket->Entry );
188 break;
189 } else {
190 TI_DbgPrint(DEBUG_TCP,
191 ("Completing Receive request: %x %x\n",
192 Bucket->Request, Status));
193
194 Bucket->Status = Status;
195 Bucket->Information = (Bucket->Status == STATUS_SUCCESS) ? Received : 0;
196
197 CompleteBucket(Connection, Bucket);
198 }
199 }
200 }
201 if( Connection->SignalState & (SEL_WRITE | SEL_FIN | SEL_ERROR) ) {
202 TI_DbgPrint(DEBUG_TCP,("Writeable: irp list %s\n",
203 IsListEmpty(&Connection->SendRequest) ?
204 "empty" : "nonempty"));
205
206 while (!IsListEmpty(&Connection->SendRequest)) {
207 OSK_UINT SendLen = 0, Sent = 0;
208 PVOID SendBuffer = 0;
209
210 Entry = RemoveHeadList( &Connection->SendRequest );
211
212 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
213
214 Irp = Bucket->Request.RequestContext;
215 Mdl = Irp->MdlAddress;
216
217 TI_DbgPrint(DEBUG_TCP,
218 ("Getting the user buffer from %x\n", Mdl));
219
220 NdisQueryBuffer( Mdl, &SendBuffer, &SendLen );
221
222 TI_DbgPrint(DEBUG_TCP,
223 ("Writing %d bytes to %x\n", SendLen, SendBuffer));
224
225 TI_DbgPrint(DEBUG_TCP, ("Connection: %x\n", Connection));
226
227 if (Connection->SignalState & SEL_ERROR)
228 {
229 Status = TCPTranslateError(OskitTCPGetSocketError(Connection));
230 }
231 else if (Connection->SignalState & SEL_FIN)
232 {
233 /* We got here because of a SEL_FIN event */
234 Status = STATUS_CANCELLED;
235 }
236 else
237 {
238 Status = TCPTranslateError(OskitTCPSend(Connection,
239 SendBuffer,
240 SendLen,
241 &Sent,
242 0));
243 }
244
245 TI_DbgPrint(DEBUG_TCP,("TCP Bytes: %d\n", Sent));
246
247 if( Status == STATUS_PENDING ) {
248 InsertHeadList( &Connection->SendRequest, &Bucket->Entry );
249 break;
250 } else {
251 TI_DbgPrint(DEBUG_TCP,
252 ("Completing Send request: %x %x\n",
253 Bucket->Request, Status));
254
255 Bucket->Status = Status;
256 Bucket->Information = (Bucket->Status == STATUS_SUCCESS) ? Sent : 0;
257
258 CompleteBucket(Connection, Bucket);
259 }
260 }
261 }
262 if( Connection->SignalState & (SEL_WRITE | SEL_FIN | SEL_ERROR) ) {
263 while (!IsListEmpty(&Connection->ShutdownRequest)) {
264 Entry = RemoveHeadList( &Connection->ShutdownRequest );
265
266 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
267
268 if (Connection->SignalState & SEL_ERROR)
269 {
270 Status = TCPTranslateError(OskitTCPGetSocketError(Connection));
271 }
272 else if (Connection->SignalState & SEL_FIN)
273 {
274 /* We were cancelled by a FIN */
275 Status = STATUS_CANCELLED;
276 }
277 else
278 {
279 /* See if we can satisfy this after the events */
280 if (IsListEmpty(&Connection->SendRequest))
281 {
282 /* Send queue is empty so we're good to go */
283 Status = TCPTranslateError(OskitTCPShutdown(Connection, FWRITE));
284 }
285 else
286 {
287 /* We still have to wait */
288 Status = STATUS_PENDING;
289 }
290 }
291
292 if( Status == STATUS_PENDING ) {
293 InsertHeadList( &Connection->ShutdownRequest, &Bucket->Entry );
294 break;
295 } else {
296 TI_DbgPrint(DEBUG_TCP,
297 ("Completing shutdown request: %x %x\n",
298 Bucket->Request, Status));
299
300 if (KeCancelTimer(&Connection->DisconnectTimer))
301 {
302 DereferenceObject(Connection);
303 }
304
305 Bucket->Status = Status;
306 Bucket->Information = 0;
307
308 CompleteBucket(Connection, Bucket);
309 }
310 }
311 }
312
313 if (Connection->SignalState & SEL_FIN)
314 {
315 Connection->SocketContext = NULL;
316 DereferenceObject(Connection);
317 }
318
319 if (ClientInfo.Unlocked)
320 UnlockObjectFromDpcLevel(Connection);
321 }
322
323 VOID NTAPI
324 DisconnectTimeoutDpc(PKDPC Dpc,
325 PVOID DeferredContext,
326 PVOID SystemArgument1,
327 PVOID SystemArgument2)
328 {
329 PCONNECTION_ENDPOINT Connection = DeferredContext;
330 PLIST_ENTRY Entry;
331 PTDI_BUCKET Bucket;
332
333 LockObjectAtDpcLevel(Connection);
334
335 /* We timed out waiting for pending sends so force it to shutdown */
336 OskitTCPShutdown(Connection, FWRITE);
337
338 while (!IsListEmpty(&Connection->SendRequest))
339 {
340 Entry = RemoveHeadList(&Connection->SendRequest);
341
342 Bucket = CONTAINING_RECORD(Entry, TDI_BUCKET, Entry);
343
344 Bucket->Information = 0;
345 Bucket->Status = STATUS_FILE_CLOSED;
346
347 CompleteBucket(Connection, Bucket);
348 }
349
350 while (!IsListEmpty(&Connection->ShutdownRequest)) {
351 Entry = RemoveHeadList( &Connection->ShutdownRequest );
352
353 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
354
355 Bucket->Status = STATUS_TIMEOUT;
356 Bucket->Information = 0;
357
358 CompleteBucket(Connection, Bucket);
359 }
360
361 UnlockObjectFromDpcLevel(Connection);
362
363 DereferenceObject(Connection);
364 }
365
366 VOID ConnectionFree(PVOID Object) {
367 PCONNECTION_ENDPOINT Connection = Object;
368 KIRQL OldIrql;
369
370 TI_DbgPrint(DEBUG_TCP, ("Freeing TCP Endpoint\n"));
371
372 TcpipAcquireSpinLock(&ConnectionEndpointListLock, &OldIrql);
373 RemoveEntryList(&Connection->ListEntry);
374 TcpipReleaseSpinLock(&ConnectionEndpointListLock, OldIrql);
375
376 ExFreePoolWithTag( Connection, CONN_ENDPT_TAG );
377 }
378
379 PCONNECTION_ENDPOINT TCPAllocateConnectionEndpoint( PVOID ClientContext ) {
380 PCONNECTION_ENDPOINT Connection =
381 ExAllocatePoolWithTag(NonPagedPool, sizeof(CONNECTION_ENDPOINT),
382 CONN_ENDPT_TAG);
383 if (!Connection)
384 return Connection;
385
386 TI_DbgPrint(DEBUG_CPOINT, ("Connection point file object allocated at (0x%X).\n", Connection));
387
388 RtlZeroMemory(Connection, sizeof(CONNECTION_ENDPOINT));
389
390 /* Initialize spin lock that protects the connection endpoint file object */
391 KeInitializeSpinLock(&Connection->Lock);
392 InitializeListHead(&Connection->ConnectRequest);
393 InitializeListHead(&Connection->ListenRequest);
394 InitializeListHead(&Connection->ReceiveRequest);
395 InitializeListHead(&Connection->SendRequest);
396 InitializeListHead(&Connection->ShutdownRequest);
397
398 KeInitializeTimer(&Connection->DisconnectTimer);
399 KeInitializeDpc(&Connection->DisconnectDpc, DisconnectTimeoutDpc, Connection);
400
401 /* Save client context pointer */
402 Connection->ClientContext = ClientContext;
403
404 Connection->RefCount = 2;
405 Connection->Free = ConnectionFree;
406
407 /* Add connection endpoint to global list */
408 ExInterlockedInsertTailList(&ConnectionEndpointListHead,
409 &Connection->ListEntry,
410 &ConnectionEndpointListLock);
411
412 return Connection;
413 }
414
415 NTSTATUS TCPSocket( PCONNECTION_ENDPOINT Connection,
416 UINT Family, UINT Type, UINT Proto ) {
417 NTSTATUS Status;
418 KIRQL OldIrql;
419
420 LockObject(Connection, &OldIrql);
421
422 TI_DbgPrint(DEBUG_TCP,("Called: Connection %x, Family %d, Type %d, "
423 "Proto %d\n",
424 Connection, Family, Type, Proto));
425
426 Status = TCPTranslateError( OskitTCPSocket( Connection,
427 &Connection->SocketContext,
428 Family,
429 Type,
430 Proto ) );
431
432 ASSERT_KM_POINTER(Connection->SocketContext);
433
434 UnlockObject(Connection, OldIrql);
435
436 return Status;
437 }
438
439 VOID TCPReceive(PIP_INTERFACE Interface, PIP_PACKET IPPacket)
440 /*
441 * FUNCTION: Receives and queues TCP data
442 * ARGUMENTS:
443 * IPPacket = Pointer to an IP packet that was received
444 * NOTES:
445 * This is the low level interface for receiving TCP data
446 */
447 {
448 KIRQL OldIrql;
449
450 TI_DbgPrint(DEBUG_TCP,("Sending packet %d (%d) to oskit\n",
451 IPPacket->TotalSize,
452 IPPacket->HeaderSize));
453
454 KeAcquireSpinLock(&ClientInfo.Lock, &OldIrql);
455 ClientInfo.Unlocked = TRUE;
456 ClientInfo.OldIrql = OldIrql;
457
458 OskitTCPReceiveDatagram( IPPacket->Header,
459 IPPacket->TotalSize,
460 IPPacket->HeaderSize );
461
462 ClientInfo.Unlocked = FALSE;
463 KeReleaseSpinLock(&ClientInfo.Lock, OldIrql);
464 }
465
466 /* event.c */
467 int TCPSocketState( void *ClientData,
468 void *WhichSocket,
469 void *WhichConnection,
470 OSK_UINT NewState );
471
472 int TCPPacketSend( void *ClientData,
473 OSK_PCHAR Data,
474 OSK_UINT Len );
475
476 POSK_IFADDR TCPFindInterface( void *ClientData,
477 OSK_UINT AddrType,
478 OSK_UINT FindType,
479 OSK_SOCKADDR *ReqAddr );
480
481 NTSTATUS TCPMemStartup( void );
482 void *TCPMalloc( void *ClientData,
483 OSK_UINT bytes, OSK_PCHAR file, OSK_UINT line );
484 void TCPFree( void *ClientData,
485 void *data, OSK_PCHAR file, OSK_UINT line );
486 void TCPMemShutdown( void );
487
488 OSKITTCP_EVENT_HANDLERS EventHandlers = {
489 NULL, /* Client Data */
490 TCPSocketState, /* SocketState */
491 TCPPacketSend, /* PacketSend */
492 TCPFindInterface, /* FindInterface */
493 TCPMalloc, /* Malloc */
494 TCPFree, /* Free */
495 NULL, /* Sleep */
496 NULL, /* Wakeup */
497 };
498
499 static KEVENT TimerLoopEvent;
500 static HANDLE TimerThreadHandle;
501
502 /*
503 * We are running 2 timers here, one with a 200ms interval (fast) and the other
504 * with a 500ms interval (slow). So we need to time out at 200, 400, 500, 600,
505 * 800, 1000 and process the "fast" events at 200, 400, 600, 800, 1000 and the
506 * "slow" events at 500 and 1000.
507 */
508 static VOID NTAPI
509 TimerThread(PVOID Context)
510 {
511 LARGE_INTEGER Timeout;
512 NTSTATUS Status;
513 unsigned Current, NextFast, NextSlow, Next;
514
515 Current = 0;
516 Next = 0;
517 NextFast = 0;
518 NextSlow = 0;
519 while ( 1 ) {
520 if (Next == NextFast) {
521 NextFast += 2;
522 }
523 if (Next == NextSlow) {
524 NextSlow += 5;
525 }
526 Next = min(NextFast, NextSlow);
527 Timeout.QuadPart = (LONGLONG) (Next - Current) * -1000000; /* 100 ms */
528 Status = KeWaitForSingleObject(&TimerLoopEvent, Executive, KernelMode,
529 FALSE, &Timeout);
530 if (Status != STATUS_TIMEOUT) {
531 PsTerminateSystemThread(Status);
532 }
533
534 TimerOskitTCP( Next == NextFast, Next == NextSlow );
535
536 Current = Next;
537 if (10 <= Current) {
538 Current = 0;
539 Next = 0;
540 NextFast = 0;
541 NextSlow = 0;
542 }
543 }
544 }
545
546 static VOID
547 StartTimer(VOID)
548 {
549 KeInitializeEvent(&TimerLoopEvent, NotificationEvent, FALSE);
550 PsCreateSystemThread(&TimerThreadHandle, THREAD_ALL_ACCESS, 0, 0, 0,
551 TimerThread, NULL);
552 }
553
554 NTSTATUS TCPStartup(VOID)
555 /*
556 * FUNCTION: Initializes the TCP subsystem
557 * RETURNS:
558 * Status of operation
559 */
560 {
561 NTSTATUS Status;
562
563 Status = TCPMemStartup();
564 if ( ! NT_SUCCESS(Status) ) {
565 return Status;
566 }
567
568 Status = PortsStartup( &TCPPorts, 1, 0xfffe );
569 if( !NT_SUCCESS(Status) ) {
570 TCPMemShutdown();
571 return Status;
572 }
573
574 KeInitializeSpinLock(&ClientInfo.Lock);
575 ClientInfo.Unlocked = FALSE;
576
577 RegisterOskitTCPEventHandlers( &EventHandlers );
578 InitOskitTCP();
579
580 /* Register this protocol with IP layer */
581 IPRegisterProtocol(IPPROTO_TCP, TCPReceive);
582
583 ExInitializeNPagedLookasideList(
584 &TCPSegmentList, /* Lookaside list */
585 NULL, /* Allocate routine */
586 NULL, /* Free routine */
587 0, /* Flags */
588 sizeof(TCP_SEGMENT), /* Size of each entry */
589 'SPCT', /* Tag */
590 0); /* Depth */
591
592 StartTimer();
593
594 TCPInitialized = TRUE;
595
596 return STATUS_SUCCESS;
597 }
598
599
600 NTSTATUS TCPShutdown(VOID)
601 /*
602 * FUNCTION: Shuts down the TCP subsystem
603 * RETURNS:
604 * Status of operation
605 */
606 {
607 LARGE_INTEGER WaitForThread;
608
609 if (!TCPInitialized)
610 return STATUS_SUCCESS;
611
612 WaitForThread.QuadPart = -2500000; /* 250 ms */
613 KeSetEvent(&TimerLoopEvent, IO_NO_INCREMENT, FALSE);
614 ZwWaitForSingleObject(TimerThreadHandle, FALSE, &WaitForThread);
615
616 /* Deregister this protocol with IP layer */
617 IPRegisterProtocol(IPPROTO_TCP, NULL);
618
619 ExDeleteNPagedLookasideList(&TCPSegmentList);
620
621 TCPInitialized = FALSE;
622
623 DeinitOskitTCP();
624
625 PortsShutdown( &TCPPorts );
626
627 TCPMemShutdown();
628
629 return STATUS_SUCCESS;
630 }
631
632 NTSTATUS TCPTranslateError( int OskitError ) {
633 NTSTATUS Status;
634
635 switch( OskitError ) {
636 case 0: Status = STATUS_SUCCESS; break;
637 case OSK_EADDRNOTAVAIL:
638 Status = STATUS_INVALID_ADDRESS;
639 DbgPrint("OskitTCP: EADDRNOTAVAIL\n");
640 break;
641 case OSK_EADDRINUSE:
642 Status = STATUS_ADDRESS_ALREADY_EXISTS;
643 DbgPrint("OskitTCP: EADDRINUSE\n");
644 break;
645 case OSK_EAFNOSUPPORT:
646 Status = STATUS_INVALID_CONNECTION;
647 DbgPrint("OskitTCP: EAFNOSUPPORT\n");
648 break;
649 case OSK_ECONNREFUSED:
650 Status = STATUS_REMOTE_NOT_LISTENING;
651 DbgPrint("OskitTCP: ECONNREFUSED\n");
652 break;
653 case OSK_ECONNRESET:
654 Status = STATUS_REMOTE_DISCONNECT;
655 DbgPrint("OskitTCP: ECONNRESET\n");
656 break;
657 case OSK_ECONNABORTED:
658 Status = STATUS_LOCAL_DISCONNECT;
659 DbgPrint("OskitTCP: ECONNABORTED\n");
660 break;
661 case OSK_EWOULDBLOCK:
662 case OSK_EINPROGRESS: Status = STATUS_PENDING; break;
663 case OSK_EINVAL:
664 Status = STATUS_INVALID_PARAMETER;
665 DbgPrint("OskitTCP: EINVAL\n");
666 break;
667 case OSK_ENOMEM:
668 case OSK_ENOBUFS:
669 Status = STATUS_INSUFFICIENT_RESOURCES;
670 DbgPrint("OskitTCP: ENOMEM/ENOBUFS\n");
671 break;
672 case OSK_EPIPE:
673 case OSK_ESHUTDOWN:
674 Status = STATUS_FILE_CLOSED;
675 DbgPrint("OskitTCP: ESHUTDOWN/EPIPE\n");
676 break;
677 case OSK_EMSGSIZE:
678 Status = STATUS_BUFFER_TOO_SMALL;
679 DbgPrint("OskitTCP: EMSGSIZE\n");
680 break;
681 case OSK_ETIMEDOUT:
682 Status = STATUS_TIMEOUT;
683 DbgPrint("OskitTCP: ETIMEDOUT\n");
684 break;
685 case OSK_ENETUNREACH:
686 Status = STATUS_NETWORK_UNREACHABLE;
687 DbgPrint("OskitTCP: ENETUNREACH\n");
688 break;
689 case OSK_EFAULT:
690 Status = STATUS_ACCESS_VIOLATION;
691 DbgPrint("OskitTCP: EFAULT\n");
692 break;
693 default:
694 DbgPrint("OskitTCP returned unhandled error code: %d\n", OskitError);
695 Status = STATUS_INVALID_CONNECTION;
696 break;
697 }
698
699 TI_DbgPrint(DEBUG_TCP,("Error %d -> %x\n", OskitError, Status));
700 return Status;
701 }
702
703 NTSTATUS TCPConnect
704 ( PCONNECTION_ENDPOINT Connection,
705 PTDI_CONNECTION_INFORMATION ConnInfo,
706 PTDI_CONNECTION_INFORMATION ReturnInfo,
707 PTCP_COMPLETION_ROUTINE Complete,
708 PVOID Context ) {
709 NTSTATUS Status;
710 SOCKADDR_IN AddressToConnect = { 0 }, AddressToBind = { 0 };
711 IP_ADDRESS RemoteAddress;
712 USHORT RemotePort;
713 TA_IP_ADDRESS LocalAddress;
714 PTDI_BUCKET Bucket;
715 PNEIGHBOR_CACHE_ENTRY NCE = NULL;
716 KIRQL OldIrql;
717
718 TI_DbgPrint(DEBUG_TCP,("TCPConnect: Called\n"));
719
720 Status = AddrBuildAddress
721 ((PTRANSPORT_ADDRESS)ConnInfo->RemoteAddress,
722 &RemoteAddress,
723 &RemotePort);
724
725 if (!NT_SUCCESS(Status)) {
726 TI_DbgPrint(DEBUG_TCP, ("Could not AddrBuildAddress in TCPConnect\n"));
727 return Status;
728 }
729
730 /* Freed in TCPSocketState */
731 TI_DbgPrint(DEBUG_TCP,
732 ("Connecting to address %x:%x\n",
733 RemoteAddress.Address.IPv4Address,
734 RemotePort));
735
736 AddressToConnect.sin_family = AF_INET;
737 AddressToBind = AddressToConnect;
738
739 LockObject(Connection, &OldIrql);
740
741 if (!Connection->AddressFile)
742 {
743 UnlockObject(Connection, OldIrql);
744 return STATUS_INVALID_PARAMETER;
745 }
746
747 if (AddrIsUnspecified(&Connection->AddressFile->Address))
748 {
749 if (!(NCE = RouteGetRouteToDestination(&RemoteAddress)))
750 {
751 UnlockObject(Connection, OldIrql);
752 return STATUS_NETWORK_UNREACHABLE;
753 }
754 }
755
756 if (Connection->AddressFile->Port)
757 {
758 /* See if we had an unspecified bind address */
759 if (NCE)
760 {
761 /* We did, so use the interface unicast address associated with the route */
762 AddressToBind.sin_addr.s_addr = NCE->Interface->Unicast.Address.IPv4Address;
763 }
764 else
765 {
766 /* Bind address was explicit so use it */
767 AddressToBind.sin_addr.s_addr = Connection->AddressFile->Address.Address.IPv4Address;
768 }
769
770 AddressToBind.sin_port = Connection->AddressFile->Port;
771
772 /* Perform an explicit bind */
773 Status = TCPTranslateError(OskitTCPBind(Connection,
774 &AddressToBind,
775 sizeof(AddressToBind)));
776 }
777 else
778 {
779 /* An implicit bind will be performed */
780 Status = STATUS_SUCCESS;
781 }
782
783 if (NT_SUCCESS(Status)) {
784 if (NT_SUCCESS(Status))
785 {
786 memcpy( &AddressToConnect.sin_addr,
787 &RemoteAddress.Address.IPv4Address,
788 sizeof(AddressToConnect.sin_addr) );
789 AddressToConnect.sin_port = RemotePort;
790
791 Status = TCPTranslateError
792 ( OskitTCPConnect( Connection,
793 &AddressToConnect,
794 sizeof(AddressToConnect) ) );
795
796 if (NT_SUCCESS(Status))
797 {
798 /* Check if we had an unspecified port */
799 if (!Connection->AddressFile->Port)
800 {
801 /* We did, so we need to copy back the port */
802 if (NT_SUCCESS(TCPGetSockAddress(Connection, (PTRANSPORT_ADDRESS)&LocalAddress, FALSE)))
803 {
804 /* Allocate the port in the port bitmap */
805 Connection->AddressFile->Port = TCPAllocatePort(LocalAddress.Address[0].Address[0].sin_port);
806
807 /* This should never fail */
808 ASSERT(Connection->AddressFile->Port != 0xFFFF);
809 }
810 }
811
812 /* Check if the address was unspecified */
813 if (AddrIsUnspecified(&Connection->AddressFile->Address))
814 {
815 /* It is, so store the address of the outgoing NIC */
816 Connection->AddressFile->Address = NCE->Interface->Unicast;
817 }
818 }
819
820 if (Status == STATUS_PENDING)
821 {
822 Bucket = ExAllocatePoolWithTag( NonPagedPool, sizeof(*Bucket), TDI_BUCKET_TAG );
823 if( !Bucket )
824 {
825 UnlockObject(Connection, OldIrql);
826 return STATUS_NO_MEMORY;
827 }
828
829 Bucket->Request.RequestNotifyObject = (PVOID)Complete;
830 Bucket->Request.RequestContext = Context;
831
832 InsertTailList( &Connection->ConnectRequest, &Bucket->Entry );
833 }
834 }
835 }
836
837 UnlockObject(Connection, OldIrql);
838
839 return Status;
840 }
841
842 NTSTATUS TCPDisconnect
843 ( PCONNECTION_ENDPOINT Connection,
844 UINT Flags,
845 PLARGE_INTEGER Timeout,
846 PTDI_CONNECTION_INFORMATION ConnInfo,
847 PTDI_CONNECTION_INFORMATION ReturnInfo,
848 PTCP_COMPLETION_ROUTINE Complete,
849 PVOID Context ) {
850 NTSTATUS Status = STATUS_INVALID_PARAMETER;
851 PTDI_BUCKET Bucket;
852 KIRQL OldIrql;
853 PLIST_ENTRY Entry;
854 LARGE_INTEGER ActualTimeout;
855
856 TI_DbgPrint(DEBUG_TCP,("started\n"));
857
858 LockObject(Connection, &OldIrql);
859
860 if (Flags & TDI_DISCONNECT_RELEASE)
861 {
862 /* See if we can satisfy this right now */
863 if (IsListEmpty(&Connection->SendRequest))
864 {
865 /* Send queue is empty so we're good to go */
866 Status = TCPTranslateError(OskitTCPShutdown(Connection, FWRITE));
867
868 UnlockObject(Connection, OldIrql);
869
870 return Status;
871 }
872
873 /* Check if the timeout was 0 */
874 if (Timeout && Timeout->QuadPart == 0)
875 {
876 OskitTCPShutdown(Connection, FWRITE);
877
878 while (!IsListEmpty(&Connection->SendRequest))
879 {
880 Entry = RemoveHeadList(&Connection->SendRequest);
881
882 Bucket = CONTAINING_RECORD(Entry, TDI_BUCKET, Entry);
883
884 Bucket->Information = 0;
885 Bucket->Status = STATUS_FILE_CLOSED;
886
887 CompleteBucket(Connection, Bucket);
888 }
889
890 UnlockObject(Connection, OldIrql);
891
892 return STATUS_TIMEOUT;
893 }
894
895 /* Otherwise we wait for the send queue to be empty */
896 }
897
898 if ((Flags & TDI_DISCONNECT_ABORT) || !Flags)
899 {
900 /* This request overrides any pending graceful disconnects */
901 while (!IsListEmpty(&Connection->ShutdownRequest))
902 {
903 Entry = RemoveHeadList(&Connection->ShutdownRequest);
904
905 Bucket = CONTAINING_RECORD(Entry, TDI_BUCKET, Entry);
906
907 Bucket->Information = 0;
908 Bucket->Status = STATUS_FILE_CLOSED;
909
910 CompleteBucket(Connection, Bucket);
911 }
912
913 /* Also kill any pending reads and writes */
914 while (!IsListEmpty(&Connection->SendRequest))
915 {
916 Entry = RemoveHeadList(&Connection->SendRequest);
917
918 Bucket = CONTAINING_RECORD(Entry, TDI_BUCKET, Entry);
919
920 Bucket->Information = 0;
921 Bucket->Status = STATUS_FILE_CLOSED;
922
923 CompleteBucket(Connection, Bucket);
924 }
925
926 while (!IsListEmpty(&Connection->ReceiveRequest))
927 {
928 Entry = RemoveHeadList(&Connection->ReceiveRequest);
929
930 Bucket = CONTAINING_RECORD(Entry, TDI_BUCKET, Entry);
931
932 Bucket->Information = 0;
933 Bucket->Status = STATUS_FILE_CLOSED;
934
935 CompleteBucket(Connection, Bucket);
936 }
937
938 /* An abort never pends; we just drop everything and complete */
939 Status = TCPTranslateError(OskitTCPShutdown(Connection, FWRITE | FREAD));
940
941 UnlockObject(Connection, OldIrql);
942
943 return Status;
944 }
945
946 /* We couldn't complete the request now because we need to wait for outstanding I/O */
947 Bucket = ExAllocatePoolWithTag(NonPagedPool, sizeof(*Bucket), TDI_BUCKET_TAG);
948 if (!Bucket)
949 {
950 UnlockObject(Connection, OldIrql);
951 return STATUS_NO_MEMORY;
952 }
953
954 Bucket->Request.RequestNotifyObject = (PVOID)Complete;
955 Bucket->Request.RequestContext = Context;
956
957 InsertTailList(&Connection->ShutdownRequest, &Bucket->Entry);
958
959 /* Use the timeout specified or 1 second if none was specified */
960 if (Timeout)
961 {
962 ActualTimeout = *Timeout;
963 }
964 else
965 {
966 ActualTimeout.QuadPart = -1000000;
967 }
968
969 ReferenceObject(Connection);
970 KeSetTimer(&Connection->DisconnectTimer, ActualTimeout, &Connection->DisconnectDpc);
971
972 UnlockObject(Connection, OldIrql);
973
974 TI_DbgPrint(DEBUG_TCP,("finished %x\n", Status));
975
976 return STATUS_PENDING;
977 }
978
979 NTSTATUS TCPClose
980 ( PCONNECTION_ENDPOINT Connection )
981 {
982 KIRQL OldIrql;
983
984 LockObject(Connection, &OldIrql);
985
986 /* We should not be associated to an address file at this point */
987 ASSERT(!Connection->AddressFile);
988
989 OskitTCPClose(Connection);
990
991 Connection->SocketContext = NULL;
992
993 UnlockObject(Connection, OldIrql);
994
995 DereferenceObject(Connection);
996
997 return STATUS_SUCCESS;
998 }
999
1000 NTSTATUS TCPReceiveData
1001 ( PCONNECTION_ENDPOINT Connection,
1002 PNDIS_BUFFER Buffer,
1003 ULONG ReceiveLength,
1004 PULONG BytesReceived,
1005 ULONG ReceiveFlags,
1006 PTCP_COMPLETION_ROUTINE Complete,
1007 PVOID Context ) {
1008 PVOID DataBuffer;
1009 UINT DataLen, Received = 0;
1010 NTSTATUS Status;
1011 PTDI_BUCKET Bucket;
1012 KIRQL OldIrql;
1013
1014 NdisQueryBuffer( Buffer, &DataBuffer, &DataLen );
1015
1016 TI_DbgPrint(DEBUG_TCP,("TCP>|< Got an MDL %x (%x:%d)\n", Buffer, DataBuffer, DataLen));
1017
1018 LockObject(Connection, &OldIrql);
1019
1020 Status = TCPTranslateError
1021 ( OskitTCPRecv
1022 ( Connection,
1023 DataBuffer,
1024 DataLen,
1025 &Received,
1026 ReceiveFlags ) );
1027
1028 TI_DbgPrint(DEBUG_TCP,("OskitTCPReceive: %x, %d\n", Status, Received));
1029
1030 /* Keep this request around ... there was no data yet */
1031 if( Status == STATUS_PENDING ) {
1032 /* Freed in TCPSocketState */
1033 Bucket = ExAllocatePoolWithTag( NonPagedPool, sizeof(*Bucket), TDI_BUCKET_TAG );
1034 if( !Bucket ) {
1035 TI_DbgPrint(DEBUG_TCP,("Failed to allocate bucket\n"));
1036 UnlockObject(Connection, OldIrql);
1037 return STATUS_NO_MEMORY;
1038 }
1039
1040 Bucket->Request.RequestNotifyObject = Complete;
1041 Bucket->Request.RequestContext = Context;
1042 *BytesReceived = 0;
1043
1044 InsertTailList( &Connection->ReceiveRequest, &Bucket->Entry );
1045 TI_DbgPrint(DEBUG_TCP,("Queued read irp\n"));
1046 } else {
1047 TI_DbgPrint(DEBUG_TCP,("Got status %x, bytes %d\n", Status, Received));
1048 *BytesReceived = Received;
1049 }
1050
1051 UnlockObject(Connection, OldIrql);
1052
1053 TI_DbgPrint(DEBUG_TCP,("Status %x\n", Status));
1054
1055 return Status;
1056 }
1057
1058 NTSTATUS TCPSendData
1059 ( PCONNECTION_ENDPOINT Connection,
1060 PCHAR BufferData,
1061 ULONG SendLength,
1062 PULONG BytesSent,
1063 ULONG Flags,
1064 PTCP_COMPLETION_ROUTINE Complete,
1065 PVOID Context ) {
1066 UINT Sent = 0;
1067 NTSTATUS Status;
1068 PTDI_BUCKET Bucket;
1069 KIRQL OldIrql;
1070
1071 LockObject(Connection, &OldIrql);
1072
1073 TI_DbgPrint(DEBUG_TCP,("Connection = %x\n", Connection));
1074
1075 Status = TCPTranslateError
1076 ( OskitTCPSend( Connection,
1077 (OSK_PCHAR)BufferData, SendLength,
1078 &Sent, 0 ) );
1079
1080 TI_DbgPrint(DEBUG_TCP,("OskitTCPSend: %x, %d\n", Status, Sent));
1081
1082 /* Keep this request around ... there was no data yet */
1083 if( Status == STATUS_PENDING ) {
1084 /* Freed in TCPSocketState */
1085 Bucket = ExAllocatePoolWithTag( NonPagedPool, sizeof(*Bucket), TDI_BUCKET_TAG );
1086 if( !Bucket ) {
1087 UnlockObject(Connection, OldIrql);
1088 TI_DbgPrint(DEBUG_TCP,("Failed to allocate bucket\n"));
1089 return STATUS_NO_MEMORY;
1090 }
1091
1092 Bucket->Request.RequestNotifyObject = Complete;
1093 Bucket->Request.RequestContext = Context;
1094 *BytesSent = 0;
1095
1096 InsertTailList( &Connection->SendRequest, &Bucket->Entry );
1097 TI_DbgPrint(DEBUG_TCP,("Queued write irp\n"));
1098 } else {
1099 TI_DbgPrint(DEBUG_TCP,("Got status %x, bytes %d\n", Status, Sent));
1100 *BytesSent = Sent;
1101 }
1102
1103 UnlockObject(Connection, OldIrql);
1104
1105 TI_DbgPrint(DEBUG_TCP,("Status %x\n", Status));
1106
1107 return Status;
1108 }
1109
1110 UINT TCPAllocatePort( UINT HintPort ) {
1111 if( HintPort ) {
1112 if( AllocatePort( &TCPPorts, HintPort ) ) return HintPort;
1113 else {
1114 TI_DbgPrint
1115 (MID_TRACE,("We got a hint port but couldn't allocate it\n"));
1116 return (UINT)-1;
1117 }
1118 } else return AllocatePortFromRange( &TCPPorts, 1024, 5000 );
1119 }
1120
1121 VOID TCPFreePort( UINT Port ) {
1122 DeallocatePort( &TCPPorts, Port );
1123 }
1124
1125 NTSTATUS TCPGetSockAddress
1126 ( PCONNECTION_ENDPOINT Connection,
1127 PTRANSPORT_ADDRESS Address,
1128 BOOLEAN GetRemote ) {
1129 OSK_UINT LocalAddress, RemoteAddress;
1130 OSK_UI16 LocalPort, RemotePort;
1131 PTA_IP_ADDRESS AddressIP = (PTA_IP_ADDRESS)Address;
1132 NTSTATUS Status;
1133 KIRQL OldIrql;
1134
1135 LockObject(Connection, &OldIrql);
1136
1137 Status = TCPTranslateError(OskitTCPGetAddress(Connection,
1138 &LocalAddress, &LocalPort,
1139 &RemoteAddress, &RemotePort));
1140
1141 UnlockObject(Connection, OldIrql);
1142
1143 if (!NT_SUCCESS(Status))
1144 return Status;
1145
1146 AddressIP->TAAddressCount = 1;
1147 AddressIP->Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP;
1148 AddressIP->Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
1149 AddressIP->Address[0].Address[0].sin_port = GetRemote ? RemotePort : LocalPort;
1150 AddressIP->Address[0].Address[0].in_addr = GetRemote ? RemoteAddress : LocalAddress;
1151
1152 return Status;
1153 }
1154
1155 BOOLEAN TCPRemoveIRP( PCONNECTION_ENDPOINT Endpoint, PIRP Irp ) {
1156 PLIST_ENTRY Entry;
1157 PLIST_ENTRY ListHead[5];
1158 KIRQL OldIrql;
1159 PTDI_BUCKET Bucket;
1160 UINT i = 0;
1161 BOOLEAN Found = FALSE;
1162
1163 ListHead[0] = &Endpoint->SendRequest;
1164 ListHead[1] = &Endpoint->ReceiveRequest;
1165 ListHead[2] = &Endpoint->ConnectRequest;
1166 ListHead[3] = &Endpoint->ListenRequest;
1167 ListHead[4] = &Endpoint->ShutdownRequest;
1168
1169 LockObject(Endpoint, &OldIrql);
1170
1171 for( i = 0; i < 5; i++ )
1172 {
1173 for( Entry = ListHead[i]->Flink;
1174 Entry != ListHead[i];
1175 Entry = Entry->Flink )
1176 {
1177 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
1178 if( Bucket->Request.RequestContext == Irp )
1179 {
1180 RemoveEntryList( &Bucket->Entry );
1181 ExFreePoolWithTag( Bucket, TDI_BUCKET_TAG );
1182 Found = TRUE;
1183 break;
1184 }
1185 }
1186 }
1187
1188 UnlockObject(Endpoint, OldIrql);
1189
1190 return Found;
1191 }
1192
1193 /* EOF */