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