1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS TCP/IP protocol driver 4 * FILE: transport/tcp/event.c 5 * PURPOSE: Transmission Control Protocol 6 * PROGRAMMERS: Cameron Gutman (cameron.gutman@reactos.org) 7 */ 8 9 #include "precomp.h" 10 11 #include "lwip/err.h" 12 #include "lwip/sys.h" 13 #include "lwip/pbuf.h" 14 #include "lwip/tcp.h" 15 #include "lwip/api.h" 16 17 #include "rosip.h" 18 19 extern NPAGED_LOOKASIDE_LIST TdiBucketLookasideList; 20 21 static 22 VOID 23 BucketCompletionWorker(PVOID Context) 24 { 25 PTDI_BUCKET Bucket = (PTDI_BUCKET)Context; 26 PTCP_COMPLETION_ROUTINE Complete; 27 28 Complete = (PTCP_COMPLETION_ROUTINE)Bucket->Request.RequestNotifyObject; 29 30 Complete(Bucket->Request.RequestContext, Bucket->Status, Bucket->Information); 31 32 DereferenceObject(Bucket->AssociatedEndpoint); 33 34 ExFreeToNPagedLookasideList(&TdiBucketLookasideList, Bucket); 35 } 36 37 VOID 38 CompleteBucket(PCONNECTION_ENDPOINT Connection, PTDI_BUCKET Bucket, const BOOLEAN Synchronous) 39 { 40 ReferenceObject(Connection); 41 Bucket->AssociatedEndpoint = Connection; 42 if (Synchronous) 43 { 44 BucketCompletionWorker(Bucket); 45 } 46 else 47 { 48 ChewCreate(BucketCompletionWorker, Bucket); 49 } 50 } 51 52 VOID 53 FlushReceiveQueue(PCONNECTION_ENDPOINT Connection, const NTSTATUS Status) 54 { 55 PTDI_BUCKET Bucket; 56 PLIST_ENTRY Entry; 57 58 ASSERT_TCPIP_OBJECT_LOCKED(Connection); 59 60 while (!IsListEmpty(&Connection->ReceiveRequest)) 61 { 62 Entry = RemoveHeadList(&Connection->ReceiveRequest); 63 64 Bucket = CONTAINING_RECORD(Entry, TDI_BUCKET, Entry); 65 66 Bucket->Information = 0; 67 Bucket->Status = Status; 68 69 CompleteBucket(Connection, Bucket, FALSE); 70 } 71 } 72 73 VOID 74 FlushSendQueue(PCONNECTION_ENDPOINT Connection, const NTSTATUS Status) 75 { 76 PTDI_BUCKET Bucket; 77 PLIST_ENTRY Entry; 78 79 ASSERT_TCPIP_OBJECT_LOCKED(Connection); 80 81 while (!IsListEmpty(&Connection->SendRequest)) 82 { 83 Entry = RemoveHeadList(&Connection->SendRequest); 84 85 Bucket = CONTAINING_RECORD(Entry, TDI_BUCKET, Entry); 86 87 Bucket->Information = 0; 88 Bucket->Status = Status; 89 90 CompleteBucket(Connection, Bucket, FALSE); 91 } 92 } 93 94 VOID 95 FlushShutdownQueue(PCONNECTION_ENDPOINT Connection, const NTSTATUS Status) 96 { 97 PTDI_BUCKET Bucket; 98 PLIST_ENTRY Entry; 99 100 ASSERT_TCPIP_OBJECT_LOCKED(Connection); 101 102 while (!IsListEmpty(&Connection->ShutdownRequest)) 103 { 104 Entry = RemoveHeadList(&Connection->ShutdownRequest); 105 106 Bucket = CONTAINING_RECORD(Entry, TDI_BUCKET, Entry); 107 108 Bucket->Information = 0; 109 Bucket->Status = Status; 110 111 CompleteBucket(Connection, Bucket, FALSE); 112 } 113 } 114 115 VOID 116 FlushConnectQueue(PCONNECTION_ENDPOINT Connection, const NTSTATUS Status) 117 { 118 PTDI_BUCKET Bucket; 119 PLIST_ENTRY Entry; 120 121 ASSERT_TCPIP_OBJECT_LOCKED(Connection); 122 123 while (!IsListEmpty(&Connection->ConnectRequest)) 124 { 125 Entry = RemoveHeadList(&Connection->ConnectRequest); 126 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry ); 127 128 Bucket->Status = Status; 129 Bucket->Information = 0; 130 131 CompleteBucket(Connection, Bucket, FALSE); 132 } 133 } 134 135 VOID 136 FlushListenQueue(PCONNECTION_ENDPOINT Connection, const NTSTATUS Status) 137 { 138 PTDI_BUCKET Bucket; 139 PLIST_ENTRY Entry; 140 141 ASSERT_TCPIP_OBJECT_LOCKED(Connection); 142 143 while (!IsListEmpty(&Connection->ListenRequest)) 144 { 145 Entry = RemoveHeadList(&Connection->ListenRequest); 146 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry ); 147 148 Bucket->Status = Status; 149 Bucket->Information = 0; 150 151 DereferenceObject(Bucket->AssociatedEndpoint); 152 CompleteBucket(Connection, Bucket, FALSE); 153 } 154 } 155 156 VOID 157 FlushAllQueues(PCONNECTION_ENDPOINT Connection, NTSTATUS Status) 158 { 159 // flush receive queue 160 FlushReceiveQueue(Connection, Status); 161 162 /* We completed the reads successfully but we need to return failure now */ 163 if (Status == STATUS_SUCCESS) 164 { 165 Status = STATUS_FILE_CLOSED; 166 } 167 168 // flush listen queue 169 FlushListenQueue(Connection, Status); 170 171 // flush send queue 172 FlushSendQueue(Connection, Status); 173 174 // flush connect queue 175 FlushConnectQueue(Connection, Status); 176 177 // flush shutdown queue 178 FlushShutdownQueue(Connection, Status); 179 } 180 181 VOID 182 TCPFinEventHandler(void *arg, const err_t err) 183 { 184 PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg, LastConnection; 185 const NTSTATUS Status = TCPTranslateError(err); 186 187 ASSERT(Connection->SocketContext == NULL); 188 ASSERT(Connection->AddressFile); 189 ASSERT(err != ERR_OK); 190 191 LockObject(Connection); 192 193 /* Complete all outstanding requests now */ 194 FlushAllQueues(Connection, Status); 195 196 LockObject(Connection->AddressFile); 197 198 /* Unlink this connection from the address file */ 199 if (Connection->AddressFile->Connection == Connection) 200 { 201 Connection->AddressFile->Connection = Connection->Next; 202 DereferenceObject(Connection); 203 } 204 else if (Connection->AddressFile->Listener == Connection) 205 { 206 Connection->AddressFile->Listener = NULL; 207 DereferenceObject(Connection); 208 } 209 else 210 { 211 LastConnection = Connection->AddressFile->Connection; 212 while (LastConnection->Next != Connection && LastConnection->Next != NULL) 213 LastConnection = LastConnection->Next; 214 if (LastConnection->Next == Connection) 215 { 216 LastConnection->Next = Connection->Next; 217 DereferenceObject(Connection); 218 } 219 } 220 221 UnlockObject(Connection->AddressFile); 222 223 /* Remove the address file from this connection */ 224 DereferenceObject(Connection->AddressFile); 225 Connection->AddressFile = NULL; 226 227 UnlockObject(Connection); 228 } 229 230 VOID 231 TCPAcceptEventHandler(void *arg, PTCP_PCB newpcb) 232 { 233 PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg; 234 PTDI_BUCKET Bucket; 235 PLIST_ENTRY Entry; 236 PIRP Irp; 237 NTSTATUS Status; 238 239 LockObject(Connection); 240 241 while (!IsListEmpty(&Connection->ListenRequest)) 242 { 243 PIO_STACK_LOCATION IrpSp; 244 245 Entry = RemoveHeadList(&Connection->ListenRequest); 246 247 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry ); 248 249 Irp = Bucket->Request.RequestContext; 250 IrpSp = IoGetCurrentIrpStackLocation( Irp ); 251 252 TI_DbgPrint(DEBUG_TCP,("[IP, TCPAcceptEventHandler] Getting the socket\n")); 253 254 Status = TCPCheckPeerForAccept(newpcb, 255 (PTDI_REQUEST_KERNEL)&IrpSp->Parameters); 256 257 TI_DbgPrint(DEBUG_TCP,("Socket: Status: %x\n", Status)); 258 259 Bucket->Status = Status; 260 Bucket->Information = 0; 261 262 if (Status == STATUS_SUCCESS) 263 { 264 LockObject(Bucket->AssociatedEndpoint); 265 266 /* sanity assert...this should never be in anything else but a CLOSED state */ 267 ASSERT( ((PTCP_PCB)Bucket->AssociatedEndpoint->SocketContext)->state == CLOSED ); 268 269 /* free socket context created in FileOpenConnection, as we're using a new one */ 270 LibTCPClose(Bucket->AssociatedEndpoint, TRUE, FALSE); 271 272 /* free previously created socket context (we don't use it, we use newpcb) */ 273 Bucket->AssociatedEndpoint->SocketContext = newpcb; 274 275 UnlockObject(Bucket->AssociatedEndpoint); 276 277 LibTCPAccept(newpcb, (PTCP_PCB)Connection->SocketContext, Bucket->AssociatedEndpoint); 278 } 279 280 DereferenceObject(Bucket->AssociatedEndpoint); 281 282 CompleteBucket(Connection, Bucket, FALSE); 283 284 if (Status == STATUS_SUCCESS) 285 { 286 break; 287 } 288 } 289 290 UnlockObject(Connection); 291 } 292 293 VOID 294 TCPSendEventHandler(void *arg, const u16_t space) 295 { 296 PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg; 297 PTDI_BUCKET Bucket; 298 PLIST_ENTRY Entry; 299 PIRP Irp; 300 NTSTATUS Status; 301 PMDL Mdl; 302 ULONG BytesSent; 303 304 ReferenceObject(Connection); 305 LockObject(Connection); 306 307 while (!IsListEmpty(&Connection->SendRequest)) 308 { 309 UINT SendLen = 0; 310 PVOID SendBuffer = 0; 311 312 Entry = RemoveHeadList(&Connection->SendRequest); 313 314 UnlockObject(Connection); 315 316 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry ); 317 318 Irp = Bucket->Request.RequestContext; 319 Mdl = Irp->MdlAddress; 320 321 TI_DbgPrint(DEBUG_TCP, 322 ("Getting the user buffer from %x\n", Mdl)); 323 324 NdisQueryBuffer( Mdl, &SendBuffer, &SendLen ); 325 326 TI_DbgPrint(DEBUG_TCP, 327 ("Writing %d bytes to %x\n", SendLen, SendBuffer)); 328 329 TI_DbgPrint(DEBUG_TCP, ("Connection: %x\n", Connection)); 330 TI_DbgPrint 331 (DEBUG_TCP, 332 ("Connection->SocketContext: %x\n", 333 Connection->SocketContext)); 334 335 Status = TCPTranslateError(LibTCPSend(Connection, 336 SendBuffer, 337 SendLen, &BytesSent, TRUE)); 338 339 TI_DbgPrint(DEBUG_TCP,("TCP Bytes: %d\n", BytesSent)); 340 341 if( Status == STATUS_PENDING ) 342 { 343 LockObject(Connection); 344 InsertHeadList(&Connection->SendRequest, &Bucket->Entry); 345 break; 346 } 347 else 348 { 349 TI_DbgPrint(DEBUG_TCP, 350 ("Completing Send request: %x %x\n", 351 Bucket->Request, Status)); 352 353 Bucket->Status = Status; 354 Bucket->Information = (Bucket->Status == STATUS_SUCCESS) ? BytesSent : 0; 355 356 CompleteBucket(Connection, Bucket, FALSE); 357 } 358 359 LockObject(Connection); 360 } 361 362 // If we completed all outstanding send requests then finish all pending shutdown requests, 363 // cancel the timer and dereference the connection 364 if (IsListEmpty(&Connection->SendRequest)) 365 { 366 FlushShutdownQueue(Connection, STATUS_SUCCESS); 367 368 if (KeCancelTimer(&Connection->DisconnectTimer)) 369 { 370 DereferenceObject(Connection); 371 } 372 } 373 374 UnlockObject(Connection); 375 376 DereferenceObject(Connection); 377 } 378 379 VOID 380 TCPRecvEventHandler(void *arg) 381 { 382 PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg; 383 PTDI_BUCKET Bucket; 384 PLIST_ENTRY Entry; 385 PIRP Irp; 386 PMDL Mdl; 387 UINT Received; 388 UINT RecvLen; 389 PUCHAR RecvBuffer; 390 NTSTATUS Status; 391 392 LockObject(Connection); 393 394 while(!IsListEmpty(&Connection->ReceiveRequest)) 395 { 396 Entry = RemoveHeadList(&Connection->ReceiveRequest); 397 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry ); 398 399 Irp = Bucket->Request.RequestContext; 400 Mdl = Irp->MdlAddress; 401 402 NdisQueryBuffer( Mdl, &RecvBuffer, &RecvLen ); 403 404 Status = LibTCPGetDataFromConnectionQueue(Connection, RecvBuffer, RecvLen, &Received); 405 if (Status == STATUS_PENDING) 406 { 407 InsertHeadList(&Connection->ReceiveRequest, &Bucket->Entry); 408 break; 409 } 410 411 Bucket->Status = Status; 412 Bucket->Information = Received; 413 414 CompleteBucket(Connection, Bucket, FALSE); 415 } 416 UnlockObject(Connection); 417 } 418 419 VOID 420 TCPConnectEventHandler(void *arg, const err_t err) 421 { 422 PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg; 423 PTDI_BUCKET Bucket; 424 PLIST_ENTRY Entry; 425 426 LockObject(Connection); 427 428 while (!IsListEmpty(&Connection->ConnectRequest)) 429 { 430 Entry = RemoveHeadList(&Connection->ConnectRequest); 431 432 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry ); 433 434 Bucket->Status = TCPTranslateError(err); 435 Bucket->Information = 0; 436 437 CompleteBucket(Connection, Bucket, FALSE); 438 } 439 440 UnlockObject(Connection); 441 } 442