/*******************************************************************/ /* Copyright(c) 1993 Microsoft Corporation */ /*******************************************************************/ //*** // // Filename: options.c // // Description: routines for options handling // // Author: Stefan Solomon (stefans) November 24, 1993. // // Revision History: // //*** #include "precomp.h" #pragma hdrstop VOID NetToWideChar( OUT PWCHAR ascp, IN PUCHAR net); extern HANDLE g_hRouterLog; VOID SetNetworkNak(PUCHAR resptr, PIPXCP_CONTEXT contextp); VOID SetNodeNak(PUCHAR resptr, PIPXCP_CONTEXT contextp); VOID SetOptionTypeAndLength(PUCHAR dstptr, UCHAR opttype, UCHAR optlen); #if DBG #define GET_LOCAL_NET GETLONG2ULONG(&dbglocnet, contextp->Config.Network) #else #define GET_LOCAL_NET #endif //*** // // Global description of option handlers // // Input: optptr - pointer to the respective option in the frame // contextp - pointer to the associated context (work buffer) // resptr - pointer to the response frame to be generated // Action - one of: // SNDREQ_OPTION - optptr is the frame to be sent as // a config request; // RCVNAK_OPTION - optptr is the frame received as NAK // RCVREQ_OPTION - optptr is the received request. // resptr is the frame to generate back // a response. If the response is not // an ACK, the return code is FALSE. // In this case if resptr is not NULL it // gets the NAK frame. // //*** BOOL NetworkNumberHandler(PUCHAR optptr, PIPXCP_CONTEXT contextp, PUCHAR resptr, OPT_ACTION Action) { ULONG recvdnet; ULONG localnet; BOOL rc = TRUE; UCHAR newnet[4]; WCHAR asc[9]; PWCHAR ascp; // prepare to log if error ZeroMemory(asc, sizeof(asc)); ascp = asc; switch(Action) { case SNDREQ_OPTION: SetOptionTypeAndLength(optptr, IPX_NETWORK_NUMBER, 6); memcpy(optptr + OPTIONH_DATA, contextp->Config.Network, 4); GETLONG2ULONG(&localnet, contextp->Config.Network); TraceIpx(OPTIONS_TRACE, "NetworkNumberHandler: SND REQ with net 0x%x\n", localnet); break; case RCVNAK_OPTION: contextp->NetNumberNakReceivedCount++; GETLONG2ULONG(&recvdnet, optptr + OPTIONH_DATA); GETLONG2ULONG(&localnet, contextp->Config.Network); TraceIpx(OPTIONS_TRACE, "NetworkNumberHandler: RCV NAK with net 0x%x\n", recvdnet); if(recvdnet > localnet) { if(IsRoute(optptr + OPTIONH_DATA)) { if(GetUniqueHigherNetNumber(newnet, optptr + OPTIONH_DATA, contextp) == NO_ERROR) { // store a new net proposal for the next net send config request memcpy(contextp->Config.Network, newnet, 4); } else { // cannot get a net number unique and higher break; } } else { if((contextp->InterfaceType == IF_TYPE_WAN_WORKSTATION) && GlobalConfig.RParams.EnableGlobalWanNet) { break; } else { memcpy(contextp->Config.Network, optptr + OPTIONH_DATA, 4); } } } break; case RCVACK_OPTION: if(memcmp(contextp->Config.Network, optptr + OPTIONH_DATA, 4)) { rc = FALSE; } break; case RCVREQ_OPTION: // if we have already negotiated and this is a renegociation, stick by // what we have already told the stack in line-up if(contextp->RouteState == ROUTE_ACTIVATED) { TraceIpx(OPTIONS_TRACE, "NetworkNumberHandler: rcv req in re-negociation\n"); if(memcmp(contextp->Config.Network, optptr + OPTIONH_DATA, 4)) { SetNetworkNak(resptr, contextp); rc = FALSE; } break; } GETLONG2ULONG(&recvdnet, optptr + OPTIONH_DATA); GETLONG2ULONG(&localnet, contextp->Config.Network); // check if a network number has been requested if((recvdnet == 0) && ((contextp->InterfaceType == IF_TYPE_STANDALONE_WORKSTATION_DIALOUT) || (contextp->InterfaceType == IF_TYPE_ROUTER_WORKSTATION_DIALOUT))) { // this is a workstation and needs a network number if(GetUniqueHigherNetNumber(newnet, nullnet, contextp) == NO_ERROR) { memcpy(contextp->Config.Network, newnet, 4); } SetNetworkNak(resptr, contextp); rc = FALSE; } else { if(recvdnet > localnet) { // check if we don't have a net number conflict if(IsRoute(optptr + OPTIONH_DATA)) { NetToWideChar(ascp, optptr + OPTIONH_DATA); RouterLogErrorW( g_hRouterLog, ROUTERLOG_IPXCP_NETWORK_NUMBER_CONFLICT, 1, (PWCHAR*)&ascp, NO_ERROR); if(GetUniqueHigherNetNumber(newnet, optptr + OPTIONH_DATA, contextp) == NO_ERROR) { // new net is different, NAK with this new value memcpy(contextp->Config.Network, newnet, 4); } SetNetworkNak(resptr, contextp); rc = FALSE; } else { // the received net number is unique but is different // of the locally configured net number. if((contextp->InterfaceType == IF_TYPE_WAN_WORKSTATION) && GlobalConfig.RParams.EnableGlobalWanNet) { NetToWideChar(ascp, optptr + OPTIONH_DATA); RouterLogErrorW( g_hRouterLog, ROUTERLOG_IPXCP_CANNOT_CHANGE_WAN_NETWORK_NUMBER, 1, (PWCHAR*)&ascp, NO_ERROR); SetNetworkNak(resptr, contextp); rc = FALSE; } else { // router is not installed or net number is unique memcpy(contextp->Config.Network, optptr + OPTIONH_DATA, 4); } } } else { // recvdnet is smaller or equal with the local net if(recvdnet < localnet) { // as per RFC - return the highest network number SetNetworkNak(resptr, contextp); rc = FALSE; } } } break; case SNDNAK_OPTION: // this option has not been requested by the remote end. // Force it to request in a NAK SetNetworkNak(resptr, contextp); GETLONG2ULONG(&localnet, contextp->Config.Network); TraceIpx(OPTIONS_TRACE, "NetworkNumberHandler: SND NAK to force request for net 0x%x\n", localnet); rc = FALSE; break; default: SS_ASSERT(FALSE); break; } return rc; } BOOL NodeNumberHandler(PUCHAR optptr, PIPXCP_CONTEXT contextp, PUCHAR resptr, OPT_ACTION Action) { BOOL rc = TRUE; switch(Action) { case SNDREQ_OPTION: SetOptionTypeAndLength(optptr, IPX_NODE_NUMBER, 8); memcpy(optptr + OPTIONH_DATA, contextp->Config.LocalNode, 6); TraceIpx(OPTIONS_TRACE, "NodeNumberHandler: SND REQ with local node %.2x%.2x%.2x%.2x%.2x%.2x\n", contextp->Config.LocalNode[0], contextp->Config.LocalNode[1], contextp->Config.LocalNode[2], contextp->Config.LocalNode[3], contextp->Config.LocalNode[4], contextp->Config.LocalNode[5]); break; case RCVNAK_OPTION: // If this is server config, then the client has rejected // our local node number. Ignore the suggestion // to use a new one. We will not negociate if(!contextp->Config.ConnectionClient) break; // If we are the client, then we'll be happy to accept // whatever the server assigns us. memcpy(contextp->Config.LocalNode, optptr + OPTIONH_DATA, 6); TraceIpx(OPTIONS_TRACE, "NodeNumberHandler: RCV NAK accepted. New local node %.2x%.2x%.2x%.2x%.2x%.2x\n", contextp->Config.LocalNode[0], contextp->Config.LocalNode[1], contextp->Config.LocalNode[2], contextp->Config.LocalNode[3], contextp->Config.LocalNode[4], contextp->Config.LocalNode[5]); break; case RCVACK_OPTION: if(memcmp(optptr + OPTIONH_DATA, contextp->Config.LocalNode, 6)) { rc = FALSE; } break; case RCVREQ_OPTION: // Is it legal to consider node options at this time? if(contextp->RouteState == ROUTE_ACTIVATED) { TraceIpx(OPTIONS_TRACE, "NodeNumberHandler: rcv req in re-negociation\n"); if(memcmp(contextp->Config.RemoteNode, optptr + OPTIONH_DATA, 6)) { SetNodeNak(resptr, contextp); rc = FALSE; } break; } // Check if the remote machine has specified any node number if(!memcmp(optptr + OPTIONH_DATA, nullnode, 6)) { // the remote node wants us to specify its node number. SetNodeNak(resptr, contextp); TraceIpx(OPTIONS_TRACE, "NodeNumberHandler: RCV REQ with remote node 0x0, snd NAK with remote node %.2x%.2x%.2x%.2x%.2x%.2x\n", contextp->Config.RemoteNode[0], contextp->Config.RemoteNode[1], contextp->Config.RemoteNode[2], contextp->Config.RemoteNode[3], contextp->Config.RemoteNode[4], contextp->Config.RemoteNode[5]); rc = FALSE; } // Otherwise go through the process of determining whether we // are able/willing to accept the remote node number suggested. else { // If we have been set up as the ras server to reject the request for // a specific node number, do so here. if ( (GlobalConfig.AcceptRemoteNodeNumber == 0) && (contextp->InterfaceType == IF_TYPE_WAN_WORKSTATION) && (memcmp(contextp->Config.RemoteNode, optptr + OPTIONH_DATA, 6)) ) { SetNodeNak(resptr, contextp); TraceIpx(OPTIONS_TRACE, "NodeNumberHandler: RCV REQ with remote client node but we force a specific node, snd NAK with remote node %.2x%.2x%.2x%.2x%.2x%.2x\n", contextp->Config.RemoteNode[0], contextp->Config.RemoteNode[1], contextp->Config.RemoteNode[2], contextp->Config.RemoteNode[3], contextp->Config.RemoteNode[4], contextp->Config.RemoteNode[5]); rc = FALSE; } // else, if we are a ras server set up with a global network and the client // requests a specific node number (different from our suggestion), then accept // or reject the node based on whether that node is unique in the global network. else if ( (!contextp->Config.ConnectionClient) && (contextp->InterfaceType == IF_TYPE_WAN_WORKSTATION) && (memcmp(contextp->Config.RemoteNode, optptr + OPTIONH_DATA, 6)) && (GlobalConfig.RParams.EnableGlobalWanNet) ) { ACQUIRE_DATABASE_LOCK; // remove the present node from the node HT RemoveFromNodeHT(contextp); // check the remote node is unique if(NodeIsUnique(optptr + OPTIONH_DATA)) { // copy this value in the context buffer memcpy(contextp->Config.RemoteNode, optptr + OPTIONH_DATA, 6); TraceIpx(OPTIONS_TRACE, "NodeNumberHandler: RCV REQ with remote client node different, ACCEPT it\n"); } else { // proposed node not unique -> NAK it SetNodeNak(resptr, contextp); TraceIpx(OPTIONS_TRACE, "NodeNumberHandler: RCV REQ with non unique remote client node, snd NAK with remote node %.2x%.2x%.2x%.2x%.2x%.2x\n", contextp->Config.RemoteNode[0], contextp->Config.RemoteNode[1], contextp->Config.RemoteNode[2], contextp->Config.RemoteNode[3], contextp->Config.RemoteNode[4], contextp->Config.RemoteNode[5]); rc = FALSE; } // add node to HT AddToNodeHT(contextp); RELEASE_DATABASE_LOCK; } // Otherwise, it's ok to accept the node number that the other side // requests. This is true for ras clients, ras servers that don't enforce // specific node numbers, and ras server that don't assign the same // network number to every dialed in client. else { memcpy(contextp->Config.RemoteNode, optptr + OPTIONH_DATA, 6); TraceIpx(OPTIONS_TRACE, "NodeNumberHandler: RCV REQ with remote node %.2x%.2x%.2x%.2x%.2x%.2x, accepted\n", contextp->Config.RemoteNode[0], contextp->Config.RemoteNode[1], contextp->Config.RemoteNode[2], contextp->Config.RemoteNode[3], contextp->Config.RemoteNode[4], contextp->Config.RemoteNode[5]); } } break; case SNDNAK_OPTION: // the remote node didn't specify this parameter as a desired // parameter. We suggest it what to specify in a further REQ SetNodeNak(resptr, contextp); TraceIpx(OPTIONS_TRACE, "NodeNumberHandler: SND NAK to force the remote to request node %.2x%.2x%.2x%.2x%.2x%.2x\n", contextp->Config.RemoteNode[0], contextp->Config.RemoteNode[1], contextp->Config.RemoteNode[2], contextp->Config.RemoteNode[3], contextp->Config.RemoteNode[4], contextp->Config.RemoteNode[5]); rc = FALSE; break; default: SS_ASSERT(FALSE); break; } return rc; } BOOL RoutingProtocolHandler(PUCHAR optptr, PIPXCP_CONTEXT contextp, PUCHAR resptr, OPT_ACTION Action) { USHORT RoutingProtocol; BOOL rc = TRUE; switch(Action) { case SNDREQ_OPTION: SetOptionTypeAndLength(optptr, IPX_ROUTING_PROTOCOL, 4); PUTUSHORT2SHORT(optptr + OPTIONH_DATA, (USHORT)RIP_SAP_ROUTING); break; case RCVNAK_OPTION: // if this option get NAK-ed, we ignore any other suggestions // for it break; case RCVACK_OPTION: GETSHORT2USHORT(&RoutingProtocol, optptr + OPTIONH_DATA); if(RoutingProtocol != RIP_SAP_ROUTING) { rc = FALSE; } break; case RCVREQ_OPTION: GETSHORT2USHORT(&RoutingProtocol, optptr + OPTIONH_DATA); if(RoutingProtocol != RIP_SAP_ROUTING) { SetOptionTypeAndLength(resptr, IPX_ROUTING_PROTOCOL, 4); PUTUSHORT2SHORT(resptr + OPTIONH_DATA, (USHORT)RIP_SAP_ROUTING); rc = FALSE; } break; case SNDNAK_OPTION: SetOptionTypeAndLength(resptr, IPX_ROUTING_PROTOCOL, 4); PUTUSHORT2SHORT(resptr + OPTIONH_DATA, (USHORT)RIP_SAP_ROUTING); rc = FALSE; break; default: SS_ASSERT(FALSE); break; } return rc; } BOOL CompressionProtocolHandler(PUCHAR optptr, PIPXCP_CONTEXT contextp, PUCHAR resptr, OPT_ACTION Action) { USHORT CompressionProtocol; BOOL rc = TRUE; switch(Action) { case SNDREQ_OPTION: SetOptionTypeAndLength(optptr, IPX_COMPRESSION_PROTOCOL, 4); PUTUSHORT2SHORT(optptr + OPTIONH_DATA, (USHORT)TELEBIT_COMPRESSED_IPX); break; case RCVNAK_OPTION: // if this option gets NAK-ed it means that the remote node doesn't // support Telebit compression but supports another type of compression // that we don't support. In this case we turn off compression negotiation. break; case RCVACK_OPTION: GETSHORT2USHORT(&CompressionProtocol, optptr + OPTIONH_DATA); if(CompressionProtocol != TELEBIT_COMPRESSED_IPX) { rc = FALSE; } else { // Our compression option got ACK-ed by the other end. This means that // we can receive compressed packets and have to set the receive // compression on our end. contextp->SetReceiveCompressionProtocol = TRUE; } break; case RCVREQ_OPTION: // if we have already negotiated and this is a renegociation, stick by // what we have already told the stack in line-up if(contextp->RouteState == ROUTE_ACTIVATED) { TraceIpx(OPTIONS_TRACE, "CompressionProtocolHandler: rcv req in re-negociation\n"); } GETSHORT2USHORT(&CompressionProtocol, optptr + OPTIONH_DATA); if(CompressionProtocol != TELEBIT_COMPRESSED_IPX) { if(resptr) { SetOptionTypeAndLength(resptr, IPX_COMPRESSION_PROTOCOL, 4); PUTUSHORT2SHORT(resptr + OPTIONH_DATA, (USHORT)TELEBIT_COMPRESSED_IPX); } rc = FALSE; } else { // The remote requests the supported compression option and we ACK it. // This means it can receive compressed packets and we have to // set the send compression on our end. contextp->SetSendCompressionProtocol = TRUE; } break; default: SS_ASSERT(FALSE); break; } return rc; } BOOL ConfigurationCompleteHandler(PUCHAR optptr, PIPXCP_CONTEXT contextp, PUCHAR resptr, OPT_ACTION Action) { BOOL rc = TRUE; switch(Action) { case SNDREQ_OPTION: SetOptionTypeAndLength(optptr, IPX_CONFIGURATION_COMPLETE, 2); break; case RCVNAK_OPTION: // if this option gets NAK-ed we ignore any other suggestions case RCVREQ_OPTION: case RCVACK_OPTION: break; case SNDNAK_OPTION: SetOptionTypeAndLength(resptr, IPX_CONFIGURATION_COMPLETE, 2); rc = FALSE; break; default: SS_ASSERT(FALSE); break; } return rc; } VOID CopyOption(PUCHAR dstptr, PUCHAR srcptr) { USHORT optlen; optlen = *(srcptr + OPTIONH_LENGTH); memcpy(dstptr, srcptr, optlen); } VOID SetOptionTypeAndLength(PUCHAR dstptr, UCHAR opttype, UCHAR optlen) { *(dstptr + OPTIONH_TYPE) = opttype; *(dstptr + OPTIONH_LENGTH) = optlen; } VOID SetNetworkNak(PUCHAR resptr, PIPXCP_CONTEXT contextp) { SetOptionTypeAndLength(resptr, IPX_NETWORK_NUMBER, 6); memcpy(resptr + OPTIONH_DATA, contextp->Config.Network, 4); contextp->NetNumberNakSentCount++; } VOID SetNodeNak(PUCHAR resptr, PIPXCP_CONTEXT contextp) { SetOptionTypeAndLength(resptr, IPX_NODE_NUMBER, 8); memcpy(resptr + OPTIONH_DATA, contextp->Config.RemoteNode, 6); }