/********************************************************************/ /** Copyright(c) 1998 Microsoft Corporation. **/ /********************************************************************/ //*** // // Filename: rasatcp.c // // Description: Contains routines that implement the ATCP functionality. // // History: Feb 26, 1998 Shirish Koti Created original version. // //*** #include #include #include #include #include #include #include #include #include #include #include #include #include #define INCL_HOSTWIRE #include #include #include #include "rasatcp.h" // // Globals // HANDLE AtcpHandle=NULL; CRITICAL_SECTION AtcpCritSect; NET_ADDR AtcpServerAddress; NET_ADDR AtcpDefaultRouter; DWORD AtcpNumConnections=0; UCHAR AtcpServerName[NAMESTR_LEN]; UCHAR AtcpZoneName[ZONESTR_LEN]; //*** // // Function: atcpStartup // This routine does init time setup // // Return: result of operation // //***$ DWORD atcpStartup( IN VOID ) { DWORD dwRetCode=NO_ERROR; DWORD dwSrvNameLen=MAX_COMPUTERNAME_LENGTH+1; // get the server name if (!GetComputerName((LPTSTR)&AtcpServerName[1],&dwSrvNameLen)) { dwRetCode = GetLastError(); ATCP_DBGPRINT(("atcpStartup: GetComputerName failed %ld\n",dwRetCode)); return(dwRetCode); } // store it in Pascal string format AtcpServerName[0] = (BYTE)dwSrvNameLen; InitializeCriticalSection( &AtcpCritSect ); return(dwRetCode); } //*** // // Function: atcpOpenHandle // Opens the RAS device exported by the appletalk stack // // Parameters: None // // Return: None // // Globals: AtcpHandle, if successful // //***$ VOID atcpOpenHandle( IN VOID ) { NTSTATUS status; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING DeviceName; IO_STATUS_BLOCK IoStatus; HANDLE hLocalHandle; if (AtcpHandle) { ATCP_DBGPRINT(("atcpOpenHandle: handle %lx already open!\n",AtcpHandle)); return; } RtlInitUnicodeString( &DeviceName, ARAP_DEVICE_NAME ); InitializeObjectAttributes( &ObjectAttributes, &DeviceName, OBJ_CASE_INSENSITIVE, NULL, NULL ); status = NtCreateFile( &hLocalHandle, SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA, &ObjectAttributes, &IoStatus, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN_IF, 0, NULL, 0 ); if ( NT_SUCCESS(status) ) { AtcpHandle = hLocalHandle; ATCP_DBGPRINT(("atcpOpenHandle: NtCreateFile succeeded\n",status)); } else { ATCP_DBGPRINT(("atcpOpenHandle: NtCreateFile failed %lx\n",status)); } } //*** // // Function: atcpCloseHandle // Closes the RAS device (opened in atcpOpenHandle) // // Parameters: None // // Return: None // // Globals: AtalkHandle // //***$ VOID atcpCloseHandle( IN VOID ) { NTSTATUS status=STATUS_SUCCESS; if (!AtcpHandle) { ATCP_DBGPRINT(("atcpCloseHandle: handle already closed!\n")); return; } status = NtClose( AtcpHandle ); AtcpHandle = NULL; if ( !NT_SUCCESS( status ) ) { ATCP_DBGPRINT(("atcpCloseHandle: NtClose failed %lx\n",status)); ATCP_ASSERT(0); } else { ATCP_DBGPRINT(("atcpCloseHandle: NtClose succeeded\n",status)); } } //*** // // Function: atcpAtkSetup // This is the entry point into the stack to tell the stack to // set up a context for this connection, to get a network address // for the dial-in client, server's zone name, and router address // // Parameters: pAtcpConn - connection context // // Return: status returned by NtDeviceIoControlFile // //***$ DWORD atcpAtkSetup( IN PATCPCONN pAtcpConn, IN ULONG IoControlCode ) { NTSTATUS status; IO_STATUS_BLOCK iosb; HANDLE Event; BYTE Buffer[sizeof(ARAP_SEND_RECV_INFO) + sizeof(ATCPINFO)]; PARAP_SEND_RECV_INFO pSndRcvInfo; PATCPINFO pAtcpInfo; PATCP_SUPPRESS_INFO pSupprInfo; DWORD dwRetCode=NO_ERROR; RtlZeroMemory((PBYTE)Buffer, sizeof(Buffer)); pSndRcvInfo = (PARAP_SEND_RECV_INFO)Buffer; pSndRcvInfo->StatusCode = (DWORD)-1; pSndRcvInfo->pDllContext = (PVOID)pAtcpConn; pSndRcvInfo->IoctlCode = IoControlCode; pSndRcvInfo->ClientAddr = pAtcpConn->ClientAddr; if (IoControlCode == IOCTL_ATCP_SETUP_CONNECTION) { pSndRcvInfo->DataLen = sizeof(ATCPINFO); } else if (IoControlCode == IOCTL_ATCP_SUPPRESS_BCAST) { // if we don't need to suppress broadcasts, done here if ((!pAtcpConn->SuppressRtmp) && (!pAtcpConn->SuppressAllBcast)) { return(NO_ERROR); } pSndRcvInfo->DataLen = sizeof(ATCP_SUPPRESS_INFO); pSupprInfo = (PATCP_SUPPRESS_INFO)&pSndRcvInfo->Data[0]; pSupprInfo->SuppressRtmp = pAtcpConn->SuppressRtmp; pSupprInfo->SuppressAllBcast = pAtcpConn->SuppressAllBcast; } else { pSndRcvInfo->DataLen = 0; } Event = CreateEvent(NULL, FALSE, FALSE, NULL); if (Event == NULL) { ATCP_DBGPRINT(("atcpAtkSetup: CreateEvent failed (%ld)\n",GetLastError())); return(ARAPERR_OUT_OF_RESOURCES); } status = NtDeviceIoControlFile( AtcpHandle, Event, // Event NULL, // ApcRoutine NULL, // ApcContext &iosb, // IoStatusBlock IoControlCode, // IoControlCode Buffer, // InputBuffer sizeof(Buffer), // InputBufferSize Buffer, // OutputBuffer sizeof(Buffer)); // OutputBufferSize if (status == STATUS_PENDING) { status = NtWaitForSingleObject( Event, // Handle TRUE, // Alertable NULL); // Timeout if (NT_SUCCESS(status)) { status = iosb.Status; } } if (status != STATUS_SUCCESS) { ATCP_DBGPRINT(("atcpAtkSetup: NtDeviceIoControlFile failure (%lx)\n", status)); dwRetCode = ARAPERR_IOCTL_FAILURE; } CloseHandle(Event); dwRetCode = pSndRcvInfo->StatusCode; if (dwRetCode != NO_ERROR) { ATCP_DBGPRINT(("atcpAtkSetup: ioctl %lx failed %ld\n", IoControlCode,dwRetCode)); return(dwRetCode); } // // for SETUP ioctl, we have some info from stack we need to copy // if (IoControlCode == IOCTL_ATCP_SETUP_CONNECTION) { pAtcpInfo = (PATCPINFO)&pSndRcvInfo->Data[0]; // get the client's address out EnterCriticalSection(&pAtcpConn->CritSect); pAtcpConn->AtalkContext = pSndRcvInfo->AtalkContext; pAtcpConn->ClientAddr = pSndRcvInfo->ClientAddr; LeaveCriticalSection(&pAtcpConn->CritSect); // // get the default router's address and the zone name // EnterCriticalSection( &AtcpCritSect ); AtcpServerAddress = pAtcpInfo->ServerAddr; AtcpDefaultRouter = pAtcpInfo->DefaultRouterAddr; ATCP_ASSERT(pAtcpInfo->ServerZoneName[0] < ZONESTR_LEN); CopyMemory(&AtcpZoneName[1], &pAtcpInfo->ServerZoneName[1], pAtcpInfo->ServerZoneName[0]); AtcpZoneName[0] = pAtcpInfo->ServerZoneName[0]; // got one more connection! AtcpNumConnections++; LeaveCriticalSection( &AtcpCritSect ); } return(dwRetCode); } //*** // // Function: atcpAllocConnection // This routine allocates an ATCP connection block, initializes // it with info provided by PPP engine. // // Parameters: pInfo - PPPCP_INIT info // // Return: pointer to ATCP connection if successful, NULL otherwise // //***$ PATCPCONN atcpAllocConnection( IN PPPCP_INIT *pPppInit ) { PATCPCONN pAtcpConn=NULL; pAtcpConn = (PATCPCONN)LocalAlloc(LPTR, sizeof(ATCPCONN)); if (pAtcpConn == NULL) { ATCP_DBGPRINT(("atcpAllocConnection: malloc failed\n")); return(NULL); } memset( pAtcpConn, 0, sizeof(ATCPCONN) ); pAtcpConn->Signature = ATCP_SIGNATURE; // by default, broadcasts are not suppressed pAtcpConn->SuppressRtmp = FALSE; pAtcpConn->SuppressAllBcast = FALSE; pAtcpConn->fLineUpDone = FALSE; InitializeCriticalSection( &pAtcpConn->CritSect ); pAtcpConn->hPort = pPppInit->hPort; pAtcpConn->hConnection = pPppInit->hConnection; return(pAtcpConn); } //*** // // Function: atcpParseRequest // This routine parses the incoming ATCP packet and prepares a // response as appropriate (Rej, Nak or Ack) // // AppleTalk-Address // 1 6 0 AT-NET(2) AT-Node (1) // Routing-Protocol // 2 4 0 0 (Routing protocol - last 2 bytes - can be 0, 1, 2, 3: // we only support 0) // Suppress-Broadcasts // 3 2 (to suppress all broadcasts) // 3 3 1 (to suppress RTMP bcasts. We don't support other types) // AT-Compression-Protocol // 4 4 Undefined! // Server-information // 6 Len ..... // Zone-Information // 7 Len ZoneName // Default-Router-Address // 8 6 0 AT-NET(2) AT-Node (1) // // // Parameters: pAtcpConn - the connection // pReceiveBuf - PPP_CONFIG info: the request // pSendBuf - PPP_CONFIG info: our response // cbSendBuf - how big is the Data buffer for our response (for Rej) // ParseResult - array where we mark off options we saw // pfRejectingSomething - pointer to TRUE if Rejecting something // // Return: result of the operation // //***$ DWORD atcpParseRequest( IN PATCPCONN pAtcpConn, IN PPP_CONFIG *pReceiveBuf, OUT PPP_CONFIG *pSendBuf, IN DWORD cbSendBuf, OUT BYTE ParseResult[ATCP_OPT_MAX_VAL+1], OUT BOOL *pfRejectingSomething ) { PPP_OPTION UNALIGNED *pRequest; PPP_OPTION UNALIGNED *pReject; DWORD BytesLeftInSendBuf; PBYTE pOptData; USHORT OptDataLen; USHORT PktLen; USHORT RequestLen; USHORT UnParsedBytes; NET_ADDR ClientAddr; DWORD i; *pfRejectingSomething = FALSE; pRequest = (PPP_OPTION UNALIGNED* )pReceiveBuf->Data; pReject = (PPP_OPTION UNALIGNED* )pSendBuf->Data; BytesLeftInSendBuf = cbSendBuf; PktLen = WireToHostFormat16( pReceiveBuf->Length ); UnParsedBytes = PktLen - PPP_CONFIG_HDR_LEN; // initialize for now to "nothing requested" for (i=0; i 0) { RequestLen = (USHORT)pRequest->Length; if (UnParsedBytes < RequestLen) { ATCP_DBGPRINT(("atcpParseRequest: too few bytes %d vs. %d\n", UnParsedBytes,RequestLen)); return(ERROR_PPP_INVALID_PACKET); } // // assume we're going to accept this option. We'll overwrite if that's // not the case // ParseResult[pRequest->Type] = ATCP_ACK; // the point where the data portion for this option starts pOptData = &pRequest->Data[0]; // remove the Type and Len bytes, remaining is option data OptDataLen = RequestLen - 2; #if 0 ATCP_DBGPRINT(("atcpParseRequest: type %d OptLen %d (", pRequest->Type,OptDataLen)); for (i=0; iType) { // // client wants an appletalk address. We don't allow the client to // request which address he wants. // case ATCP_OPT_APPLETALK_ADDRESS: if (RequestLen != 6) { ATCP_DBGPRINT(("atcpParseRequest: AT_ADDR wrong pktlen %d\n", RequestLen)); return(ERROR_PPP_INVALID_PACKET); } ClientAddr.ata_Network = WireToHostFormat16(&pOptData[1]); ClientAddr.ata_Node = (USHORT)pOptData[3]; if ((ClientAddr.ata_Network == 0) || (ClientAddr.ata_Node == 0) || (ClientAddr.ata_Network != pAtcpConn->ClientAddr.ata_Network) || (ClientAddr.ata_Node != pAtcpConn->ClientAddr.ata_Node)) { ParseResult[pRequest->Type] = ATCP_NAK; } break; // // client wants some routing protocol. we don't send out Routing // info, so we should just Nak this option (unless the client also // is telling us not to send any routing info) // case ATCP_OPT_ROUTING_PROTOCOL: if (RequestLen < 4) { ATCP_DBGPRINT(("atcpParseRequest: ROUTING wrong pktlen %d\n", RequestLen)); return(ERROR_PPP_INVALID_PACKET); } // // we don't send out Routing info, so attempt to negotiate any // other protocol should be Nak'ed // if ((*(USHORT *)&pOptData[0]) != ATCP_OPT_ROUTING_NONE) { ParseResult[pRequest->Type] = ATCP_NAK; } break; // // client wants to suppress broadcasts of some (or all) types of // DDP types. // case ATCP_OPT_SUPPRESS_BROADCAST: // // client wants us to suppress only some bcasts? // if (OptDataLen > 0) { // if requesting RTMP data suppression, we'll allow it if (pOptData[0] == DDPPROTO_RTMPRESPONSEORDATA) { pAtcpConn->SuppressRtmp = TRUE; } // hmm, some other protocol: sorry, no can do else { ATCP_DBGPRINT(("atcpParseRequest: Naking suppression %d\n", pOptData[0])); ParseResult[pRequest->Type] = ATCP_NAK; } } else { pAtcpConn->SuppressAllBcast = TRUE; } break; // // client wants to negotiate some compression. No compression // scheme is defined, so we just have to reject this option // case ATCP_OPT_AT_COMPRESSION_PROTOCOL: ATCP_DBGPRINT(("atcpParseRequest: COMPRESSION sending Rej\n")); if (BytesLeftInSendBuf >= RequestLen) { CopyMemory((PVOID)pReject, (PVOID)pRequest, RequestLen); BytesLeftInSendBuf -= RequestLen; } else { ATCP_DBGPRINT(("atcpParseRequest: PPP engine's buffer too small\n", RequestLen)); return(ERROR_BUFFER_TOO_SMALL); } pReject = (PPP_OPTION UNALIGNED *)((BYTE* )pReject + RequestLen); *pfRejectingSomething = TRUE; ParseResult[pRequest->Type] = ATCP_REJ; break; // // for the following options, we just take note of the fact that // the client has requested it and we send the info over. Nothing // to negotiate in these options. // (We aren't supposed to Nak these either) // case ATCP_OPT_RESERVED: case ATCP_OPT_SERVER_INFORMATION: case ATCP_OPT_ZONE_INFORMATION: case ATCP_OPT_DEFAULT_ROUTER_ADDRESS: break; default: ATCP_DBGPRINT(("atcpParseRequest: unknown type %d\n", pRequest->Type)); return(ERROR_PPP_INVALID_PACKET); } // // move to the next option // UnParsedBytes -= RequestLen; pRequest = (PPP_OPTION UNALIGNED *)((BYTE* )pRequest + RequestLen); } // // see if we are rejecting some option. If so, set some values // if (*pfRejectingSomething) { pSendBuf->Code = CONFIG_REJ; HostToWireFormat16( (USHORT)((PBYTE)pReject - (PBYTE)pSendBuf), pSendBuf->Length ); ATCP_DUMP_BYTES("atcpParseRequest: Rejecting these options:", &pSendBuf->Data[0], (DWORD)WireToHostFormat16( pSendBuf->Length)-4); } return(NO_ERROR); } //*** // // Function: atcpPrepareResponse // This routine prepares a response, depending on what all info // was parsed out from the client's request. // // Parameters: pAtcpConn - the connection // pSendBuf - PPP_CONFIG info: our response // cbSendBuf - how big is the Data buffer for our response // ParseResult - array where we have the parsed info // // Return: result of the operation // //***$ DWORD atcpPrepareResponse( IN PATCPCONN pAtcpConn, OUT PPP_CONFIG *pSendBuf, IN DWORD cbSendBuf, OUT BYTE ParseResult[ATCP_OPT_MAX_VAL+1] ) { DWORD dwRetCode=NO_ERROR; DWORD BytesLeftInSendBuf; PPP_OPTION UNALIGNED *pResponse; PBYTE pOptData; USHORT OptDataLen; USHORT OptionType; DWORD i; BOOL fNakingSomething=FALSE; BOOL fRequestingSomething=FALSE; BOOL fIncludeThisOption; pResponse = (PPP_OPTION UNALIGNED* )pSendBuf->Data; BytesLeftInSendBuf = cbSendBuf; // first find out if we are going to be Nak'ing anything for (OptionType=1; OptionTypeData[0]; OptDataLen = 0; fIncludeThisOption = TRUE; switch (OptionType) { // // tell client (again) the client's network address // case ATCP_OPT_APPLETALK_ADDRESS: OptDataLen = sizeof(NET_ADDR); if (BytesLeftInSendBuf < OptDataLen) { ATCP_DBGPRINT(("atcpPrepareResponse: B: buf too small\n")); return(ERROR_BUFFER_TOO_SMALL); } // skip the reserved byte *pOptData++ = 0; // // if we are sending our REQUEST, send server's address // if (ParseResult[OptionType] == ATCP_REQ) { // put in the network address HostToWireFormat16(AtcpServerAddress.ata_Network, pOptData); pOptData += sizeof(USHORT); // put in the network node ATCP_ASSERT(pAtcpConn->ClientAddr.ata_Node != 0); *pOptData++ = (BYTE)AtcpServerAddress.ata_Node; fRequestingSomething = TRUE; } // // no, we must send the client's network address // else { // put in the network address HostToWireFormat16(pAtcpConn->ClientAddr.ata_Network, pOptData); pOptData += sizeof(USHORT); // put in the network node ATCP_ASSERT(pAtcpConn->ClientAddr.ata_Node != 0); *pOptData++ = (BYTE)pAtcpConn->ClientAddr.ata_Node; } break; // // tell client (again) that we support no routing info // case ATCP_OPT_ROUTING_PROTOCOL: OptDataLen = sizeof(USHORT); HostToWireFormat16(ATCP_OPT_ROUTING_NONE, pOptData); pOptData += sizeof(USHORT); break; // // tell client that we can suppress RTMP or all Bcast // case ATCP_OPT_SUPPRESS_BROADCAST: // if this is an ack, see if we have agreed to suppressing RTMP if (!fNakingSomething) { if (pAtcpConn->SuppressRtmp) { OptDataLen = 1; *pOptData++ = DDPPROTO_RTMPRESPONSEORDATA; } } break; // // we reach here only if are Acking the client's entire request // case ATCP_OPT_SERVER_INFORMATION: ATCP_ASSERT(ParseResult[OptionType] != ATCP_NAK); ATCP_ASSERT(!fNakingSomething); OptDataLen = sizeof(USHORT) + sizeof(DWORD) + AtcpServerName[0]; if (BytesLeftInSendBuf < OptDataLen) { ATCP_DBGPRINT(("atcpPrepareResponse: C: buf too small\n")); return(ERROR_BUFFER_TOO_SMALL); } // copy the server's class-id HostToWireFormat16(ATCP_SERVER_CLASS, pOptData); pOptData += sizeof(USHORT); // copy the server's implementation-id HostToWireFormat32(ATCP_SERVER_IMPLEMENTATION_ID, pOptData); pOptData += sizeof(DWORD); // copy the server's name CopyMemory(pOptData, &AtcpServerName[1], AtcpServerName[0]); break; // // we reach here only if are Acking the client's entire request // case ATCP_OPT_ZONE_INFORMATION: ATCP_ASSERT(ParseResult[OptionType] != ATCP_NAK); ATCP_ASSERT(!fNakingSomething); // if we don't have a zone name, skip this option if (AtcpZoneName[0] == 0) { fIncludeThisOption = FALSE; break; } OptDataLen = AtcpZoneName[0]; if (BytesLeftInSendBuf < OptDataLen) { ATCP_DBGPRINT(("atcpPrepareResponse: D: buf too small\n")); return(ERROR_BUFFER_TOO_SMALL); } // copy the zone name CopyMemory(pOptData, &AtcpZoneName[1], AtcpZoneName[0]); break; // // we reach here only if are Acking the client's entire request // case ATCP_OPT_DEFAULT_ROUTER_ADDRESS: ATCP_ASSERT(ParseResult[OptionType] != ATCP_NAK); ATCP_ASSERT(!fNakingSomething); // if we don't have a router address, skip this option if (AtcpDefaultRouter.ata_Network == 0) { fIncludeThisOption = FALSE; break; } OptDataLen = sizeof(NET_ADDR); if (BytesLeftInSendBuf < OptDataLen) { ATCP_DBGPRINT(("atcpPrepareResponse: E: buf too small\n")); return(ERROR_BUFFER_TOO_SMALL); } // skip the reserved byte *pOptData++ = 0; // put in the network address HostToWireFormat16(AtcpDefaultRouter.ata_Network, pOptData); pOptData += sizeof(USHORT); // put in the network node *pOptData++ = (BYTE)AtcpDefaultRouter.ata_Node; break; default: ATCP_DBGPRINT(("atcpPrepareResponse: opt %d ignored\n",OptionType)); ATCP_ASSERT(0); break; } if (fIncludeThisOption) { BytesLeftInSendBuf -= OptDataLen; pResponse->Type = (BYTE)OptionType; pResponse->Length = OptDataLen + 2; // 2 = 1 Type byte + 1 Length byte pResponse = (PPP_OPTION UNALIGNED *) ((BYTE* )pResponse + pResponse->Length); } } HostToWireFormat16( (USHORT)((PBYTE)pResponse - (PBYTE)pSendBuf), pSendBuf->Length ); pSendBuf->Code = (fNakingSomething) ? CONFIG_NAK : ((fRequestingSomething)? CONFIG_REQ : CONFIG_ACK); #if 0 if (pSendBuf->Code == CONFIG_REQ) { ATCP_DUMP_BYTES("atcpParseRequest: Sending our request:", &pSendBuf->Data[0], (DWORD)WireToHostFormat16( pSendBuf->Length)-4); } else if (pSendBuf->Code == CONFIG_NAK) { ATCP_DUMP_BYTES("atcpParseRequest: Nak'ing these options:", &pSendBuf->Data[0], (DWORD)WireToHostFormat16( pSendBuf->Length)-4); } else { ATCP_DUMP_BYTES("atcpParseRequest: Ack packet from us to client:", &pSendBuf->Data[0], (DWORD)WireToHostFormat16( pSendBuf->Length)-4); } #endif return(NO_ERROR); } //*** // // Function: atcpCloseAtalkConnection // This routine tells the stack to close this ATCP connection // // Parameters: pAtcpConn - the connection to close // // Return: result of the operation // //***$ DWORD atcpCloseAtalkConnection( IN PATCPCONN pAtcpConn ) { DWORD dwRetCode=NO_ERROR; // tell the stack that this connection is going away! dwRetCode = atcpAtkSetup(pAtcpConn, IOCTL_ATCP_CLOSE_CONNECTION); return(dwRetCode); } #if DBG //*** // // Function: atcpDumpBytes // DEBUG only: This routine dumps out a given packet to debugger // // Parameters: Str - string, if any, to be printed out // Packet - packet! // PacketLen - how big is the packet // // Return: none // //***$ VOID atcpDumpBytes( IN PBYTE Str, IN PBYTE Packet, IN DWORD PacketLen ) { DWORD i; if (Str) { DbgPrint("%s: Packet size %ld\n ",Str,PacketLen); } else { DbgPrint("Packet size %ld\n ",PacketLen); } for (i=0; i