Help me understand the logic of this function

Results 1 to 5 of 5
  1. #1
    Programmer cyberinferno is offline
    True MemberRank
    Jun 2009 Join Date
    BangaloreLocation
    469Posts

    thumbs up Help me understand the logic of this function


    RaGEZONE Recommends

    RaGEZONE Recommends

    This is the logic how the server reads packets from the client taken from outdated server files released long back for the game Tantra Online. As I am trying to build emulator in C# for a client which is of later version, I would like to understand what exactly this code is doing so that I can code it in C#.

    Code:
    char* CPSock::ReadClientMessage(int* ErrorCode, int* ErrorType)
    {
        *ErrorCode = 0;
    
        // Proc°¡ Recv¸¦ ÃÊ°úÇÑ °æ¿ì. (½É°¢ÇÑ ¿À·ù, ³ªÅ¸³¯¼ö ¾ø� °æ¿ì�Ù)
        if (nProcPosition >= nRecvPosition) {
            nRecvPosition = 0;
            nProcPosition = 0;
            return 0;
        }
        //  Init packet authentication
        if (Init == 0) {
            if (nRecvPosition - nProcPosition < 4)
                return 0;
            int InitCode = *((unsigned int*)(pRecvBuffer + nProcPosition));
            if (InitCode != INITCODE) {
                *ErrorCode = 2;
                *ErrorType = InitCode;
                CloseSocket(); //fors_debug ÌØÊâ�¦Àí
                return 0;
            }
            Init = 1;
            nProcPosition += 4;
        }
        //	Ccheck received message is larger than HEADER
        if (nRecvPosition - nProcPosition < sizeof(HEADER))
            return 0;
        //	Check full message arrived
        int sh = sizeof(HEADER);
        unsigned short Size = *((unsigned short*)(pRecvBuffer + nProcPosition + 4));
        Size = Size + sizeof(HEADER);
        unsigned char CheckSum = *((unsigned char*)(pRecvBuffer + nProcPosition + 3));
        unsigned int SockType = *((unsigned short*)(pRecvBuffer + nProcPosition));
        unsigned int ClentTick = *((unsigned int*)(pRecvBuffer + nProcPosition + 6));
        if (Size > MAX_MESSAGE_SIZE || Size < sizeof(HEADER)) {
            nRecvPosition = 0;
            nProcPosition = 0;
            *ErrorCode = 2;
            *ErrorType = Size;
            return 0;
        }
    
        unsigned short Rest = nRecvPosition - nProcPosition;
        if (Size > Rest)
            return 0;
    
        //	Get message
        char* pMsg = &(pRecvBuffer[nProcPosition]);
        nProcPosition = nProcPosition + Size;
        if (nRecvPosition <= nProcPosition) {
            nRecvPosition = 0;
            nProcPosition = 0;
        }
    
        // Compare check_sum in packet
        int KeywordFlag = oldRecvChecksum % 2;
        unsigned char Sum = 0;
        int pos = ucRecvSeq;
        for (int i = sizeof(HEADER); i < Size; i++, pos++) {
            int rst = pos % 256;
            unsigned char Trans = pKeyWord[rst][KeywordFlag];
            int mod = i & 0x3;
            if (mod == 0)
                pMsg[i] = pMsg[i] - (Trans << 2);
            if (mod == 1)
                pMsg[i] = pMsg[i] + (Trans >> 1);
            if (mod == 2)
                pMsg[i] = pMsg[i] - (Trans << 1);
            if (mod == 3)
                pMsg[i] = pMsg[i] + (Trans >> 2);
            Sum += pMsg[i];
        }
    
        ucRecvSeq++;
        oldRecvChecksum = CheckSum;
    
        // return packet, even check_sum not match
        if (Sum != CheckSum) {
            *ErrorCode = 1;
            *ErrorType = Size;
            return pMsg;
        }
        return pMsg;
    }
    Thanks in advance!
    Have a look at my simple Discord bot https://github.com/cyberinferno/discord-chum


  2. #2
    Member Nothilvien is online now
    MemberRank
    Nov 2007 Join Date
    39Posts

    Re: Help me understand the logic of this function

    int nProcPosition = Processes Position
    int nRecvPosition = Receive Position
    int pRecvBuffer = Buffer containing received data
    HEADER = Expected Size of Header (looks like its an buffer?) size is determined by sizeof()
    const uint INITCODE = Expected first 4bytes as integer of data


    The method 'ReadClientMessage' is probably called in a separate thread and operates on the 'pRecvBuffer', while the sockets will write data and increase 'nRecvPosition' count.
    Code:
        if (nProcPosition >= nRecvPosition) {
            nRecvPosition = 0;
            nProcPosition = 0;
            return 0;
        }
    This checks if we have processed as much data as we received, if so there is nothing to read for us so we return


    Code:
        if (Init == 0) {
            if (nRecvPosition - nProcPosition < 4)
                return 0;
            int InitCode = *((unsigned int*)(pRecvBuffer + nProcPosition));
            if (InitCode != INITCODE) {
                *ErrorCode = 2;
                *ErrorType = InitCode;
                CloseSocket(); //fors_debug ÌØÊâ¦Àí
                return 0;
            }
            Init = 1;
            nProcPosition += 4;
        }
    This checks if the beginning of the data stream, or packet (depends on when 'Init' is reset, not visible in this code) starts with an expected uint defined in 'INITCODE'. Usually this is done to check that the client speaks the same protocol as the server, and reject it quickly if not.


    Only perform this check if we haven't done it ' (Init == 0)' {
    if we have less than 4bytes to read '(nRecvPosition - nProcPosition < 4)' {
    abort;
    }
    read 4bytes from the buffer and cast it to an unsigned int 'int InitCode = *((unsigned int*)(pRecvBuffer + nProcPosition));'
    if the read 'InitCode' is not what we expect 'INITCODE' {
    set some errors, close the connection and abort
    }
    Mark that we succesfully checked the first 4 bytes 'Init = 1;' so we don't need to check again (until init is reset?)
    Advance the number of processed bytes by 4 ' nProcPosition += 4;'
    }


    Code:
      //    Ccheck received message is larger than HEADER
        if (nRecvPosition - nProcPosition < sizeof(HEADER))
            return 0;
    This checks if we have enough data to read the header, if not we abort.

    Code:
        //    Check full message arrived
        int sh = sizeof(HEADER);
        unsigned short Size = *((unsigned short*)(pRecvBuffer + nProcPosition + 4));
        Size = Size + sizeof(HEADER);
        unsigned char CheckSum = *((unsigned char*)(pRecvBuffer + nProcPosition + 3));
        unsigned int SockType = *((unsigned short*)(pRecvBuffer + nProcPosition));
        unsigned int ClentTick = *((unsigned int*)(pRecvBuffer + nProcPosition + 6));
        if (Size > MAX_MESSAGE_SIZE || Size < sizeof(HEADER)) {
            nRecvPosition = 0;
            nProcPosition = 0;
            *ErrorCode = 2;
            *ErrorType = Size;
            return 0;
        }
    'unsigned short Size = *((unsigned short*)(pRecvBuffer + nProcPosition + 4));' This reads 2bytes after (last processed position + 4) from the buffer
    The result tells us how much data we should read (so it should read position 8 and 9 if the buffer started at 0)


    ' Size = Size + sizeof(HEADER);' Add the header size itself.


    ' unsigned char CheckSum = *((unsigned char*)(pRecvBuffer + nProcPosition + 3));' This reads 1byte after (last processed position + 3) from the buffer
    The name donates its used as a checksum (it should read position 6 if the buffer started at 0)


    ' unsigned int SockType = *((unsigned short*)(pRecvBuffer + nProcPosition));' This reads 2byte after (last processed position ) from the buffer
    (it should read position 4 and 5 if the buffer started at 0)


    ' unsigned int ClentTick = *((unsigned int*)(pRecvBuffer + nProcPosition + 6));' This reads 2byte after (last processed position + 6 ) from the buffer
    The name sounds like it synchronises the time (it should read position 9,10,11 and 12 if the buffer started at 0)


    Code:
    [4bytes|uint|InitCode]-[2bytes|ushort|SockType]-[1byte|char|Checksum]-[2bytes|ushort|PacketSize]-[4bytes|uint|ClentTick] <- Structure to read
    {0, 1, 2, 3, ----------4,5----------------------6---------------------7,8------------------------9,10,11,12} <--------------Buffer Position

    ' if (Size > MAX_MESSAGE_SIZE || Size < sizeof(HEADER)) {'
    If the packet size which the packet tells us to read, is bigger than the max message size we abort (because we can not trust the read value from a packet blindly)


    Code:
     unsigned short Rest = nRecvPosition - nProcPosition;
        if (Size > Rest)
            return 0;
    we calculate how much data we still have to read, and if we have more to read than we actually can abort.



    Code:
        //    Get message
        char* pMsg = &(pRecvBuffer[nProcPosition]);
        nProcPosition = nProcPosition + Size;
    we create a pointer to the area inside the buffer where our complete packet resides called 'pMsg'
    we advance our processed position by the size we have just read 'nProcPosition = nProcPosition + Size;'


    Code:
        if (nRecvPosition <= nProcPosition) {
            nRecvPosition = 0;
            nProcPosition = 0;
        }
    if we have caught up with all the data we reset our counters (otherwise there might be another packet in the buffer, that will possibly be read on next calls?)




    Im not to sure of the following it looks like it calculates the checksum of the data we just received, but it also looks like it is modifying the packet?


    Code:
        // Compare check_sum in packet
        int KeywordFlag = oldRecvChecksum % 2;
        unsigned char Sum = 0;
        int pos = ucRecvSeq;
        for (int i = sizeof(HEADER); i < Size; i++, pos++) {
            int rst = pos % 256;
            unsigned char Trans = pKeyWord[rst][KeywordFlag];
            int mod = i & 0x3;
            if (mod == 0)
                pMsg[i] = pMsg[i] - (Trans << 2);
            if (mod == 1)
                pMsg[i] = pMsg[i] + (Trans >> 1);
            if (mod == 2)
                pMsg[i] = pMsg[i] - (Trans << 1);
            if (mod == 3)
                pMsg[i] = pMsg[i] + (Trans >> 2);
            Sum += pMsg[i];
        }
        ucRecvSeq++;
        oldRecvChecksum = CheckSum;


    As you can see most of the code is just reading bytes in a correct order from a buffer and create a packet from it, i.e recovering the send data as packets.

    Note: I don't know c++ so my understanding of the buffer and pointer instructions or data sizes could be off

  3. #3
    Programmer cyberinferno is offline
    True MemberRank
    Jun 2009 Join Date
    BangaloreLocation
    469Posts

    Re: Help me understand the logic of this function

    Thanks for the explanation.
    Actually HEADER is a 12 bytes structure. Something like this
    Code:
    struct HEADER {
        WORD wType;
        WORD wSeq;						
        WORD wPDULength;
        WORD wDummy;
        DWORD dwClientTick;  
    }
    INITCODE is sent by the client only for the 1st time.

    I wanted to know if it was using some standard algorithm inside the checksum loop to manipulate data from the received bytes!
    Last edited by cyberinferno; 1 Week Ago at 05:50 AM.
    Have a look at my simple Discord bot https://github.com/cyberinferno/discord-chum

  4. #4
    Member Nothilvien is online now
    MemberRank
    Nov 2007 Join Date
    39Posts

    Re: Help me understand the logic of this function

    Ah a struct makes more sense :)
    As noted early this code reads and parsed the header (12 bytes)
    (EDIT: The header should only be 8bytes, as the INIT takes 4bytes and is not part of the header)
    Code:
    [4bytes|uint|InitCode]-[2bytes|ushort|SockType]-[1byte|char|Checksum]-[2bytes|ushort|PacketSize]-[4bytes|uint|ClentTick] <- Structure to read
    {0, 1, 2, 3, ----------4,5----------------------6---------------------7,8------------------------9,10,11,12} <--------------Buffer Position
    But only reads all the body and makes no sense of it (only uses it as checksum calculation here). This step happens probably later based on some ID.

    From the code, the algorithm operates only on the data, not the header. (int i = sizeof(HEADER);)
    Also it seems that the checksum is continuously based on the last checksum.
    Code:
    int KeywordFlag = oldRecvChecksum % 2;
    ....
    unsigned char Trans = pKeyWord[rst][KeywordFlag];
    So the only missing part to calculating the checksum is most likly the 'pKeyWord' variable.
    it looks like it is some sort of JaggedArray that acts as a lookup table.

    I would assume that it should be defined or build somewhere, also I would assume you can see how the checksum gets created by looking into the 'Send' part of the code.

    I can't tell if its a well known algo, but since its coded like that and don't use a library I'd guess its custom.
    Last edited by Nothilvien; 1 Week Ago at 06:56 AM.

  5. #5
    Programmer cyberinferno is offline
    True MemberRank
    Jun 2009 Join Date
    BangaloreLocation
    469Posts

    Re: Help me understand the logic of this function

    If you want to take a look at the whole source then here is the link Tantra_DBSRV_TMSRV_source

    TMSRV is actually the source of Zone Server where this code is available in the file named CPSock.cpp!
    Have a look at my simple Discord bot https://github.com/cyberinferno/discord-chum




Advertisement