* Sync with recent trunk (r52669).
[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 = NULL;
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
767 if (Connection->AddressFile->Port)
768 {
769 /* See if we had an unspecified bind address */
770 if (NCE)
771 {
772 /* We did, so use the interface unicast address associated with the route */
773 AddressToBind.sin_addr.s_addr = NCE->Interface->Unicast.Address.IPv4Address;
774 }
775 else
776 {
777 /* Bind address was explicit so use it */
778 AddressToBind.sin_addr.s_addr = Connection->AddressFile->Address.Address.IPv4Address;
779 }
780
781 AddressToBind.sin_port = Connection->AddressFile->Port;
782
783 /* Perform an explicit bind */
784 Status = TCPTranslateError(OskitTCPBind(Connection->SocketContext,
785 &AddressToBind,
786 sizeof(AddressToBind)));
787 }
788 else
789 {
790 /* An implicit bind will be performed */
791 Status = STATUS_SUCCESS;
792 }
793
794 if (NT_SUCCESS(Status)) {
795 if (NT_SUCCESS(Status))
796 {
797 memcpy( &AddressToConnect.sin_addr,
798 &RemoteAddress.Address.IPv4Address,
799 sizeof(AddressToConnect.sin_addr) );
800 AddressToConnect.sin_port = RemotePort;
801
802 Status = TCPTranslateError
803 ( OskitTCPConnect( Connection->SocketContext,
804 &AddressToConnect,
805 sizeof(AddressToConnect) ) );
806
807 if (NT_SUCCESS(Status))
808 {
809 /* Check if we had an unspecified port */
810 if (!Connection->AddressFile->Port)
811 {
812 /* We did, so we need to copy back the port */
813 if (NT_SUCCESS(TCPGetSockAddress(Connection, (PTRANSPORT_ADDRESS)&LocalAddress, FALSE)))
814 {
815 /* Allocate the port in the port bitmap */
816 Connection->AddressFile->Port = TCPAllocatePort(LocalAddress.Address[0].Address[0].sin_port);
817
818 /* This should never fail */
819 ASSERT(Connection->AddressFile->Port != 0xFFFF);
820 }
821 }
822
823 /* Check if the address was unspecified */
824 if (AddrIsUnspecified(&Connection->AddressFile->Address))
825 {
826 /* It is, so store the address of the outgoing NIC */
827 Connection->AddressFile->Address = NCE->Interface->Unicast;
828 }
829 }
830
831 if (Status == STATUS_PENDING)
832 {
833 Bucket = ExAllocatePoolWithTag( NonPagedPool, sizeof(*Bucket), TDI_BUCKET_TAG );
834 if( !Bucket )
835 {
836 UnlockObject(Connection, OldIrql);
837 return STATUS_NO_MEMORY;
838 }
839
840 Bucket->Request.RequestNotifyObject = (PVOID)Complete;
841 Bucket->Request.RequestContext = Context;
842
843 InsertTailList( &Connection->ConnectRequest, &Bucket->Entry );
844 }
845 }
846 }
847
848 UnlockObject(Connection, OldIrql);
849
850 return Status;
851 }
852
853 NTSTATUS TCPDisconnect
854 ( PCONNECTION_ENDPOINT Connection,
855 UINT Flags,
856 PLARGE_INTEGER Timeout,
857 PTDI_CONNECTION_INFORMATION ConnInfo,
858 PTDI_CONNECTION_INFORMATION ReturnInfo,
859 PTCP_COMPLETION_ROUTINE Complete,
860 PVOID Context ) {
861 NTSTATUS Status = STATUS_INVALID_PARAMETER;
862 PTDI_BUCKET Bucket;
863 KIRQL OldIrql;
864 PLIST_ENTRY Entry;
865 LARGE_INTEGER ActualTimeout;
866
867 TI_DbgPrint(DEBUG_TCP,("started\n"));
868
869 LockObject(Connection, &OldIrql);
870
871 if (Flags & TDI_DISCONNECT_RELEASE)
872 {
873 /* See if we can satisfy this right now */
874 if (IsListEmpty(&Connection->SendRequest))
875 {
876 /* Send queue is empty so we're good to go */
877 Status = TCPTranslateError(OskitTCPShutdown(Connection->SocketContext, FWRITE));
878
879 UnlockObject(Connection, OldIrql);
880
881 return Status;
882 }
883
884 /* Check if the timeout was 0 */
885 if (Timeout && Timeout->QuadPart == 0)
886 {
887 OskitTCPShutdown(Connection->SocketContext, FWRITE);
888
889 while (!IsListEmpty(&Connection->SendRequest))
890 {
891 Entry = RemoveHeadList(&Connection->SendRequest);
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 UnlockObject(Connection, OldIrql);
902
903 return STATUS_TIMEOUT;
904 }
905
906 /* Otherwise we wait for the send queue to be empty */
907 }
908
909 if ((Flags & TDI_DISCONNECT_ABORT) || !Flags)
910 {
911 /* This request overrides any pending graceful disconnects */
912 while (!IsListEmpty(&Connection->ShutdownRequest))
913 {
914 Entry = RemoveHeadList(&Connection->ShutdownRequest);
915
916 Bucket = CONTAINING_RECORD(Entry, TDI_BUCKET, Entry);
917
918 Bucket->Information = 0;
919 Bucket->Status = STATUS_FILE_CLOSED;
920
921 CompleteBucket(Connection, Bucket);
922 }
923
924 /* Also kill any pending reads and writes */
925 while (!IsListEmpty(&Connection->SendRequest))
926 {
927 Entry = RemoveHeadList(&Connection->SendRequest);
928
929 Bucket = CONTAINING_RECORD(Entry, TDI_BUCKET, Entry);
930
931 Bucket->Information = 0;
932 Bucket->Status = STATUS_FILE_CLOSED;
933
934 CompleteBucket(Connection, Bucket);
935 }
936
937 while (!IsListEmpty(&Connection->ReceiveRequest))
938 {
939 Entry = RemoveHeadList(&Connection->ReceiveRequest);
940
941 Bucket = CONTAINING_RECORD(Entry, TDI_BUCKET, Entry);
942
943 Bucket->Information = 0;
944 Bucket->Status = STATUS_FILE_CLOSED;
945
946 CompleteBucket(Connection, Bucket);
947 }
948
949 /* An abort never pends; we just drop everything and complete */
950 Status = TCPTranslateError(OskitTCPShutdown(Connection->SocketContext, FWRITE | FREAD));
951
952 UnlockObject(Connection, OldIrql);
953
954 return Status;
955 }
956
957 /* We couldn't complete the request now because we need to wait for outstanding I/O */
958 Bucket = ExAllocatePoolWithTag(NonPagedPool, sizeof(*Bucket), TDI_BUCKET_TAG);
959 if (!Bucket)
960 {
961 UnlockObject(Connection, OldIrql);
962 return STATUS_NO_MEMORY;
963 }
964
965 Bucket->Request.RequestNotifyObject = (PVOID)Complete;
966 Bucket->Request.RequestContext = Context;
967
968 InsertTailList(&Connection->ShutdownRequest, &Bucket->Entry);
969
970 /* Use the timeout specified or 1 second if none was specified */
971 if (Timeout)
972 {
973 ActualTimeout = *Timeout;
974 }
975 else
976 {
977 ActualTimeout.QuadPart = -1000000;
978 }
979
980 ReferenceObject(Connection);
981 KeSetTimer(&Connection->DisconnectTimer, ActualTimeout, &Connection->DisconnectDpc);
982
983 UnlockObject(Connection, OldIrql);
984
985 TI_DbgPrint(DEBUG_TCP,("finished %x\n", Status));
986
987 return STATUS_PENDING;
988 }
989
990 NTSTATUS TCPClose
991 ( PCONNECTION_ENDPOINT Connection )
992 {
993 KIRQL OldIrql;
994 NTSTATUS Status;
995 PVOID Socket;
996
997 LockObject(Connection, &OldIrql);
998 Socket = Connection->SocketContext;
999 Connection->SocketContext = NULL;
1000
1001 /* We should not be associated to an address file at this point */
1002 ASSERT(!Connection->AddressFile);
1003
1004 /* Don't try to close again if the other side closed us already */
1005 if (Socket)
1006 {
1007 /* We need to close here otherwise oskit will never indicate
1008 * SEL_FIN and we will never fully close the connection */
1009 Status = TCPTranslateError( OskitTCPClose( Socket ) );
1010
1011 if (!NT_SUCCESS(Status))
1012 {
1013 Connection->SocketContext = Socket;
1014 UnlockObject(Connection, OldIrql);
1015 return Status;
1016 }
1017 }
1018 else
1019 {
1020 /* We are already closed by the other end so return success */
1021 Status = STATUS_SUCCESS;
1022 }
1023
1024 UnlockObject(Connection, OldIrql);
1025
1026 DereferenceObject(Connection);
1027
1028 return Status;
1029 }
1030
1031 NTSTATUS TCPReceiveData
1032 ( PCONNECTION_ENDPOINT Connection,
1033 PNDIS_BUFFER Buffer,
1034 ULONG ReceiveLength,
1035 PULONG BytesReceived,
1036 ULONG ReceiveFlags,
1037 PTCP_COMPLETION_ROUTINE Complete,
1038 PVOID Context ) {
1039 PVOID DataBuffer;
1040 UINT DataLen, Received = 0;
1041 NTSTATUS Status;
1042 PTDI_BUCKET Bucket;
1043 KIRQL OldIrql;
1044
1045 TI_DbgPrint(DEBUG_TCP,("Called for %d bytes (on socket %x)\n",
1046 ReceiveLength, Connection->SocketContext));
1047
1048 NdisQueryBuffer( Buffer, &DataBuffer, &DataLen );
1049
1050 TI_DbgPrint(DEBUG_TCP,("TCP>|< Got an MDL %x (%x:%d)\n", Buffer, DataBuffer, DataLen));
1051
1052 LockObject(Connection, &OldIrql);
1053
1054 Status = TCPTranslateError
1055 ( OskitTCPRecv
1056 ( Connection->SocketContext,
1057 DataBuffer,
1058 DataLen,
1059 &Received,
1060 ReceiveFlags ) );
1061
1062 TI_DbgPrint(DEBUG_TCP,("OskitTCPReceive: %x, %d\n", Status, Received));
1063
1064 /* Keep this request around ... there was no data yet */
1065 if( Status == STATUS_PENDING ) {
1066 /* Freed in TCPSocketState */
1067 Bucket = ExAllocatePoolWithTag( NonPagedPool, sizeof(*Bucket), TDI_BUCKET_TAG );
1068 if( !Bucket ) {
1069 TI_DbgPrint(DEBUG_TCP,("Failed to allocate bucket\n"));
1070 UnlockObject(Connection, OldIrql);
1071 return STATUS_NO_MEMORY;
1072 }
1073
1074 Bucket->Request.RequestNotifyObject = Complete;
1075 Bucket->Request.RequestContext = Context;
1076 *BytesReceived = 0;
1077
1078 InsertTailList( &Connection->ReceiveRequest, &Bucket->Entry );
1079 TI_DbgPrint(DEBUG_TCP,("Queued read irp\n"));
1080 } else {
1081 TI_DbgPrint(DEBUG_TCP,("Got status %x, bytes %d\n", Status, Received));
1082 *BytesReceived = Received;
1083 }
1084
1085 UnlockObject(Connection, OldIrql);
1086
1087 TI_DbgPrint(DEBUG_TCP,("Status %x\n", Status));
1088
1089 return Status;
1090 }
1091
1092 NTSTATUS TCPSendData
1093 ( PCONNECTION_ENDPOINT Connection,
1094 PCHAR BufferData,
1095 ULONG SendLength,
1096 PULONG BytesSent,
1097 ULONG Flags,
1098 PTCP_COMPLETION_ROUTINE Complete,
1099 PVOID Context ) {
1100 UINT Sent = 0;
1101 NTSTATUS Status;
1102 PTDI_BUCKET Bucket;
1103 KIRQL OldIrql;
1104
1105 LockObject(Connection, &OldIrql);
1106
1107 TI_DbgPrint(DEBUG_TCP,("Called for %d bytes (on socket %x)\n",
1108 SendLength, Connection->SocketContext));
1109
1110 TI_DbgPrint(DEBUG_TCP,("Connection = %x\n", Connection));
1111 TI_DbgPrint(DEBUG_TCP,("Connection->SocketContext = %x\n",
1112 Connection->SocketContext));
1113
1114 Status = TCPTranslateError
1115 ( OskitTCPSend( Connection->SocketContext,
1116 (OSK_PCHAR)BufferData, SendLength,
1117 &Sent, 0 ) );
1118
1119 TI_DbgPrint(DEBUG_TCP,("OskitTCPSend: %x, %d\n", Status, Sent));
1120
1121 /* Keep this request around ... there was no data yet */
1122 if( Status == STATUS_PENDING ) {
1123 /* Freed in TCPSocketState */
1124 Bucket = ExAllocatePoolWithTag( NonPagedPool, sizeof(*Bucket), TDI_BUCKET_TAG );
1125 if( !Bucket ) {
1126 UnlockObject(Connection, OldIrql);
1127 TI_DbgPrint(DEBUG_TCP,("Failed to allocate bucket\n"));
1128 return STATUS_NO_MEMORY;
1129 }
1130
1131 Bucket->Request.RequestNotifyObject = Complete;
1132 Bucket->Request.RequestContext = Context;
1133 *BytesSent = 0;
1134
1135 InsertTailList( &Connection->SendRequest, &Bucket->Entry );
1136 TI_DbgPrint(DEBUG_TCP,("Queued write irp\n"));
1137 } else {
1138 TI_DbgPrint(DEBUG_TCP,("Got status %x, bytes %d\n", Status, Sent));
1139 *BytesSent = Sent;
1140 }
1141
1142 UnlockObject(Connection, OldIrql);
1143
1144 TI_DbgPrint(DEBUG_TCP,("Status %x\n", Status));
1145
1146 return Status;
1147 }
1148
1149 UINT TCPAllocatePort( UINT HintPort ) {
1150 if( HintPort ) {
1151 if( AllocatePort( &TCPPorts, HintPort ) ) return HintPort;
1152 else {
1153 TI_DbgPrint
1154 (MID_TRACE,("We got a hint port but couldn't allocate it\n"));
1155 return (UINT)-1;
1156 }
1157 } else return AllocatePortFromRange( &TCPPorts, 1024, 5000 );
1158 }
1159
1160 VOID TCPFreePort( UINT Port ) {
1161 DeallocatePort( &TCPPorts, Port );
1162 }
1163
1164 NTSTATUS TCPGetSockAddress
1165 ( PCONNECTION_ENDPOINT Connection,
1166 PTRANSPORT_ADDRESS Address,
1167 BOOLEAN GetRemote ) {
1168 OSK_UINT LocalAddress, RemoteAddress;
1169 OSK_UI16 LocalPort, RemotePort;
1170 PTA_IP_ADDRESS AddressIP = (PTA_IP_ADDRESS)Address;
1171 NTSTATUS Status;
1172 KIRQL OldIrql;
1173
1174 LockObject(Connection, &OldIrql);
1175
1176 Status = TCPTranslateError(OskitTCPGetAddress(Connection->SocketContext,
1177 &LocalAddress, &LocalPort,
1178 &RemoteAddress, &RemotePort));
1179
1180 UnlockObject(Connection, OldIrql);
1181
1182 if (!NT_SUCCESS(Status))
1183 return Status;
1184
1185 AddressIP->TAAddressCount = 1;
1186 AddressIP->Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP;
1187 AddressIP->Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
1188 AddressIP->Address[0].Address[0].sin_port = GetRemote ? RemotePort : LocalPort;
1189 AddressIP->Address[0].Address[0].in_addr = GetRemote ? RemoteAddress : LocalAddress;
1190
1191 return Status;
1192 }
1193
1194 BOOLEAN TCPRemoveIRP( PCONNECTION_ENDPOINT Endpoint, PIRP Irp ) {
1195 PLIST_ENTRY Entry;
1196 PLIST_ENTRY ListHead[5];
1197 KIRQL OldIrql;
1198 PTDI_BUCKET Bucket;
1199 UINT i = 0;
1200 BOOLEAN Found = FALSE;
1201
1202 ListHead[0] = &Endpoint->SendRequest;
1203 ListHead[1] = &Endpoint->ReceiveRequest;
1204 ListHead[2] = &Endpoint->ConnectRequest;
1205 ListHead[3] = &Endpoint->ListenRequest;
1206 ListHead[4] = &Endpoint->ShutdownRequest;
1207
1208 LockObject(Endpoint, &OldIrql);
1209
1210 for( i = 0; i < 5; i++ )
1211 {
1212 for( Entry = ListHead[i]->Flink;
1213 Entry != ListHead[i];
1214 Entry = Entry->Flink )
1215 {
1216 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
1217 if( Bucket->Request.RequestContext == Irp )
1218 {
1219 RemoveEntryList( &Bucket->Entry );
1220 ExFreePoolWithTag( Bucket, TDI_BUCKET_TAG );
1221 Found = TRUE;
1222 break;
1223 }
1224 }
1225 }
1226
1227 UnlockObject(Endpoint, OldIrql);
1228
1229 return Found;
1230 }
1231
1232 /* EOF */