- Use a spin lock to protect OSKit instead of a recursive mutex
[reactos.git] / reactos / drivers / network / tcpip / tcpip / fileobjs.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS TCP/IP protocol driver
4 * FILE: tcpip/fileobjs.c
5 * PURPOSE: Routines for handling file objects
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * REVISIONS:
8 * CSH 01/08-2000 Created
9 */
10
11 #include "precomp.h"
12
13
14 /* List of all address file objects managed by this driver */
15 LIST_ENTRY AddressFileListHead;
16 KSPIN_LOCK AddressFileListLock;
17
18 /* List of all connection endpoint file objects managed by this driver */
19 LIST_ENTRY ConnectionEndpointListHead;
20 KSPIN_LOCK ConnectionEndpointListLock;
21
22 /*
23 * FUNCTION: Searches through address file entries to find the first match
24 * ARGUMENTS:
25 * Address = IP address
26 * Port = Port number
27 * Protocol = Protocol number
28 * SearchContext = Pointer to search context
29 * RETURNS:
30 * Pointer to address file, NULL if none was found
31 */
32 PADDRESS_FILE AddrSearchFirst(
33 PIP_ADDRESS Address,
34 USHORT Port,
35 USHORT Protocol,
36 PAF_SEARCH SearchContext)
37 {
38 SearchContext->Address = Address;
39 SearchContext->Port = Port;
40 SearchContext->Next = AddressFileListHead.Flink;
41 SearchContext->Protocol = Protocol;
42
43 return AddrSearchNext(SearchContext);
44 }
45
46 BOOLEAN AddrIsBroadcastMatch(
47 PIP_ADDRESS UnicastAddress,
48 PIP_ADDRESS BroadcastAddress ) {
49 IF_LIST_ITER(IF);
50
51 ForEachInterface(IF) {
52 if ((AddrIsUnspecified(UnicastAddress) ||
53 AddrIsEqual(&IF->Unicast, UnicastAddress)) &&
54 (AddrIsEqual(&IF->Broadcast, BroadcastAddress)))
55 return TRUE;
56 } EndFor(IF);
57
58 return FALSE;
59 }
60
61 BOOLEAN AddrReceiveMatch(
62 PIP_ADDRESS LocalAddress,
63 PIP_ADDRESS RemoteAddress)
64 {
65 if (AddrIsEqual(LocalAddress, RemoteAddress))
66 {
67 /* Unicast address match */
68 return TRUE;
69 }
70
71 if (AddrIsBroadcastMatch(LocalAddress, RemoteAddress))
72 {
73 /* Broadcast address match */
74 return TRUE;
75 }
76
77 if (AddrIsUnspecified(LocalAddress))
78 {
79 /* Local address unspecified */
80 return TRUE;
81 }
82
83 if (AddrIsUnspecified(RemoteAddress))
84 {
85 /* Remote address unspecified */
86 return TRUE;
87 }
88
89 return FALSE;
90 }
91
92 /*
93 * FUNCTION: Searches through address file entries to find next match
94 * ARGUMENTS:
95 * SearchContext = Pointer to search context
96 * RETURNS:
97 * Pointer to address file, NULL if none was found
98 */
99 PADDRESS_FILE AddrSearchNext(
100 PAF_SEARCH SearchContext)
101 {
102 PLIST_ENTRY CurrentEntry;
103 PIP_ADDRESS IPAddress;
104 KIRQL OldIrql;
105 PADDRESS_FILE Current = NULL;
106 BOOLEAN Found = FALSE;
107
108 if (IsListEmpty(SearchContext->Next))
109 return NULL;
110
111 CurrentEntry = SearchContext->Next;
112
113 TcpipAcquireSpinLock(&AddressFileListLock, &OldIrql);
114
115 while (CurrentEntry != &AddressFileListHead) {
116 Current = CONTAINING_RECORD(CurrentEntry, ADDRESS_FILE, ListEntry);
117
118 IPAddress = &Current->Address;
119
120 TI_DbgPrint(DEBUG_ADDRFILE, ("Comparing: ((%d, %d, %s), (%d, %d, %s)).\n",
121 WN2H(Current->Port),
122 Current->Protocol,
123 A2S(IPAddress),
124 WN2H(SearchContext->Port),
125 SearchContext->Protocol,
126 A2S(SearchContext->Address)));
127
128 /* See if this address matches the search criteria */
129 if ((Current->Port == SearchContext->Port) &&
130 (Current->Protocol == SearchContext->Protocol) &&
131 (AddrReceiveMatch(IPAddress, SearchContext->Address))) {
132 /* We've found a match */
133 Found = TRUE;
134 break;
135 }
136 CurrentEntry = CurrentEntry->Flink;
137 }
138
139 TcpipReleaseSpinLock(&AddressFileListLock, OldIrql);
140
141 if (Found) {
142 SearchContext->Next = CurrentEntry->Flink;
143 return Current;
144 } else
145 return NULL;
146 }
147
148 VOID AddrFileFree(
149 PVOID Object)
150 /*
151 * FUNCTION: Frees an address file object
152 * ARGUMENTS:
153 * Object = Pointer to address file object to free
154 */
155 {
156 exFreePool(Object);
157 }
158
159
160 VOID ControlChannelFree(
161 PVOID Object)
162 /*
163 * FUNCTION: Frees an address file object
164 * ARGUMENTS:
165 * Object = Pointer to address file object to free
166 */
167 {
168 exFreePool(Object);
169 }
170
171
172 /*
173 * FUNCTION: Open an address file object
174 * ARGUMENTS:
175 * Request = Pointer to TDI request structure for this request
176 * Address = Pointer to address to be opened
177 * Protocol = Protocol on which to open the address
178 * Options = Pointer to option buffer
179 * RETURNS:
180 * Status of operation
181 */
182 NTSTATUS FileOpenAddress(
183 PTDI_REQUEST Request,
184 PTA_IP_ADDRESS Address,
185 USHORT Protocol,
186 PVOID Options)
187 {
188 PADDRESS_FILE AddrFile;
189
190 TI_DbgPrint(MID_TRACE, ("Called (Proto %d).\n", Protocol));
191
192 AddrFile = exAllocatePool(NonPagedPool, sizeof(ADDRESS_FILE));
193 if (!AddrFile) {
194 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
195 return STATUS_INSUFFICIENT_RESOURCES;
196 }
197
198 TI_DbgPrint(DEBUG_ADDRFILE, ("Address file object allocated at (0x%X).\n", AddrFile));
199
200 RtlZeroMemory(AddrFile, sizeof(ADDRESS_FILE));
201
202 AddrFile->Free = AddrFileFree;
203
204 /* Set our default TTL */
205 AddrFile->TTL = 128;
206
207 /* Make sure address is a local unicast address or 0 */
208 /* FIXME: IPv4 only */
209 AddrFile->Family = Address->Address[0].AddressType;
210 AddrFile->Address.Address.IPv4Address = Address->Address[0].Address[0].in_addr;
211 AddrFile->Address.Type = IP_ADDRESS_V4;
212
213 if (!AddrIsUnspecified(&AddrFile->Address) &&
214 !AddrLocateInterface(&AddrFile->Address)) {
215 exFreePool(AddrFile);
216 TI_DbgPrint(MIN_TRACE, ("Non-local address given (0x%X).\n", A2S(&AddrFile->Address)));
217 return STATUS_INVALID_PARAMETER;
218 }
219
220 TI_DbgPrint(MID_TRACE, ("Opening address %s for communication (P=%d U=%d).\n",
221 A2S(&AddrFile->Address), Protocol, IPPROTO_UDP));
222
223 /* Protocol specific handling */
224 switch (Protocol) {
225 case IPPROTO_TCP:
226 AddrFile->Port =
227 TCPAllocatePort(Address->Address[0].Address[0].sin_port);
228
229 if ((Address->Address[0].Address[0].sin_port &&
230 AddrFile->Port != Address->Address[0].Address[0].sin_port) ||
231 AddrFile->Port == 0xffff)
232 {
233 exFreePool(AddrFile);
234 return STATUS_INVALID_PARAMETER;
235 }
236
237 AddEntity(CO_TL_ENTITY, AddrFile, CO_TL_TCP);
238
239 AddrFile->Send = NULL; /* TCPSendData */
240 break;
241
242 case IPPROTO_UDP:
243 TI_DbgPrint(MID_TRACE,("Allocating udp port\n"));
244 AddrFile->Port =
245 UDPAllocatePort(Address->Address[0].Address[0].sin_port);
246
247 if ((Address->Address[0].Address[0].sin_port &&
248 AddrFile->Port != Address->Address[0].Address[0].sin_port) ||
249 AddrFile->Port == 0xffff)
250 {
251 exFreePool(AddrFile);
252 return STATUS_INVALID_PARAMETER;
253 }
254
255 TI_DbgPrint(MID_TRACE,("Setting port %d (wanted %d)\n",
256 AddrFile->Port,
257 Address->Address[0].Address[0].sin_port));
258
259 AddEntity(CL_TL_ENTITY, AddrFile, CL_TL_UDP);
260
261 AddrFile->Send = UDPSendDatagram;
262 break;
263
264 case IPPROTO_ICMP:
265 AddrFile->Port = 0;
266 AddrFile->Send = ICMPSendDatagram;
267
268 /* FIXME: Verify this */
269 AddEntity(ER_ENTITY, AddrFile, ER_ICMP);
270 break;
271
272 default:
273 /* Use raw IP for all other protocols */
274 AddrFile->Port = 0;
275 AddrFile->Send = RawIPSendDatagram;
276
277 /* FIXME: Verify this */
278 AddEntity(CL_TL_ENTITY, AddrFile, 0);
279 break;
280 }
281
282 TI_DbgPrint(MID_TRACE, ("IP protocol number for address file object is %d.\n",
283 Protocol));
284
285 TI_DbgPrint(MID_TRACE, ("Port number for address file object is %d.\n",
286 WN2H(AddrFile->Port)));
287
288 /* Set protocol */
289 AddrFile->Protocol = Protocol;
290
291 /* Initialize receive and transmit queues */
292 InitializeListHead(&AddrFile->ReceiveQueue);
293 InitializeListHead(&AddrFile->TransmitQueue);
294
295 /* Initialize spin lock that protects the address file object */
296 KeInitializeSpinLock(&AddrFile->Lock);
297
298 /* Return address file object */
299 Request->Handle.AddressHandle = AddrFile;
300
301 /* Add address file to global list */
302 ExInterlockedInsertTailList(
303 &AddressFileListHead,
304 &AddrFile->ListEntry,
305 &AddressFileListLock);
306
307 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
308
309 return STATUS_SUCCESS;
310 }
311
312
313 /*
314 * FUNCTION: Closes an address file object
315 * ARGUMENTS:
316 * Request = Pointer to TDI request structure for this request
317 * RETURNS:
318 * Status of operation
319 */
320 NTSTATUS FileCloseAddress(
321 PTDI_REQUEST Request)
322 {
323 PADDRESS_FILE AddrFile;
324 NTSTATUS Status = STATUS_SUCCESS;
325 KIRQL OldIrql;
326 PDATAGRAM_RECEIVE_REQUEST ReceiveRequest;
327 PDATAGRAM_SEND_REQUEST SendRequest;
328 PLIST_ENTRY CurrentEntry;
329
330 AddrFile = Request->Handle.AddressHandle;
331
332 TI_DbgPrint(MID_TRACE, ("Called.\n"));
333
334 /* Remove address file from the global list */
335 TcpipAcquireSpinLock(&AddressFileListLock, &OldIrql);
336 RemoveEntryList(&AddrFile->ListEntry);
337 TcpipReleaseSpinLock(&AddressFileListLock, OldIrql);
338
339 /* FIXME: Kill TCP connections on this address file object */
340
341 /* Return pending requests with error */
342
343 TI_DbgPrint(DEBUG_ADDRFILE, ("Aborting receive requests on AddrFile at (0x%X).\n", AddrFile));
344
345 /* Go through pending receive request list and cancel them all */
346 while ((CurrentEntry = ExInterlockedRemoveHeadList(&AddrFile->ReceiveQueue, &AddrFile->Lock))) {
347 ReceiveRequest = CONTAINING_RECORD(CurrentEntry, DATAGRAM_RECEIVE_REQUEST, ListEntry);
348 (*ReceiveRequest->Complete)(ReceiveRequest->Context, STATUS_CANCELLED, 0);
349 /* exFreePool(ReceiveRequest); FIXME: WTF? */
350 }
351
352 TI_DbgPrint(DEBUG_ADDRFILE, ("Aborting send requests on address file at (0x%X).\n", AddrFile));
353
354 /* Go through pending send request list and cancel them all */
355 while ((CurrentEntry = ExInterlockedRemoveHeadList(&AddrFile->ReceiveQueue, &AddrFile->Lock))) {
356 SendRequest = CONTAINING_RECORD(CurrentEntry, DATAGRAM_SEND_REQUEST, ListEntry);
357 (*SendRequest->Complete)(SendRequest->Context, STATUS_CANCELLED, 0);
358 exFreePool(SendRequest);
359 }
360
361 /* Protocol specific handling */
362 switch (AddrFile->Protocol) {
363 case IPPROTO_TCP:
364 TCPFreePort( AddrFile->Port );
365 if( AddrFile->Listener )
366 TCPClose( AddrFile->Listener );
367 break;
368
369 case IPPROTO_UDP:
370 UDPFreePort( AddrFile->Port );
371 break;
372 }
373
374 RemoveEntityByContext(AddrFile);
375
376 (*AddrFile->Free)(AddrFile);
377
378 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
379
380 return Status;
381 }
382
383
384 /*
385 * FUNCTION: Opens a connection file object
386 * ARGUMENTS:
387 * Request = Pointer to TDI request structure for this request
388 * ClientContext = Pointer to client context information
389 * RETURNS:
390 * Status of operation
391 */
392 NTSTATUS FileOpenConnection(
393 PTDI_REQUEST Request,
394 PVOID ClientContext)
395 {
396 NTSTATUS Status;
397 PCONNECTION_ENDPOINT Connection;
398
399 TI_DbgPrint(MID_TRACE, ("Called.\n"));
400
401 Connection = TCPAllocateConnectionEndpoint( ClientContext );
402
403 if( !Connection ) return STATUS_NO_MEMORY;
404
405 Status = TCPSocket( Connection, AF_INET, SOCK_STREAM, IPPROTO_TCP );
406
407 if( !NT_SUCCESS(Status) ) {
408 TCPFreeConnectionEndpoint( Connection );
409 return Status;
410 }
411
412 /* Return connection endpoint file object */
413 Request->Handle.ConnectionContext = Connection;
414
415 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
416
417 return STATUS_SUCCESS;
418 }
419
420 /*
421 * FUNCTION: Closes an connection file object
422 * ARGUMENTS:
423 * Request = Pointer to TDI request structure for this request
424 * RETURNS:
425 * Status of operation
426 */
427 NTSTATUS FileCloseConnection(
428 PTDI_REQUEST Request)
429 {
430 PCONNECTION_ENDPOINT Connection;
431
432 TI_DbgPrint(MID_TRACE, ("Called.\n"));
433
434 Connection = Request->Handle.ConnectionContext;
435
436 TCPClose( Connection );
437
438 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
439
440 return STATUS_SUCCESS;
441 }
442
443
444 /*
445 * FUNCTION: Opens a control channel file object
446 * ARGUMENTS:
447 * Request = Pointer to TDI request structure for this request
448 * RETURNS:
449 * Status of operation
450 */
451 NTSTATUS FileOpenControlChannel(
452 PTDI_REQUEST Request)
453 {
454 PCONTROL_CHANNEL ControlChannel;
455 TI_DbgPrint(MID_TRACE, ("Called.\n"));
456
457 ControlChannel = exAllocatePool(NonPagedPool, sizeof(*ControlChannel));
458
459 if (!ControlChannel) {
460 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
461 return STATUS_INSUFFICIENT_RESOURCES;
462 }
463
464 RtlZeroMemory(ControlChannel, sizeof(CONTROL_CHANNEL));
465
466 /* Make sure address is a local unicast address or 0 */
467
468 /* Locate address entry. If specified address is 0, a random address is chosen */
469
470 /* Initialize receive and transmit queues */
471 InitializeListHead(&ControlChannel->ListEntry);
472
473 /* Initialize spin lock that protects the address file object */
474 KeInitializeSpinLock(&ControlChannel->Lock);
475
476 /* Return address file object */
477 Request->Handle.ControlChannel = ControlChannel;
478
479 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
480
481 return STATUS_SUCCESS;
482 }
483
484 /*
485 * FUNCTION: Closes a control channel file object
486 * ARGUMENTS:
487 * Request = Pointer to TDI request structure for this request
488 * RETURNS:
489 * Status of operation
490 */
491 NTSTATUS FileCloseControlChannel(
492 PTDI_REQUEST Request)
493 {
494 PCONTROL_CHANNEL ControlChannel = Request->Handle.ControlChannel;
495 NTSTATUS Status = STATUS_SUCCESS;
496
497 exFreePool(ControlChannel);
498 Request->Handle.ControlChannel = NULL;
499
500 return Status;
501 }
502
503 /* EOF */