/*++ Copyright (C) Microsoft Corporation, 1996 - 1999 Module Name: Protocol.cxx Abstract: Transport Protocol abstraction used mainly by PnP. The following is a brief description of the current RPC PnP mechanism: Whenever a non-local transport level object (a socket, or an address) is opened for a given protocol, it is added to the list of objects for the given protocol. This alllows all objects for a given protocol to be tracked down and closed if the protocol is unloaded. By the same token, objects closed are removed from the list of the given protocol. When a PnP notification arrives, the lower level transport code call into this PnP module to tell it that there may be a change of state. For each PnP protocol, the PnP code examines whether the protocol is active, and what was its last state. Depending of the outcome of this comparison, it will take appropriate action. Here's the finite state automaton with the transitions: Currently, RPC differentiates between four networking states of each protocol - protocol is not loaded, protocol is partially loaded (i.e. it is active, but does not have an address on it yet), it is fully loaded (or functional), and it is fully loaded and network address change monitoring is required. The fully loaded state with address change monitoring may be abbreviated to FunctionalMon for the purpose of this document. Note that Partially Loaded, and loaded without address are equivalent terms for the purpose of this module. Also, note that currently the networking code does not allow reloading of a protocol, so some of the code paths will never get exercised. RPC in turn maintains the following RPC (as opposed to networking) states: ProtocolNotLoaded ProtocolLoadedWithoutAddress ProtocolWasLoadedOrNeedsActivation ProtocolLoaded ProtocolWasLoadedOrNeedsActivationWithoutAddress ProtocolLoadedAndMonitored Depending on the last RPC state, and the new networking state, the following changes and state transitions are effected: If a PnP notification fails asynchronously, the completion port code will call into PnP code to try to resubmit the queries. Note that the threads on which overlapped WSAIoctl's are submitted are not protected. If a thread dies, the IO will fail, and the thread that picks the failure will resubmit the WSAIoctl. This strategy allows up to keep the thread pool smaller. Networking State Action RPC State ProtocolNotLoaded NotLoaded No-op PartiallyLoaded Submit a WSAIoctl to be notified when the protocol becomes functional. State = ProtocolLoadedWithoutAddress Functional State = ProtocolLoaded FunctionalMon Submit address change WSAIoctl. State = ProtocolLoadedAndMonitored ProtocolLoadedWithoutAddress NotLoaded Cancel WSAIoctl State = ProtocolNotLoaded PartiallyLoaded No-op Functional CancelWSAIoctl State = ProtocolLoaded FunctionalMon Submit address change WSAIoctl if necessary State = ProtocolLoadedAndMonitored ProtocolWasLoadedOrNeedsActivation NotLoaded No-op PartiallyLoaded Submit a WSAIoctl to be notified when the protocol becomes functional. State = ProtocolWasLoadedOrNeedsActivationWithoutAddress Functional Restart protocol State = ProtocolLoaded FunctionalMon Submit address change WSAIoctl. State = ProtocolLoadedAndMonitored ProtocolLoaded NotLoaded Unload protocol State = ProtocolWasLoadedOrNeedsActivation PartiallyLoaded Invalid Functional No-op FunctionalMon Invalid transition ProtocolWasLoadedOrNeedsActivationWithoutAddress NotLoaded Cancel WSAIoctl State = ProtocolWasLoadedOrNeedsActivation PartiallyLoaded No-op Functional Restart protocol State = ProtocolLoaded FunctionalMon Submit address change WSAIoctl. State = ProtocolLoadedAndMonitored ProtocolLoadedAndMonitored NotLoaded Cancel address change WSAIoctl Unload protocol State = ProtocolWasLoadedOrNeedsActivation PartiallyLoaded Resubmit address change WSAIoclt if necessary Functional Invalid transition FunctionalMon No-op ProtocolNeedToLoadWhenReady NotLoaded No-op PartiallyLoaded Submit a WSAIoctl to be notified when the protocol becomes functional. State = ProtocolNeedToLoadWhenReadyWithoutAddress Functional Restart protocol State = ProtocolLoaded FunctionalMon Submit address change WSAIoctl. State = ProtocolLoadedAndMonitored ProtocolNeedToLoadWhenReadyWithoutAddress NotLoaded Cancel WSAIoctl State = ProtocolNeedToLoadWhenReady PartiallyLoaded No-op Functional Restart protocol State = ProtocolLoaded FunctionalMon Submit address change WSAIoctl. State = ProtocolLoadedAndMonitored Author: Kamen Moutafov [KamenM] Revision History: KamenM 12/22/1998 Creation KamenM 03/05/1999 Adding state ProtocolLoadedAndMonitored and support for it. KamenM 07/17/2000 Adding support for ProtocolWasLoadedOrNeedsActivation/ ProtocolWasLoadedOrNeedsActivationWithoutAddress --*/ #include #include void RPC_ENTRY NullAddressChangeFn( PVOID arg ) { } RPC_ADDRESS_CHANGE_FN * AddressChangeFn = NullAddressChangeFn; #ifdef MAJOR_PNP_DEBUG const char *ProtocolStateNames[]; #endif void TransportProtocol::DetectedAsFunctional(PROTOCOL_ID ProtocolId) { if (IsAddressChangeMonitoringOn(ProtocolId)) { EnterCriticalSection(&AddressListLock); // monitor functional protocols for address change will // set the state MonitorFunctionalProtocolForAddressChange(ProtocolId); LeaveCriticalSection(&AddressListLock); } else { // we also need to take the critical section, and cancel any address change // notification (if any), to avoid race between a successful listen making // the protocol functional, and the address change notification completing EnterCriticalSection(&AddressListLock); CancelAddressChangeRequestIfNecessary(FALSE, ProtocolId); SetState(ProtocolLoaded, ProtocolId); LeaveCriticalSection(&AddressListLock); } } void TransportProtocol::HandleProtocolChange(IN WSAPROTOCOL_INFO *lpProtocolBuffer, IN int ProtocolCount, IN PROTOCOL_ID thisProtocolId) /*++ Function Name: HandleProtocolChange Parameters: lpProtocolBuffer - an array of WSAPROTOCOL_INFO structures as returned by EnumProtocols ProtocolCount - the number of elements in the lpProtocolBuffer array thisProtocolId - the ID of the protocol for this object Description: This handles protocol state change for a particular protocol. The function is idempotent - it can be called many times safely, regardless of previous calls. It will turn into no-op if it is redundant. Returns: --*/ { int i; BOOL fProtocolActive = FALSE; const WS_TRANS_INFO *pInfo; ASSERT(ProtocolCount >= 0); ASSERT(lpProtocolBuffer != NULL); ASSERT_TRANSPORT_PROTOCOL_STATE(thisProtocolId); if ( #ifdef NETBIOS_ON (thisProtocolId == NBF) || (thisProtocolId == NBT) || (thisProtocolId == NBI) || #endif #ifdef NCADG_MQ_ON (thisProtocolId == MSMQ) || #endif (thisProtocolId == CDP) ) return; if (IsTrailingProtocol(thisProtocolId)) return; for (i = 0; i < ProtocolCount; i ++) { // if the enumerated protocol is the current protocol, break out of the loop if (MapProtocolId(lpProtocolBuffer[i].iProtocol, lpProtocolBuffer[i].iAddressFamily) == thisProtocolId) { fProtocolActive = TRUE; break; } } pInfo = &WsTransportTable[thisProtocolId]; switch(State) { case ProtocolNotLoaded: case ProtocolLoadedWithoutAddress: // if the protocol was not loaded, but now it is active, attempt to verify // it is operational if (fProtocolActive) { #ifdef MAJOR_PNP_DEBUG if (State == ProtocolNotLoaded) { DbgPrint("Protocol %d was just loaded\n", thisProtocolId); } #endif // If the protocol is not fully functional, we will submit an address change // request to get notified when it does if (VerifyProtocolIsFunctional(thisProtocolId) == TRUE) { // we succeeded in changing the state of the protocol ASSERT((State == ProtocolLoaded) || (State == ProtocolLoadedAndMonitored)); if (IsAddressChangeMonitoringOn(thisProtocolId)) { MonitorFunctionalProtocolForAddressChange(thisProtocolId); (*AddressChangeFn)((PVOID) State); } } #ifdef MAJOR_PNP_DEBUG if (State == ProtocolLoadedWithoutAddress) { DbgPrint("Protocol %d was without an address\n", thisProtocolId); } #endif } else { if (State == ProtocolLoadedWithoutAddress) { // a protocol was removed without being fully initialized // cancel the pending query if any, and reset the state to not loaded CancelAddressChangeRequestIfNecessary(TRUE, thisProtocolId); SetState(ProtocolNotLoaded, thisProtocolId); #ifdef MAJOR_PNP_DEBUG DbgPrint("Protocol %d was removed without being fully initialized\n", thisProtocolId); #endif } // else - don't care. The protocol state is not loaded, and will remain so } break; case ProtocolWasLoadedOrNeedsActivation: case ProtocolWasLoadedOrNeedsActivationWithoutAddress: if (fProtocolActive) { // If the protocol is not fully functional, we will submit an address change // request to get notified when it does if (VerifyProtocolIsFunctional(thisProtocolId)) { // if a protocol was loaded, and now is active, restart the addresses on it RestartProtocol(thisProtocolId); if (thisProtocolId == TCP) { GetTransportProtocol(HTTP)->RestartProtocol(HTTP); } // we succeeded in changing the state of the protocol ASSERT(State == ProtocolLoaded); if (IsAddressChangeMonitoringOn(thisProtocolId)) { MonitorFunctionalProtocolForAddressChange(thisProtocolId); } } } else { // if the protocol was loaded, but it's not active, we don't care; // if it was trying to get an address, but then it was unloaded, // cancel the request if (State == ProtocolWasLoadedOrNeedsActivationWithoutAddress) { CancelAddressChangeRequestIfNecessary(TRUE, thisProtocolId); SetState(ProtocolWasLoadedOrNeedsActivation, thisProtocolId); #ifdef MAJOR_PNP_DEBUG DbgPrint("Protocol %d was removed without being fully initialized\n", thisProtocolId); #endif } } break; case ProtocolLoaded: ASSERT(IsAddressChangeMonitoringOn(thisProtocolId) == FALSE); // if the protocol was loaded, and it is active, we don't need to do anything; // if it was loaded, but is not active currently, we need to unload the protocol if (!fProtocolActive) { #ifdef MAJOR_PNP_DEBUG DbgPrint("Protocol %d was just unloaded\n", thisProtocolId); #endif UnloadProtocol(thisProtocolId); if (thisProtocolId == TCP) { GetTransportProtocol(HTTP)->UnloadProtocol(HTTP); } SetState(ProtocolWasLoadedOrNeedsActivation, thisProtocolId); } break; case ProtocolLoadedAndMonitored: ASSERT(IsAddressChangeMonitoringOn(thisProtocolId)); // if it was loaded, but is not active currently, we need to unload the protocol if (!fProtocolActive) { #ifdef MAJOR_PNP_DEBUG DbgPrint("Protocol %d was just unloaded\n", thisProtocolId); #endif CancelAddressChangeRequestIfNecessary(TRUE, thisProtocolId); UnloadProtocol(thisProtocolId); if (thisProtocolId == TCP) { GetTransportProtocol(HTTP)->UnloadProtocol(HTTP); } SetState(ProtocolWasLoadedOrNeedsActivation, thisProtocolId); (*AddressChangeFn)((PVOID) State); } else { #ifdef MAJOR_PNP_DEBUG DbgPrint("Protocol %d is monitored and received event\n", thisProtocolId); #endif (*AddressChangeFn)((PVOID) State); } break; #if defined(DBG) || defined(_DEBUG) default: ASSERT(!"Invalid State"); #endif } ASSERT_TRANSPORT_PROTOCOL_STATE(thisProtocolId); } void TransportProtocol::AddObjectToList(IN OUT BASE_ASYNC_OBJECT *pObj) /*++ Function Name: AddObjectToList Parameters: pObj - the object to be added Description: Add the object to the list of transport objects for this protocol. Returns: --*/ { EnterCriticalSection(&AddressListLock); RpcpfInsertHeadList(&ObjectList, &pObj->ObjectList); LeaveCriticalSection(&AddressListLock); } void TransportProtocol::RemoveObjectFromList(IN OUT BASE_ASYNC_OBJECT *pObj) /*++ Function Name: RemoveObjectFromList Parameters: pObj - the object to be removed Description: Removes the object from the list of transport objects for this protocol. Returns: --*/ { BASE_ASYNC_OBJECT *Prev, *Cur; Prev = NULL; EnterCriticalSection(&AddressListLock); RpcpfRemoveEntryList(&pObj->ObjectList); LeaveCriticalSection(&AddressListLock); } BOOL TransportProtocol::ResubmitQueriesIfNecessary(PROTOCOL_ID ProtocolId) /*++ Function Name: ResubmitQueriesIfNecessary Parameters: ProtocolId - the ID of the current protocol Description: If there was a WSAIoctl pending that failed, it will be resubmitted. If this protocol was being monitored, try to restart monitoring if necessary Returns: FALSE if the WSAIoctl was not resubmitted successfully. TRUE if the WSAIoctl was resubmitted successfully, or there was no need to resubmit it. --*/ { if (addressChangeSocket) { if ((addressChangeOverlapped.Internal != 0) && (addressChangeOverlapped.Internal != STATUS_PENDING)) { if (SubmitAddressChangeQuery() == FALSE) return FALSE; } } if (State == ProtocolLoadedAndMonitored) { if (MonitorFunctionalProtocolForAddressChange(ProtocolId) == FALSE) return FALSE; } return TRUE; } PROTOCOL_ID MapProtocolId ( IN UINT ProtocolId, IN UINT AddressFamily ) /*++ Function Name: MapProtocolId Parameters: ProtocolId - Winsock protocol ID AddressFamily - Winsock address family Description: Converts a Winsock Protocol ID to a RPC Transport protocol ID. Returns: The RPC Transport Protocol ID if successfull, or -1 if no mapping can be found --*/ { unsigned id; for (id = 1; id < cWsTransportTable; id++) { if ((WsTransportTable[id].Protocol == (int) ProtocolId) && (WsTransportTable[id].AddressFamily == (int) AddressFamily)) { return id; } } return -1; } BOOL TransportProtocol::HandlePnPStateChange(void) /*++ Function Name: HandlePnPNotification Parameters: Description: Whenever a PnP notification (NewAddress) arrives, the completion port will direct it to this routine, which will handle all state management and all handling of the PnP notification. Returns: TRUE if the runtime should be notified that a protocol state change has occurred. FALSE if the run time should not be notified, or need not be notified that a protocol state change has occurred. --*/ { int i; // // Enumerate the currently loaded protocols // WSAPROTOCOL_INFO *lpProtocolBuffer; DWORD dwBufferLength = 512; int ProtocolCount; PROTOCOL_ID ProtocolId; TransportProtocol *pCurrentProtocol; BOOL fRetVal; EnterCriticalSection(&AddressListLock); ASSERT(hWinsock2); while (1) { lpProtocolBuffer = (WSAPROTOCOL_INFO *) I_RpcAllocate(dwBufferLength); if (lpProtocolBuffer == 0) { fRetVal = FALSE; goto CleanupAndReturn; } ProtocolCount = WSAEnumProtocolsT( 0, lpProtocolBuffer, &dwBufferLength ); if (ProtocolCount != SOCKET_ERROR) { break; } I_RpcFree(lpProtocolBuffer); if (GetLastError() != WSAENOBUFS) { fRetVal = FALSE; goto CleanupAndReturn; } } for (i = 1; i < MAX_PROTOCOLS; i++) { pCurrentProtocol = GetTransportProtocol(i); pCurrentProtocol->HandleProtocolChange(lpProtocolBuffer, ProtocolCount, i); } I_RpcFree(lpProtocolBuffer); fRetVal = g_NotifyRt; CleanupAndReturn: LeaveCriticalSection(&AddressListLock); #ifdef MAJOR_PNP_DEBUG DumpProtocolState(); #endif return fRetVal; } BOOL TransportProtocol::ResubmitQueriesIfNecessary(void) /*++ Function Name: ResubmitQueriesIfNecessary Parameters: Description: Iterates through all protocols and calls their ResubmitQueriesIfNecessary Returns: FALSE if at least one protocol needed to resubmit a query, but failed to do so TRUE otherwise --*/ { int i; BOOL fAllResubmitsSucceeded = TRUE; TransportProtocol *pCurrentProtocol; #ifdef MAJOR_PNP_DEBUG DbgPrint("Resubmitting queries for process %d\n", GetCurrentProcessId()); #endif EnterCriticalSection(&AddressListLock); for (i = 1; i < MAX_PROTOCOLS; i++) { pCurrentProtocol = GetTransportProtocol(i); if (!pCurrentProtocol->ResubmitQueriesIfNecessary(i)) fAllResubmitsSucceeded = FALSE; } LeaveCriticalSection(&AddressListLock); return fAllResubmitsSucceeded; } void TransportProtocol::AddObjectToProtocolList(BASE_ASYNC_OBJECT *pObj) { GetTransportProtocol(pObj->id)->AddObjectToList(pObj); } void TransportProtocol::RemoveObjectFromProtocolList(IN OUT BASE_ASYNC_OBJECT *pObj) { // in some cases, we can legally have the id set to INVALID_PROTOCOL_ID // this happens when we have initialized the connection, but have failed // before calling Open on it, and then we attempt to destroy it if (pObj->id != INVALID_PROTOCOL_ID) { GetTransportProtocol(pObj->id)->RemoveObjectFromList(pObj); } } BOOL TransportProtocol::VerifyProtocolIsFunctional(IN PROTOCOL_ID ProtocolId) /*++ Function Name: VerifyProtocolIsFunctional Parameters: ProtocolId - the protocol which we're attempting to verify as functional or not Description: Tries to find out the listening address for a loaded protocol. The address itself is not used anywhere. It just testifies that the protocol is fully operational. Depending on how far it gets with testing whether the protocol is operational, it will change the State to ProtocolLoaded, ProtocolLoadedWithoutAddress or ProtocolWasLoadedOrNeedsActivationWithoutAddress. It will return TRUE iff the state is moved to ProtocolLoaded. Returns: TRUE if the address was found and the protocol was fully operational FALSE if the address could not be found, and the protocol is not fully operational. Note that there are two subcases here. One is when the protocol is not operational (or we cannot confirm that for lack of resources for example) and it has no sign of becoming operational. The second is when the protocol is not operational, but there are chances of it becoming operational. This happens with protocols that take some time to initialize. In the second case, this function will arrange for a retry attempt by posting an async WSAIoctl request to the completion port. In the current code base, we don't differentiate between the two cases because we don't need to. We may need to do so in the future. --*/ { ASSERT((State == ProtocolNotLoaded) || (State == ProtocolWasLoadedOrNeedsActivation) || (State == ProtocolLoadedWithoutAddress) || (State == ProtocolWasLoadedOrNeedsActivationWithoutAddress)); ASSERT_TRANSPORT_PROTOCOL_STATE(ProtocolId); if (OpenAddressChangeRequestSocket(ProtocolId) == FALSE) goto AbortAndCleanup; // if the socket already has an address, skip further checks // NOTE: this check provides a fast way to check initialization state of // protocols that initialize quickly, but more importantly, it provides a // handling path for protocols that have submitted an address list change query // and now are getting back the result if (DoesAddressSocketHaveAddress()) { #ifdef MAJOR_PNP_DEBUG DbgPrint("Protocol %d is functional (1)\n", ProtocolId); #endif CancelAddressChangeRequestIfNecessary(FALSE, ProtocolId); SetStateToLoadedAndMonitorProtocolIfNecessary(ProtocolId); ASSERT_TRANSPORT_PROTOCOL_STATE(ProtocolId); return TRUE; } // if there isn't a pending request, and there isn't a successful request (i.e. there is either // a failed request, or no request has been submitted so far) if ((addressChangeOverlapped.Internal != 0) && (addressChangeOverlapped.Internal != STATUS_PENDING)) { #ifdef MAJOR_PNP_DEBUG DbgPrint("Submitting WSAIoclt for protocol %d\n", ProtocolId); #endif if (!SubmitAddressChangeQuery()) goto AbortAndCleanup; } // check once more whether we have address - this takes care of the race where the // address did arrive between the time we checked for it in the beginning of this // function and the time we submitted the address change query if (DoesAddressSocketHaveAddress()) { #ifdef MAJOR_PNP_DEBUG DbgPrint("Protocol %d is functional (2)\n", ProtocolId); #endif CancelAddressChangeRequestIfNecessary(FALSE, ProtocolId); SetStateToLoadedAndMonitorProtocolIfNecessary(ProtocolId); ASSERT_TRANSPORT_PROTOCOL_STATE(ProtocolId); return TRUE; } // regardless of whether we succeeded immediately or we are pending, advance the // state to ProtocolLoadedWithoutAddress and return not loaded. The completion at the // completion port will take care of the rest if (State == ProtocolWasLoadedOrNeedsActivation) SetState(ProtocolWasLoadedOrNeedsActivationWithoutAddress, ProtocolId); else if (State == ProtocolNotLoaded) SetState(ProtocolLoadedWithoutAddress, ProtocolId); #ifdef MAJOR_PNP_DEBUG else { DbgPrint("VerifyProtocolIsFunctional did not change state for protocol %d, state: %s\n", ProtocolId, ProtocolStateNames[State]); } #endif ASSERT_TRANSPORT_PROTOCOL_STATE(ProtocolId); return FALSE; AbortAndCleanup: // TRUE or FALSE for this argument doesn't matter here - // the operation has failed CancelAddressChangeRequestIfNecessary(FALSE, ProtocolId); ASSERT_TRANSPORT_PROTOCOL_STATE(ProtocolId); return FALSE; } BOOL TransportProtocol::DoesAddressSocketHaveAddress(void) /*++ Function Name: DoesAddressSocketHaveAddress Parameters: Description: Checks if a valid address can be obtained for this protocol. Returns: TRUE - a valid address could be obtained for this protocol FALSE - otherwise --*/ { DWORD byteRet = 0; char buf[40]; ASSERT(addressChangeSocket != 0); if (WSAIoctl(addressChangeSocket, SIO_ADDRESS_LIST_QUERY, 0, 0, buf, sizeof(buf), &byteRet, NULL, NULL) == SOCKET_ERROR) { return FALSE; } // if the result has non-zero length ... if (byteRet != 0) { #if 0 int i; DbgPrint("WSAIoctl returned:\n"); for (i = 0; i < byteRet; i ++) { DbgPrint(" %X", (unsigned long) buf[i]); } DbgPrint("\nWSAIoctl with ADDRESS_LIST_QUERY returned addresses success\n"); #endif // ... and the resulting value is non zero ... if (*(long *)buf != 0) { // ... we have managed to get the true address return TRUE; } } return FALSE; } void TransportProtocol::CancelAddressChangeRequestIfNecessary(BOOL fForceCancel, IN PROTOCOL_ID ProtocolId) /*++ Function Name: CancelAddressChangeRequestIfNecessary Parameters: Description: If there's an active WSAIoctl on the protocol, cancel it by closing the socket. Returns: --*/ { if (addressChangeSocket != 0) { // if the address change monitoring is off, or cancel is // forced, do the actual cancelling. That is, we don't // cancel if this is a monitored protocol and cancel is // optional if (!IsAddressChangeMonitoringOn(ProtocolId) || fForceCancel) { #ifdef MAJOR_PNP_DEBUG DbgPrint("Address change request cancelled\n"); #endif closesocket(addressChangeSocket); addressChangeSocket = 0; addressChangeOverlapped.Internal = -1; } } } void TransportProtocol::RestartProtocol(PROTOCOL_ID ProtocolId) /*++ Function Name: RestartProtocol Parameters: ProtocolId - the protocol to be restarted. Description: Restarts all addresses on this protocol. Returns: --*/ { BASE_ASYNC_OBJECT *Obj; BOOL fAddressFound = 0; RPC_STATUS Status; LIST_ENTRY *CurrentEntry; VALIDATE(ProtocolId) { TCP, #ifdef SPX_ON SPX, #endif #ifdef APPLETALK_ON DSP, #endif HTTP, UDP, #ifdef IPX_ON IPX, #endif TCP_IPv6 } END_VALIDATE; CurrentEntry = ObjectList.Flink; while(CurrentEntry != &ObjectList) { Obj = CONTAINING_RECORD(CurrentEntry, BASE_ASYNC_OBJECT, ObjectList); ASSERT(Obj->id == (int) ProtocolId); if (Obj->type & ADDRESS) { if (Obj->type & DATAGRAM) { Status = DG_ReactivateAddress((WS_DATAGRAM_ENDPOINT *) Obj); } else { Status = WS_ReactivateAddress((WS_ADDRESS *) Obj); } if (Status == RPC_S_OK) { fAddressFound = 1; COMMON_AddressManager((BASE_ADDRESS *) Obj); } } CurrentEntry = CurrentEntry->Flink; } if (fAddressFound) { COMMON_PostNonIoEvent(TRANSPORT, 0, 0); } } void TransportProtocol::UnloadProtocol(PROTOCOL_ID ProtocolId) /*++ Function Name: UnloadProtocol Parameters: ProtocolId - the protocol to be unloaded. Description: Walks the list of the protocol transport objects. Closes the connections, and removes the addresses from the list. Thus failing requests on addresses will not be retried. Returns: --*/ { BASE_ASYNC_OBJECT *Obj; LIST_ENTRY *CurrentEntry; VALIDATE(ProtocolId) { TCP, #ifdef SPX_ON SPX, #endif #ifdef APPLETALK_ON DSP, #endif HTTP, UDP, #ifdef IPX_ON IPX, #endif TCP_IPv6, HTTPv2 } END_VALIDATE; CurrentEntry = ObjectList.Flink; // // - Cleanup all the objects (ie: close the socket). // - Remove all objects other than address objects // from the list. // while(CurrentEntry != &ObjectList) { Obj = CONTAINING_RECORD(CurrentEntry, BASE_ASYNC_OBJECT, ObjectList); ASSERT(Obj->id == (int)ProtocolId); if (Obj->type & ADDRESS) { COMMON_RemoveAddress((BASE_ADDRESS *) Obj); } else { RpcpfRemoveEntryList(&Obj->ObjectList); if (ProtocolId != HTTPv2) WS_Abort(Obj); else HTTP_Abort(Obj); } CurrentEntry = CurrentEntry->Flink; } } BOOL TransportProtocol::SubmitAddressChangeQuery(void) /*++ Function Name: SubmitAddressChangeQuery Parameters: Description: Submits an address change query (WSAIoctl) Returns: TRUE if the WSAIoctl was successfully posted. FALSE otherwise --*/ { ASSERT(addressChangeSocket != 0); static DWORD dwBytesReturned; // // Post an address list change request // addressChangeOverlapped.hEvent = 0; addressChangeOverlapped.Offset = 0; addressChangeOverlapped.OffsetHigh = 0; addressChangeOverlapped.Internal = 0; // submit the address change request - it will always complete on the completion port // we don't care about the dwBytesReturned. The provider requires a valid address, or // it rejects the request if (WSAIoctl(addressChangeSocket, SIO_ADDRESS_LIST_CHANGE, 0, 0, 0, 0, &dwBytesReturned, &addressChangeOverlapped, 0) == SOCKET_ERROR) { if (WSAGetLastError() != ERROR_IO_PENDING) { #ifdef MAJOR_PNP_DEBUG DbgPrint("Submitting WSAIoclt failed with: %d\n", GetLastError()); #endif return FALSE; } } return TRUE; } void TransportProtocol::SetState(TransportProtocolStates newState, PROTOCOL_ID ProtocolId) /*++ Function Name: SetState Parameters: newState - the new state that the protocol needs to move to ProtocolId - the protocol number of this protocol Description: Sets the state of the protocol. Nobody should write the state directly, because protocol state mirroring will stop working. Returns: --*/ { TransportProtocol *CurrentProtocol; #ifdef MAJOR_PNP_DEBUG DbgPrint("Protocol %d moved from state: %s to state: %s\n", ProtocolId, ProtocolStateNames[State], ProtocolStateNames[newState]); #endif // either the address change monitoring is not on for this protocol, or // the protocol doesn't move to loaded state, but not both ASSERT(!IsAddressChangeMonitoringOn(ProtocolId) || (newState != ProtocolLoaded)); State = newState; if (ProtocolId == TCP) { // if a base protocols is moved into loaded and monitored, make sure // the trailing protocols doesn't follow it there. In such a case // the trailing protocol becomes only loaded if (newState == ProtocolLoadedAndMonitored) { GetTransportProtocol(HTTP)->State = ProtocolLoaded; } else { GetTransportProtocol(HTTP)->State = newState; } MirrorProtocolState(TCP_IPv6); } else if (ProtocolId == TCP_IPv6) { MirrorProtocolState(TCP); } } void TransportProtocol::SetStateToLoadedAndMonitorProtocolIfNecessary(PROTOCOL_ID ProtocolId) { if (IsAddressChangeMonitoringOn(ProtocolId)) { // monitor functional protocols for address change will // set the state MonitorFunctionalProtocolForAddressChange(ProtocolId); } else SetState(ProtocolLoaded, ProtocolId); } RPC_STATUS InitTransportProtocols(void) /*++ Function Name: InitTransportProtocols Parameters: Description: Initializes all transport level protocols. This function should be called before any of the TransportProtocol functions. Returns: --*/ { TransportProtocolArray = new TransportProtocol[MAX_PROTOCOLS]; if (TransportProtocolArray == NULL) return (RPC_S_OUT_OF_MEMORY); else return RPC_S_OK; } #if defined(DBG) || defined(_DEBUG) void TransportProtocol::AssertTransportProtocolState(void) /*++ Function Name: AssertTransportProtocolState Parameters: Description: Loops through all protocols and calls AssertState on them Returns: --*/ { int i; for (i = 1; i < MAX_PROTOCOLS; i ++) { GetTransportProtocol(i)->AssertState(i); } } void TransportProtocol::AssertState(PROTOCOL_ID ProtocolId) /*++ Function Name: AssertState Parameters: ProtocolId - the protocol number of the this protocol Description: Currently this includes that addressChangeSocket is set only in the *WithoutAddress states and that all the objects in this protocol list are of the same protocol as this protocol. Returns: --*/ { BASE_ASYNC_OBJECT *pObject; LIST_ENTRY *CurrentEntry; // make sure that the internal state of the object is consistent ASSERT (State >= ProtocolNotLoaded); ASSERT (State <= ProtocolLoadedAndMonitored); if (IsTrailingProtocol(ProtocolId)) { ASSERT(addressChangeSocket == 0); } else { // if we are in one of these states, there should be no address change request pending if ((State == ProtocolNotLoaded) || (State == ProtocolWasLoadedOrNeedsActivation) || (State == ProtocolLoaded)) { ASSERT(addressChangeSocket == 0); } else { // if we are in one of the else states, there must be address change request pending ASSERT(addressChangeSocket != 0); } } // walk the object list and make sure every object is of the same protocol CurrentEntry = ObjectList.Flink; while (CurrentEntry != &ObjectList) { pObject = CONTAINING_RECORD(CurrentEntry, BASE_ASYNC_OBJECT, ObjectList); ASSERT(pObject->id == ProtocolId); CurrentEntry = CurrentEntry->Flink; } } #endif BOOL TransportProtocol::MonitorFunctionalProtocolForAddressChange(PROTOCOL_ID ProtocolId) /*++ Function Name: MonitorFunctionalProtocolForAddressChange Parameters: ProtocolId - the protocol id of the current protocol Description: Makes sure that an already functional protocol is monitored for address change. This is done by: - if an address change socket does not exist, one is opened. - if no address change WSAIoctl is pending on the socket, one is posted. Returns: TRUE if the posting of address change was successful FALSE if it wasn't --*/ { BOOL bRetVal = FALSE; // OpenAddressChangeRequestSocket will take care to check whether there's already // a socket opened if (OpenAddressChangeRequestSocket(ProtocolId) == FALSE) goto Cleanup; if (addressChangeOverlapped.Internal != STATUS_PENDING) { if (SubmitAddressChangeQuery() == FALSE) goto Cleanup; } bRetVal = TRUE; Cleanup: if (State != ProtocolLoadedAndMonitored) SetState(ProtocolLoadedAndMonitored, ProtocolId); return bRetVal; } BOOL TransportProtocol::OpenAddressChangeRequestSocket(PROTOCOL_ID ProtocolId) /*++ Function Name: OpenAddressChangeRequestSocket Parameters: ProtocolId - the protocol id of the current protocol Description: Makes sure that an address change request socket is opened. Returns: TRUE if the opening was successful FALSE if it wasn't --*/ { const WS_TRANS_INFO *pInfo = &WsTransportTable[ProtocolId]; HANDLE h; // we may end up with non-zero addressChangeSocket if this is an address change query // request coming back to us. In this case we don't need to open up another socket if (addressChangeSocket == 0) { ASSERT((State == ProtocolNotLoaded) || (State == ProtocolWasLoadedOrNeedsActivation) || (State == ProtocolLoaded)); // // Create a socket // addressChangeSocket = WSASocketT(pInfo->AddressFamily, pInfo->SocketType, pInfo->Protocol, 0, 0, WSA_FLAG_OVERLAPPED); if (addressChangeSocket == INVALID_SOCKET) { // // We should be able to at least open a socket on the protocol, // if not we got a bogus notification or we're out of resources addressChangeSocket = 0; return FALSE; } // // make the handle non-inheritable so it goes away when we close it. // if (FALSE == SetHandleInformation( (HANDLE) addressChangeSocket, HANDLE_FLAG_INHERIT, 0)) { closesocket(addressChangeSocket); addressChangeSocket = 0; return FALSE; } // associate the socket with the completion port so that we get the notification there h = CreateIoCompletionPort((HANDLE)addressChangeSocket, RpcCompletionPort, NewAddress, 0); if (h == 0) { closesocket(addressChangeSocket); addressChangeSocket = 0; return FALSE; } else { ASSERT(h == RpcCompletionPort); } } else { // we may have a protocol in state ProtocolNotLoaded, because we are just trying // to bring it to loaded state ASSERT((State == ProtocolLoadedWithoutAddress) || (State == ProtocolWasLoadedOrNeedsActivationWithoutAddress) || (State == ProtocolLoadedAndMonitored) || (State == ProtocolNotLoaded)); } #ifdef MAJOR_PNP_DEBUG DbgPrint("Socket was successfully opened for protocol %d\n", ProtocolId); #endif return TRUE; } void TransportProtocol::MirrorProtocolState ( IN PROTOCOL_ID MirrorProtocolId ) /*++ Function Name: MirrorProtocolState Parameters: MirrorProtocolId - the protocol which is mirrored Description: If this is one of the protocols in a dual stack configuration, change the other protocol into appropriate state Returns: --*/ { TransportProtocol *MirrorProtocol; MirrorProtocol = GetTransportProtocol(MirrorProtocolId); if ((State == ProtocolLoadedAndMonitored) || (State == ProtocolLoaded)) { if (MirrorProtocol->State == ProtocolNotLoaded) { MirrorProtocol->SetState(ProtocolWasLoadedOrNeedsActivation, MirrorProtocolId); } else if (MirrorProtocol->State == ProtocolLoadedWithoutAddress) { MirrorProtocol->SetState(ProtocolWasLoadedOrNeedsActivationWithoutAddress, MirrorProtocolId); } } } #ifdef MAJOR_PNP_DEBUG void TransportProtocol::DumpProtocolState(void) /*++ Function Name: DumpProtocolState Parameters: Description: Iterates through all protocol and calls their DumpProtocolState Returns: --*/ { int i; DbgPrint("Dumping protocol state for process %d\n", GetCurrentProcessId()); for (i = 1; i < MAX_PROTOCOLS; i ++) { GetTransportProtocol(i)->DumpProtocolState(i); } } const char *ProtocolStateNames[] = {"ProtocolNotLoaded", "ProtocolLoadedWithoutAddress" , "ProtocolWasLoadedOrNeedsActivation", "ProtocolLoaded", "ProtocolWasLoadedOrNeedsActivationWithoutAddress", "ProtocolLoadedAndMonitored"}; void TransportProtocol::DumpProtocolState(PROTOCOL_ID ProtocolId) /*++ Function Name: DumpProtocolState Parameters: ProtocolId - the protocol number for this protocol. Description: Dumps all significant information for this protcool on the debugger Returns: --*/ { BASE_ASYNC_OBJECT *pCurrentObject; LIST_ENTRY *pCurrentEntry = ObjectList.Flink; int fFirstTime = TRUE; const RPC_CHAR *Protseq; if (TransportTable[ProtocolId].pInfo) Protseq = TransportTable[ProtocolId].pInfo->ProtocolSequence; else Protseq = L"(null)"; DbgPrint("Protocol: %S\n", Protseq); DbgPrint("State: %s, Addr Change Socket: %X, Addr Change Overlapped: %X\n", ProtocolStateNames[State], addressChangeSocket, (ULONG_PTR)&addressChangeOverlapped); while (pCurrentEntry != &ObjectList) { if (fFirstTime) { DbgPrint("Object List:\n"); fFirstTime = FALSE; } pCurrentObject = CONTAINING_RECORD(pCurrentEntry, BASE_ASYNC_OBJECT, ObjectList); DbgPrint("\t%X\n", (ULONG_PTR)pCurrentObject); pCurrentEntry = pCurrentEntry->Flink; } } #endif TransportProtocol *TransportProtocolArray;