/*++ Copyright (c) 1997-1999 Microsoft Corporation Module Name: frscomm.c Abstract: Routines for the comm layer to convert to and from communication packets. Author: Billy J. Fuller 29-May-1997 David Orbits 21-Mar-2000 Restructured to use table and provide extensible elements. Environment User mode winnt --*/ #include #pragma hdrstop #include extern PGEN_TABLE CompressionTable; // // Types for the common comm subsystem // // WARNING: The order of these entries can never change. This ensures that // packets can be exchanged between uplevel and downlevel members. // typedef enum _COMMTYPE { COMM_NONE = 0, COMM_BOP, // beginning of packet COMM_COMMAND, // command packet stuff COMM_TO, COMM_FROM, COMM_REPLICA, COMM_JOIN_GUID, COMM_VVECTOR, COMM_CXTION, COMM_BLOCK, // file data COMM_BLOCK_SIZE, COMM_FILE_SIZE, COMM_FILE_OFFSET, COMM_REMOTE_CO, // remote change order command COMM_GVSN, // version (guid, vsn) COMM_CO_GUID, // change order guid COMM_CO_SEQUENCE_NUMBER,// CO Seq number for ack. COMM_JOIN_TIME, // machine's can't join if there times or badly out of sync COMM_LAST_JOIN_TIME, // The Last time this connection was joined. // Used to detect Database mismatch. COMM_EOP, // end of packet COMM_REPLICA_VERSION_GUID, // replica version guid (originator guid) COMM_MD5_DIGEST, // md5 digest // // Change Order Record Extension. If not supplied the the ptr for // what was Spare1Bin (now Extension) is left as Null. So comm packets // sent from down level members still work. // COMM_CO_EXT_WIN2K, // in down level code this was called COMM_CO_EXTENSION. // // See comment in schema.h for why we need to seperate the var len // COMM_CO_EXTENSION_2 from COMM_CO_EXT_WIN2K above. // COMM_CO_EXTENSION_2, COMM_COMPRESSION_GUID, // Guid for a supported compression algorithm. // // WARNING: To ensure that down level members can read Comm packets // from uplevel clients always add net data type codes here. // COMM_MAX } COMM_TYPE, *PCOMM_TYPE; #define COMM_NULL_DATA (-1) // // The decode data types are defined below. They are used in the CommPacketTable // to aid in decode dispatching and comm packet construction // They DO NOT get sent in the actual packet. // typedef enum _COMM_PACKET_DECODE_TYPE { COMM_DECODE_NONE = 0, COMM_DECODE_ULONG, COMM_DECODE_ULONG_TO_USHORT, COMM_DECODE_GNAME, COMM_DECODE_BLOB, COMM_DECODE_ULONGLONG, COMM_DECODE_VVECTOR, COMM_DECODE_VAR_LEN_BLOB, COMM_DECODE_REMOTE_CO, COMM_DECODE_GUID, COMM_DECODE_MAX } COMM_PACKET_DECODE_TYPE, *PCOMM_PACKET_DECODE_TYPE; // // The COMM_PACKET_ELEMENT struct is used in a table to describe the data // elements in a Comm packet. // typedef struct _COMM_PACKET_ELEMENT_ { COMM_TYPE CommType; PCHAR CommTag; ULONG DataSize; ULONG DecodeType; ULONG NativeOffset; } COMM_PACKET_ELEMENT, *PCOMM_PACKET_ELEMENT; #define COMM_MEM_SIZE (128) // // Size of the required Beginning-of-packet and End-of-Packet fields // #define MIN_COMM_PACKET_SIZE (2 * (sizeof(USHORT) + sizeof(ULONG) + sizeof(ULONG))) #define COMM_SZ_UL sizeof(ULONG) #define COMM_SZ_ULL sizeof(ULONGLONG) #define COMM_SZ_GUID sizeof(GUID) #define COMM_SZ_GUL sizeof(GUID) + sizeof(ULONG) #define COMM_SZ_GVSN sizeof(GVSN) + sizeof(ULONG) #define COMM_SZ_NULL 0 #define COMM_SZ_COC sizeof(CHANGE_ORDER_COMMAND) + sizeof(ULONG) //#define COMM_SZ_COC CO_PART1_SIZE + CO_PART2_SIZE + CO_PART3_SIZE + sizeof(ULONG) #define COMM_SZ_COEXT_W2K sizeof(CO_RECORD_EXTENSION_WIN2K) + sizeof(ULONG) #define COMM_SZ_MD5 MD5DIGESTLEN + sizeof(ULONG) #define COMM_SZ_JTIME sizeof(ULONGLONG) + sizeof(ULONG) // // Note: When using COMM_DECODE_VAR_LEN_BLOB you must also use COMM_SZ_NULL // in the table below so that no length check is made when the field is decoded. // This allows the field size to grow. Down level members must be able to // handle this by ignoring var len field components they do not understand. // // // The Communication packet element table below is used to construct and // decode comm packet data sent between members. // *** WARNING *** - the order of the rows in the table must match the // the order of the elements in the COMM_TYPE enum. See comments for COMM_TYPE // enum for restrictions on adding new elements to the table. // // Data Element Type DisplayText Size Decode Type Offset to Native Cmd Packet // COMM_PACKET_ELEMENT CommPacketTable[COMM_MAX] = { {COMM_NONE, "NONE" , COMM_SZ_NULL, COMM_DECODE_NONE, 0 }, {COMM_BOP, "BOP" , COMM_SZ_UL, COMM_DECODE_ULONG, RsOffsetSkip }, {COMM_COMMAND, "COMMAND" , COMM_SZ_UL, COMM_DECODE_ULONG_TO_USHORT, OFFSET(COMMAND_PACKET, Command)}, {COMM_TO, "TO" , COMM_SZ_NULL, COMM_DECODE_GNAME, RsOffset(To) }, {COMM_FROM, "FROM" , COMM_SZ_NULL, COMM_DECODE_GNAME, RsOffset(From) }, {COMM_REPLICA, "REPLICA" , COMM_SZ_NULL, COMM_DECODE_GNAME, RsOffset(ReplicaName) }, {COMM_JOIN_GUID, "JOIN_GUID" , COMM_SZ_GUL, COMM_DECODE_BLOB, RsOffset(JoinGuid) }, {COMM_VVECTOR, "VVECTOR" , COMM_SZ_GVSN, COMM_DECODE_VVECTOR, RsOffset(VVector) }, {COMM_CXTION, "CXTION" , COMM_SZ_NULL, COMM_DECODE_GNAME, RsOffset(Cxtion) }, {COMM_BLOCK, "BLOCK" , COMM_SZ_NULL, COMM_DECODE_BLOB, RsOffset(Block) }, {COMM_BLOCK_SIZE, "BLOCK_SIZE" , COMM_SZ_ULL, COMM_DECODE_ULONGLONG, RsOffset(BlockSize) }, {COMM_FILE_SIZE, "FILE_SIZE" , COMM_SZ_ULL, COMM_DECODE_ULONGLONG, RsOffset(FileSize) }, {COMM_FILE_OFFSET, "FILE_OFFSET" , COMM_SZ_ULL, COMM_DECODE_ULONGLONG, RsOffset(FileOffset) }, {COMM_REMOTE_CO, "REMOTE_CO" , COMM_SZ_COC, COMM_DECODE_REMOTE_CO, RsOffset(PartnerChangeOrderCommand)}, {COMM_GVSN, "GVSN" , COMM_SZ_GVSN, COMM_DECODE_BLOB, RsOffset(GVsn) }, {COMM_CO_GUID, "CO_GUID" , COMM_SZ_GUL, COMM_DECODE_BLOB, RsOffset(ChangeOrderGuid) }, {COMM_CO_SEQUENCE_NUMBER, "CO_SEQUENCE_NUMBER" , COMM_SZ_UL, COMM_DECODE_ULONG, RsOffset(ChangeOrderSequenceNumber)}, {COMM_JOIN_TIME, "JOIN_TIME" , COMM_SZ_JTIME, COMM_DECODE_BLOB, RsOffset(JoinTime) }, {COMM_LAST_JOIN_TIME, "LAST_JOIN_TIME" , COMM_SZ_ULL, COMM_DECODE_ULONGLONG, RsOffset(LastJoinTime) }, {COMM_EOP, "EOP" , COMM_SZ_UL, COMM_DECODE_ULONG, RsOffsetSkip }, {COMM_REPLICA_VERSION_GUID, "REPLICA_VERSION_GUID", COMM_SZ_GUL, COMM_DECODE_BLOB, RsOffset(ReplicaVersionGuid)}, {COMM_MD5_DIGEST, "MD5_DIGEST" , COMM_SZ_MD5, COMM_DECODE_BLOB, RsOffset(Md5Digest) }, {COMM_CO_EXT_WIN2K, "CO_EXT_WIN2K" , COMM_SZ_COEXT_W2K,COMM_DECODE_BLOB, RsOffset(PartnerChangeOrderCommandExt)}, {COMM_CO_EXTENSION_2, "CO_EXTENSION_2" , COMM_SZ_NULL, COMM_DECODE_VAR_LEN_BLOB, RsOffset(PartnerChangeOrderCommandExt)}, {COMM_COMPRESSION_GUID, "COMPRESSION_GUID" , COMM_SZ_GUID, COMM_DECODE_GUID, RsOffset(CompressionTable)} }; VOID CommInitializeCommSubsystem( VOID ) /*++ Routine Description: Initialize the generic comm subsystem Arguments: None. Return Value: None. --*/ { #undef DEBSUB #define DEBSUB "CommInitializeCommSubsystem:" // // type must fit into a short // FRS_ASSERT(COMM_MAX <= 0xFFFF); } VOID CommCopyMemory( IN PCOMM_PACKET CommPkt, IN PUCHAR Src, IN ULONG Len ) /*++ Routine Description: Copy memory into a comm packet, extending as necessary Arguments: CommPkt Src Len Return Value: None. --*/ { #undef DEBSUB #define DEBSUB "CommCopyMemory:" ULONG MemLeft; PUCHAR NewPkt; // // Adjust size of comm packet if necessary // // PERF: How many allocs get done to send a CO??? This looks expensive. MemLeft = CommPkt->MemLen - CommPkt->PktLen; if (Len > MemLeft) { // // Just filling memory; extend memory, tacking on a little extra // CommPkt->MemLen = (((CommPkt->MemLen + Len) + (COMM_MEM_SIZE - 1)) / COMM_MEM_SIZE) * COMM_MEM_SIZE; NewPkt = FrsAlloc(CommPkt->MemLen); CopyMemory(NewPkt, CommPkt->Pkt, CommPkt->PktLen); FrsFree(CommPkt->Pkt); CommPkt->Pkt = NewPkt; } // // Copy into the packet // if (Src != NULL) { CopyMemory(CommPkt->Pkt + CommPkt->PktLen, Src, Len); } else { ZeroMemory(CommPkt->Pkt + CommPkt->PktLen, Len); } CommPkt->PktLen += Len; } BOOL CommFetchMemory( IN PCOMM_PACKET CommPkt, IN PUCHAR Dst, IN ULONG Len ) /*++ Routine Description: Fetch memory from a comm packet, reading as necessary Arguments: CommPkt Dst Len Return Value: None. --*/ { #undef DEBSUB #define DEBSUB "CommFetchMemory:" PUCHAR Src; Src = CommPkt->Pkt + CommPkt->UpkLen; CommPkt->UpkLen += Len; if (CommPkt->UpkLen > CommPkt->PktLen || Len > CommPkt->PktLen) { return FALSE; } // // Copy into the packet // CopyMemory(Dst, Src, Len); return TRUE; } VOID CommCompletionRoutine( IN PCOMMAND_PACKET Cmd, IN PVOID Arg ) /*++ Routine Description: Completion routine for comm command servers. Free the comm packet and then call the generic completion routine to free the command packet. Arguments: Cmd - command packet Arg - Cmd->CompletionArg Return Value: None. --*/ { #undef DEBSUB #define DEBSUB "CommCompletionRoutine:" PCOMM_PACKET CommPkt = SRCommPkt(Cmd); PCXTION Cxtion = SRCxtion(Cmd); COMMAND_SND_COMM_TRACE(4, Cmd, Cmd->ErrorStatus, "SndComplete"); // // The SndCs and the ReplicaCs cooperate to limit the number of // active join "pings" so that the Snd threads are not hung // waiting for pings to dead servers to time out. // if ((CommPkt != NULL) && (Cxtion != NULL) && (CommPkt == Cxtion->ActiveJoinCommPkt)) { Cxtion->ActiveJoinCommPkt = NULL; } // // Free the comm packet and the attached return response command packet if // it's still attached. The Replica Cmd Server uses the CMD_JOINING_AFTER_FLUSH // command in this way. // if (CommPkt != NULL) { FrsFree(CommPkt->Pkt); FrsFree(CommPkt); } if (SRCmd(Cmd)) { FrsCompleteCommand(SRCmd(Cmd), Cmd->ErrorStatus); SRCmd(Cmd) = NULL; } // // Free the name/guid and Principal name params. // FrsFreeGName(SRTo(Cmd)); FrsFree(SRPrincName(Cmd)); // // Move the packet to the generic "done" routine // FrsSetCompletionRoutine(Cmd, FrsFreeCommand, NULL); FrsCompleteCommand(Cmd, Cmd->ErrorStatus); } PUCHAR CommGetHdr( IN PUCHAR Pnext, IN PUSHORT PCommType, IN PULONG PLen ) /*++ Routine Description: Get and skip a field header Arguments: Pnext PCommType PLen Return Value: Address of the field's data --*/ { #undef DEBSUB #define DEBSUB "CommGetHdr:" CopyMemory(PCommType, Pnext, sizeof(USHORT)); Pnext += sizeof(USHORT); CopyMemory(PLen, Pnext, sizeof(ULONG)); Pnext += sizeof(ULONG); return Pnext; } BOOL CommCheckPkt( IN PCOMM_PACKET CommPkt ) /*++ Routine Description: Check the packet for consistency Arguments: CommPkt Return Value: TRUE - consistent Otherwise - Assert failure --*/ { #undef DEBSUB #define DEBSUB "CommCheckPkt:" ULONG Len; ULONG Data; PUCHAR Pfirst; PUCHAR Pnext; PUCHAR Pend; USHORT CommType; if (!CommPkt) { return FALSE; } // // Check major. Mismatched majors cannot be handled. // if (CommPkt->Major != NtFrsMajor) { DPRINT2(3, "WARN - RpcCommPkt: MAJOR MISMATCH %d major does not match %d; ignoring\n", CommPkt->Major, NtFrsMajor); return FALSE; } // // Check minor. This service can process packets with mismatched // minors, although some functionality may be lost. // if (CommPkt->Minor != NtFrsCommMinor) { DPRINT2(5, "RpcCommPkt: MINOR MISMATCH %d minor does not match %d\n", CommPkt->Minor, NtFrsCommMinor); } // // Compare the length of the packet with its memory allocation // if (CommPkt->PktLen > CommPkt->MemLen) { DPRINT2(4, "RpcCommPkt: Packet size (%d) > Alloced Memory (%d)\n", CommPkt->PktLen, CommPkt->MemLen); return FALSE; } // // Must have at least a beginning-of-packet and end-of-packet field // if (CommPkt->PktLen < MIN_COMM_PACKET_SIZE) { DPRINT2(4, "RpcCommPkt: Packet size (%d) < Minimum size (%d)\n", CommPkt->PktLen, MIN_COMM_PACKET_SIZE); return FALSE; } // // packets begin with a beginning-of-packet // Pfirst = CommPkt->Pkt; Pnext = CommGetHdr(Pfirst, &CommType, &Len); if (CommType != COMM_BOP || Len != sizeof(ULONG)) { return FALSE; } CopyMemory(&Data, Pnext, sizeof(ULONG)); if (Data != 0) { return FALSE; } // // packets end with an end-of-packet // Pend = Pfirst + CommPkt->PktLen; if (Pend <= Pfirst) { return FALSE; } Pnext = ((Pend - sizeof(USHORT)) - sizeof(ULONG)) - sizeof(ULONG); Pnext = CommGetHdr(Pnext, &CommType, &Len); if (CommType != COMM_EOP || Len != sizeof(ULONG)) { return FALSE; } CopyMemory(&Data, Pnext, sizeof(ULONG)); if (Data != COMM_NULL_DATA) { return FALSE; } return TRUE; } VOID CommDumpCommPkt( IN PCOMM_PACKET CommPkt, IN DWORD NumDump ) /*++ Routine Description: Dump some of the comm packet Arguments: CommPkt NumDump Return Value: None. --*/ { #undef DEBSUB #define DEBSUB "CommDumpCommPkt:" ULONG Len; PUCHAR Pnext; USHORT CommType; DWORD i; DPRINT1(0, "%x:\n", CommPkt); DPRINT1(0, "\tMajor: %d\n", CommPkt->Major); DPRINT1(0, "\tMinor: %d\n", CommPkt->Minor); DPRINT1(0, "\tMemLen: %d\n", CommPkt->MemLen); DPRINT1(0, "\tPktLen: %d\n", CommPkt->PktLen); DPRINT1(0, "\tPkt: 0x%x\n", CommPkt->Pkt); // // packets begin with a beginning-of-packet // Pnext = CommPkt->Pkt; for (i = 0; i < NumDump; ++i) { Pnext = CommGetHdr(Pnext, &CommType, &Len); DPRINT4(0, "Dumping %d for %x: %d %d\n", i, CommPkt, CommType, Len); Pnext += Len; } } VOID CommPackULong( IN PCOMM_PACKET CommPkt, IN COMM_TYPE Type, IN ULONG Data ) /*++ Routine Description: Copy a header and a ulong into the comm packet. Arguments: CommPkt Type Data Return Value: None. --*/ { #undef DEBSUB #define DEBSUB "CommPackULong:" ULONG Len = sizeof(ULONG); USHORT CommType = (USHORT)Type; CommCopyMemory(CommPkt, (PUCHAR)&CommType, sizeof(USHORT)); CommCopyMemory(CommPkt, (PUCHAR)&Len, sizeof(ULONG)); CommCopyMemory(CommPkt, (PUCHAR)&Data, sizeof(ULONG)); } PCOMM_PACKET CommStartCommPkt( IN PWCHAR Name ) /*++ Routine Description: Allocate a comm packet. Arguments: Name Return Value: Address of a comm packet. --*/ { #undef DEBSUB #define DEBSUB "CommStartCommPkt:" ULONG Size; PCOMM_PACKET CommPkt; // // We can create a comm packet in a file or in memory // CommPkt = FrsAlloc(sizeof(COMM_PACKET)); Size = COMM_MEM_SIZE; CommPkt->Pkt = FrsAlloc(Size); CommPkt->MemLen = Size; CommPkt->Major = NtFrsMajor; CommPkt->Minor = NtFrsCommMinor; // // Pack the beginning-of-packet // CommPackULong(CommPkt, COMM_BOP, 0); return CommPkt; } BOOL CommUnpackBlob( IN PCOMM_PACKET CommPkt, OUT ULONG *OutBlobSize, OUT PVOID *OutBlob ) /*++ Routine Description: Unpack a blob (length + data) Arguments: CommPkt OutBlobSize - size of blob from comm packet OutBlob - data from comm packet Return Value: TRUE - Blob retrieved from comm packet FALSE - Blob was not retrieved from comm packet; bad comm packet --*/ { #undef DEBSUB #define DEBSUB "CommUnpackBlob:" ULONG BlobSize; // // Initialize return params // *OutBlob = NULL; // // Unpack the length of the blob // if (!CommFetchMemory(CommPkt, (PUCHAR)OutBlobSize, sizeof(ULONG))) { return FALSE; } BlobSize = *OutBlobSize; // // Empty blob, return NULL // if (BlobSize == 0) { return TRUE; } // // Allocate memory for the blob // *OutBlob = FrsAlloc(BlobSize); // // Unpack the blob // return CommFetchMemory(CommPkt, (PUCHAR)*OutBlob, BlobSize); } BOOL CommUnpackGName( IN PCOMM_PACKET CommPkt, OUT PGNAME *OutGName ) /*++ Routine Description: Unpack the guid and wide char string that make up a gstring Arguments: CommPkt OutGName - From comm packet Return Value: TRUE - GName fetched from comm packet successfully FALSE - GName was not fetched from comm packet; bad comm packet --*/ { #undef DEBSUB #define DEBSUB "CommUnpackGName:" ULONG BlobSize; PGNAME GName; // // Allocate a gstring (caller cleans up on error) // *OutGName = GName = FrsAlloc(sizeof(GNAME)); if (!CommUnpackBlob(CommPkt, &BlobSize, &GName->Guid) || BlobSize != sizeof(GUID)) { return FALSE; } if (!CommUnpackBlob(CommPkt, &BlobSize, &GName->Name) || GName->Name[(BlobSize / sizeof(WCHAR)) - 1] != L'\0') { return FALSE; } return TRUE; } BOOL CommGetNextElement( IN PCOMM_PACKET CommPkt, OUT COMM_TYPE *CommType, OUT ULONG *CommTypeSize ) /*++ Routine Description: Advance to the next field in the comm packet Arguments: CommPkt CommType - type of packed field CommTypeSize - size of packed field (excluding type and size) Return Value: TRUE - CommType and CommTypeSize were unpacked FALSE - Could not unpack; bad comm packet --*/ { #undef DEBSUB #define DEBSUB "CommGetNextElement:" USHORT Ushort; // // Find the type and length of this entry // if (CommFetchMemory(CommPkt, (PUCHAR)&Ushort, sizeof(USHORT)) && CommFetchMemory(CommPkt, (PUCHAR)CommTypeSize, sizeof(ULONG))) { *CommType = Ushort; return TRUE; } return FALSE; } VOID CommInsertDataElement( IN PCOMM_PACKET CommPkt, IN COMM_TYPE CommType, IN PVOID CommData, IN ULONG CommDataLen ) /*++ Routine Description: Insert the data supplied using the CommType specific format into the Comm packet. Arguments: CommPkt - The Comm packet structure. CommType - The data type for this element. CommData - The address of the data. CommDataLen - The size for var len elements. Return Value: None. --*/ { #undef DEBSUB #define DEBSUB "CommInsertDataElement:" ULONG Len; PGNAME GName; ULONG LenGuid; ULONG LenName; ULONG DataSize; ULONG DecodeType; PCHAR CommTag; if (CommData == NULL) { return; } FRS_ASSERT((CommType < COMM_MAX) && (CommType != COMM_NONE)); // // Table index MUST match table CommType Field or table is fouled up. // FRS_ASSERT(CommType == CommPacketTable[CommType].CommType); // // Length check from table for fixed length fields. // //DataSize = CommPacketTable[CommType].DataSize; //FRS_ASSERT((DataSize == COMM_SZ_NULL) || (CommDataLen == DataSize)); // // Insert the data using the data type encoding. // DecodeType = CommPacketTable[CommType].DecodeType; CommTag = CommPacketTable[CommType].CommTag; switch (DecodeType) { // // Insert a ULONG size piece of data. // case COMM_DECODE_ULONG: case COMM_DECODE_ULONG_TO_USHORT: Len = sizeof(ULONG); DPRINT2(5, ":SR: Dec_long: type: %s, len: %d\n", CommTag, Len); CommCopyMemory(CommPkt, (PUCHAR)&CommType, sizeof(USHORT)); CommCopyMemory(CommPkt, (PUCHAR)&Len, sizeof(ULONG)); CommCopyMemory(CommPkt, (PUCHAR)CommData, sizeof(ULONG)); break; // // Insert a Guid and Name string (GNAME). // case COMM_DECODE_GNAME: GName = (PGNAME)CommData; LenGuid = sizeof(GUID); LenName = (wcslen(GName->Name) + 1) * sizeof(WCHAR); Len = LenGuid + LenName + (2 * sizeof(ULONG)); DPRINT3(5, ":SR: Dec_gname: type: %s, len: %d - %ws\n", CommTag, Len, GName->Name); CommCopyMemory(CommPkt, (PUCHAR)&CommType, sizeof(USHORT)); CommCopyMemory(CommPkt, (PUCHAR)&Len, sizeof(ULONG)); CommCopyMemory(CommPkt, (PUCHAR)&LenGuid, sizeof(ULONG)); CommCopyMemory(CommPkt, (PUCHAR)GName->Guid, LenGuid); CommCopyMemory(CommPkt, (PUCHAR)&LenName, sizeof(ULONG)); CommCopyMemory(CommPkt, (PUCHAR)GName->Name, LenName); break; // // Insert a ULONGLONG. // case COMM_DECODE_ULONGLONG: Len = sizeof(ULONGLONG); DPRINT2(5, ":SR: Dec_longlong: type: %s, len: %d\n", CommTag, Len); CommCopyMemory(CommPkt, (PUCHAR)&CommType, sizeof(USHORT)); CommCopyMemory(CommPkt, (PUCHAR)&Len, sizeof(ULONG)); CommCopyMemory(CommPkt, (PUCHAR)CommData, sizeof(ULONGLONG)); break; // // Insert a Guid. // case COMM_DECODE_GUID: Len = sizeof(GUID); DPRINT2(5, ":SR: Dec_Guid: type: %s, len: %d\n", CommTag, Len); CommCopyMemory(CommPkt, (PUCHAR)&CommType, sizeof(USHORT)); CommCopyMemory(CommPkt, (PUCHAR)&Len, sizeof(ULONG)); CommCopyMemory(CommPkt, (PUCHAR)CommData, sizeof(GUID)); break; case COMM_DECODE_VVECTOR: // // Version Vector data gets inserted into Comm packet as blobs. // NOTHING; /* FALL THRU INTENDED */ // // Insert a variable length BLOB. The problem with blobs as currently // shipped in win2k is that the code on the unpack side checks for a // match on a constant length based on the COMM Data Type. This means // that a var len datatype like CHANGE_ORDER_EXTENSION can't change because // the 40 byte size is wired into the code of down level members. Sigh. // case COMM_DECODE_BLOB: Len = CommDataLen + sizeof(ULONG); DPRINT2(5, ":SR: Dec_blob: type: %s, len: %d\n", CommTag, Len); CommCopyMemory(CommPkt, (PUCHAR)&CommType, sizeof(USHORT)); CommCopyMemory(CommPkt, (PUCHAR)&Len, sizeof(ULONG)); CommCopyMemory(CommPkt, (PUCHAR)&CommDataLen, sizeof(ULONG)); CommCopyMemory(CommPkt, (PUCHAR)CommData, CommDataLen); break; // // Insert a true variable length data struct that is extensible. // The actual length comes from the first DWORD of the data. // case COMM_DECODE_VAR_LEN_BLOB: Len = *(PULONG)CommData; DPRINT2(5, ":SR: Dec_var_len_blob: type: %s, len: %d\n", CommTag, Len); CommCopyMemory(CommPkt, (PUCHAR)&CommType, sizeof(USHORT)); CommCopyMemory(CommPkt, (PUCHAR)&Len, sizeof(ULONG)); CommCopyMemory(CommPkt, (PUCHAR)CommData, CommDataLen); break; // // The CO contains four pointers occupying 16 bytes on 32 bit architectures and // 32 bytes on 64 bit architectures (PART2). When the CO is sent in a comm packet // the contents of these pointers are irrelevant so in comm packets these // ptrs are always sent as 16 bytes of zeros, regardless of architecture. // Note - In 32 bit Win2k this was sent as a BLOB so it matches BLOB format. // case COMM_DECODE_REMOTE_CO: Len = COMM_SZ_COC; CommDataLen = Len - sizeof(ULONG); DPRINT2(4, ":SR: Dec_remote_co: type: %s, len: %d\n", CommTag, Len); CommCopyMemory(CommPkt, (PUCHAR)&CommType, sizeof(USHORT)); CommCopyMemory(CommPkt, (PUCHAR)&Len, sizeof(ULONG)); CommCopyMemory(CommPkt, (PUCHAR)&CommDataLen, sizeof(ULONG)); CommCopyMemory(CommPkt, (PUCHAR)CommData, sizeof(CHANGE_ORDER_COMMAND)); //CommCopyMemory(CommPkt, ((PUCHAR)CommData)+CO_PART1_OFFSET, CO_PART1_SIZE); //CommCopyMemory(CommPkt, NULL, CO_PART2_SIZE); //CommCopyMemory(CommPkt, ((PUCHAR)CommData)+CO_PART3_OFFSET, CO_PART3_SIZE); break; default: // // Table must be fouled up. // FRS_ASSERT((DecodeType > COMM_DECODE_NONE) && (DecodeType < COMM_DECODE_MAX)); break; } return; } PCOMM_PACKET CommBuildCommPkt( IN PREPLICA Replica, IN PCXTION Cxtion, IN ULONG Command, IN PGEN_TABLE VVector, IN PCOMMAND_PACKET Cmd, IN PCHANGE_ORDER_COMMAND Coc ) /*++ Routine Description: Generate a comm packet with the info needed to execute the command on the remote machine identified by Cxtion. Arguments: Replica - Sender Cxtion - identifies the remote machine Command - command to execute on the remote machine VVector - some commands require the version vector Cmd - original command packet Coc - change order command RemoteGVsn - guid/vsn pair Return Value: Address of a comm packet. --*/ { #undef DEBSUB #define DEBSUB "CommBuildCommPkt:" ULONGLONG FileTime; GNAME GName; PVOID Key; PCOMM_PACKET CommPkt; PGVSN GVsn; PGEN_ENTRY Entry; // // Allocate and initialize a comm packet // CommPkt = CommStartCommPkt(NULL); CommPkt->CsId = CS_RS; CommInsertDataElement(CommPkt, COMM_COMMAND, &Command, 0); CommInsertDataElement(CommPkt, COMM_TO, Cxtion->Partner, 0); CommInsertDataElement(CommPkt, COMM_FROM, Replica->MemberName, 0); GName.Guid = Cxtion->Partner->Guid; GName.Name = Replica->ReplicaName->Name; CommInsertDataElement(CommPkt, COMM_REPLICA, &GName, 0); CommInsertDataElement(CommPkt, COMM_CXTION, Cxtion->Name, 0); CommInsertDataElement(CommPkt, COMM_JOIN_GUID, &Cxtion->JoinGuid, sizeof(GUID)); CommInsertDataElement(CommPkt, COMM_LAST_JOIN_TIME, &Cxtion->LastJoinTime, 0); // // Version vector (if supplied) // // // The caller is building a comm packet for join operation, // automatically include the current time and the originator guid. // if (VVector) { Key = NULL; while (GVsn = GTabNextDatum(VVector, &Key)) { CommInsertDataElement(CommPkt, COMM_VVECTOR, GVsn, sizeof(GVSN)); } GetSystemTimeAsFileTime((FILETIME *)&FileTime); CommInsertDataElement(CommPkt, COMM_JOIN_TIME, &FileTime, sizeof(ULONGLONG)); DPRINT1(4, ":X: Comm join time is %08x %08x\n", PRINTQUAD(FileTime)); CommInsertDataElement(CommPkt, COMM_REPLICA_VERSION_GUID, &Replica->ReplicaVersionGuid, sizeof(GUID)); // // Insert the list of Guids for compression algorithms that we understand. // GTabLockTable(CompressionTable); Key = NULL; while (Entry = GTabNextEntryNoLock(CompressionTable, &Key)) { CommInsertDataElement(CommPkt, COMM_COMPRESSION_GUID, Entry->Key1, 0); } GTabUnLockTable(CompressionTable); } if (Cmd) { CommInsertDataElement(CommPkt, COMM_BLOCK, RsBlock(Cmd), (ULONG)RsBlockSize(Cmd)); CommInsertDataElement(CommPkt, COMM_BLOCK_SIZE, &RsBlockSize(Cmd), 0); CommInsertDataElement(CommPkt, COMM_FILE_SIZE, &RsFileSize(Cmd).QuadPart, 0); CommInsertDataElement(CommPkt, COMM_FILE_OFFSET, &RsFileOffset(Cmd).QuadPart, 0); CommInsertDataElement(CommPkt, COMM_GVSN, RsGVsn(Cmd), sizeof(GVSN)); CommInsertDataElement(CommPkt, COMM_CO_GUID, RsCoGuid(Cmd), sizeof(GUID)); CommInsertDataElement(CommPkt, COMM_CO_SEQUENCE_NUMBER, &RsCoSn(Cmd), 0); CommInsertDataElement(CommPkt, COMM_MD5_DIGEST, RsMd5Digest(Cmd), MD5DIGESTLEN); } // // Change Order Command // if (Coc) { CommInsertDataElement(CommPkt, COMM_REMOTE_CO, Coc, 0); // // Warning: See comment in schema.h on CHANGE_ORDER_RECORD_EXTENSION for // down level conversion info if Coc->Extension ever changes. // CommInsertDataElement(CommPkt, COMM_CO_EXT_WIN2K, Coc->Extension, sizeof(CO_RECORD_EXTENSION_WIN2K)); // // For post win2k level members the CO extension info should be sent // as follows since the length comes from the first dword of the data. // //CommInsertDataElement(CommPkt, COMM_CO_EXTENSION_2, Coc->Extension, 0); } // // Terminate the packet with EOP Ulong. // CommPackULong(CommPkt, COMM_EOP, COMM_NULL_DATA); return CommPkt; } PCOMMAND_PACKET CommPktToCmd( IN PCOMM_PACKET CommPkt ) /*++ Routine Description: Unpack the data in a Comm packet and store it into a command struct. Arguments: CommPkt Return Value: Address of a command packet or NULL if unpack failed. --*/ { #undef DEBSUB #define DEBSUB "CommPktToCmd:" GUID *pTempGuid; PCOMMAND_PACKET Cmd = NULL; ULONG BlobSize; PVOID Blob; ULONG CommTypeSize; COMM_TYPE CommType; ULONG DataSize; ULONG DecodeType; ULONG NativeOffset; PUCHAR DataDest; ULONG TempUlong; BOOL b; GNAME GName; PCHAR CommTag; PUCHAR CommData; PGEN_TABLE GTable; // // Create the command packet // Cmd = FrsAllocCommand(&ReplicaCmdServer.Queue, CMD_UNKNOWN); FrsSetCompletionRoutine(Cmd, RcsCmdPktCompletionRoutine, NULL); // // Scan the comm packet from the beginning // CommPkt->UpkLen = 0; b = TRUE; while (CommGetNextElement(CommPkt, &CommType, &CommTypeSize) && CommType != COMM_EOP) { // // Uplevel members could send us comm packet data elements we don't handle. // if ((CommType >= COMM_MAX) || (CommType == COMM_NONE)) { DPRINT2(0, "++ WARN - Skipping invalid comm packet element type. CommType = %d, From %ws\n", CommType, RsFrom(Cmd) ? RsFrom(Cmd)->Name : L""); CommPkt->UpkLen += CommTypeSize; b = !(CommPkt->UpkLen > CommPkt->PktLen || CommTypeSize > CommPkt->PktLen); goto NEXT_ELEMENT; } // // Table index MUST match table CommType Field or table is fouled up. // FRS_ASSERT(CommType == CommPacketTable[CommType].CommType); DataSize = CommPacketTable[CommType].DataSize; if ((DataSize != COMM_SZ_NULL) && (CommTypeSize != DataSize)) { DPRINT3(0, "++ WARN - Invalid comm packet size. CommType = %d, DataSize = %d, From %ws\n", CommType, CommTypeSize, RsFrom(Cmd) ? RsFrom(Cmd)->Name : L""); goto CLEANUP_ON_ERROR; } // // Calc the data offset in the Cmd struct to store the data. // NativeOffset = CommPacketTable[CommType].NativeOffset; if (NativeOffset == RsOffsetSkip) { CommPkt->UpkLen += CommTypeSize; b = !(CommPkt->UpkLen > CommPkt->PktLen || CommTypeSize > CommPkt->PktLen); goto NEXT_ELEMENT; } DataDest = (PUCHAR) Cmd + NativeOffset; // // Decode the data element and store it in Cmd at the NativeOffset. // DecodeType = CommPacketTable[CommType].DecodeType; CommTag = CommPacketTable[CommType].CommTag; //DPRINT6(5, ":SR: CommType: %s, Size: %d, Cmd offset: %d, data dest: %08x, Pkt->UpkLen = %d, Pkt->PktLen = %d\n", // CommTag, CommTypeSize, NativeOffset, // DataDest, CommPkt->UpkLen, CommPkt->PktLen); switch (DecodeType) { case COMM_DECODE_ULONG: b = CommFetchMemory(CommPkt, DataDest, sizeof(ULONG)); DPRINT2(5, ":SR: rcv Dec_long: %s data: %d\n", CommTag, *(PULONG)DataDest); break; case COMM_DECODE_ULONG_TO_USHORT: b = CommFetchMemory(CommPkt, (PUCHAR)&TempUlong, sizeof(ULONG)); * ((PUSHORT) DataDest) = (USHORT)TempUlong; DPRINT2(5, ":SR: rcv Dec_ulong_to_ushort: %s data: %d\n", CommTag, TempUlong); break; case COMM_DECODE_GNAME: *(PVOID *)DataDest = FrsFreeGName(*(PVOID *)DataDest); b = CommUnpackGName(CommPkt, (PGNAME *) DataDest); GName.Guid = (*(PGNAME *)DataDest)->Guid; GName.Name = (*(PGNAME *)DataDest)->Name; DPRINT2(5, ":SR: rcv Dec_Gname: %s name: %ws\n", CommTag, GName.Name); break; case COMM_DECODE_BLOB: case COMM_DECODE_VAR_LEN_BLOB: *(PVOID *)DataDest = FrsFree(*(PVOID *)DataDest); b = CommUnpackBlob(CommPkt, &BlobSize, (PVOID *) DataDest); DPRINT1(5, ":SR: rcv Dec_blob: data: %08x\n", *(PULONG)DataDest); break; case COMM_DECODE_ULONGLONG: b = CommFetchMemory(CommPkt, DataDest, sizeof(ULONGLONG)); DPRINT2(5, ":SR: rcv Dec_long_long: %s data: %08x %08x\n", CommTag, PRINTQUAD(*(PULONGLONG)DataDest)); break; // // Version Vector data gets unpacked and inserted into Table. // case COMM_DECODE_VVECTOR: GTable = *(PGEN_TABLE *)(DataDest); if (GTable == NULL) { GTable = GTabAllocTable(); *(PGEN_TABLE *)(DataDest) = GTable; } b = CommUnpackBlob(CommPkt, &BlobSize, &Blob); DPRINT2(5, ":SR: rcv Dec_VV: %s bloblen: %d\n", CommTag, BlobSize); if (b) { VVInsertOutbound(GTable, Blob); } break; // // Compression Guid data gets unpacked and inserted into table. // case COMM_DECODE_GUID: if (CommType == COMM_COMPRESSION_GUID) { GTable = *(PGEN_TABLE *)(DataDest); if (GTable == NULL) { GTable = GTabAllocTable(); *(PGEN_TABLE *)(DataDest) = GTable; } pTempGuid = FrsAlloc(sizeof(GUID)); b = CommFetchMemory(CommPkt, (PUCHAR)pTempGuid, sizeof(GUID)); DPRINT2(5, ":SR: rcv Comp_Guid: %s bloblen: %d\n", CommTag, BlobSize); if (b) { GTabInsertEntry(GTable, NULL, pTempGuid, NULL); } } else { // // Else the guid gets stashed in the data dest. // b = CommFetchMemory(CommPkt, DataDest, sizeof(GUID)); DPRINT1(5, ":SR: rcv Guid: %s \n", CommTag); } break; // // The CO contains four pointers occupying 16 bytes on 32 bit architectures // and 32 bytes on 64 bit architectures (PART2). When the CO is sent // in a comm packet the contents of these pointers are irrelevant so in // comm packets these ptrs are always sent as 16 bytes of zeros, // regardless of architecture. // Note - In 32 bit Win2k this was sent as a BLOB so it matches BLOB format. // case COMM_DECODE_REMOTE_CO: *(PVOID *)DataDest = FrsFree(*(PVOID *)DataDest); // // Unpack the length of the CO and then unpack the CO data. // b = CommFetchMemory(CommPkt, (PUCHAR)&BlobSize, sizeof(ULONG)); if (!b || (BlobSize == 0)) { break; } CommData = FrsAlloc(sizeof(CHANGE_ORDER_COMMAND)); CommFetchMemory(CommPkt, (PUCHAR)CommData, sizeof(CHANGE_ORDER_COMMAND)); //CommFetchMemory(CommPkt, ((PUCHAR)CommData)+CO_PART1_OFFSET, CO_PART1_SIZE); //CommFetchMemory(CommPkt, ((PUCHAR)CommData)+CO_PART2_OFFSET, CO_PART2_SIZE); //CommFetchMemory(CommPkt, ((PUCHAR)CommData)+CO_PART3_OFFSET, CO_PART3_SIZE); DPRINT2(4, ":SR: rcv remote_co: type: %s, len: %d\n", CommTag, BlobSize); *(PVOID *) DataDest = CommData; break; default: // // Decode data type from an uplevel client. Although we should // not really get here because uplevel clients should only be using // new decode data types with new decode data elements which got // filtered out above. // DPRINT3(0, "++ WARN - Skipping invalid comm packet decode data type. CommType = %d, DecodeType = %d, From %ws\n", CommType, DecodeType, RsFrom(Cmd) ? RsFrom(Cmd)->Name : L""); CommPkt->UpkLen += CommTypeSize; b = !(CommPkt->UpkLen > CommPkt->PktLen || CommTypeSize > CommPkt->PktLen); break; } NEXT_ELEMENT: if (!b) { DPRINT4(0, ":SR: PKT ERROR -- CommType = %s, DataSize = %d, CommPkt->UpkLen = %d, CommPkt->PktLen = %d\n", CommTag, CommTypeSize, CommPkt->UpkLen, CommPkt->PktLen); goto CLEANUP_ON_ERROR; } } // // SUCCESS // return Cmd; // // FAILURE // CLEANUP_ON_ERROR: if (Cmd) { FrsCompleteCommand(Cmd, ERROR_OPERATION_ABORTED); } return NULL; }