iSCSI Target Emulation
by Patrick L. Garvan, Shawn McFarland, Manoj Mehta, Mike Ramsay, and Chris Robinson


Listing One

int parse_received_data(
    IN PST_TARGET_CONNECTION lpConnection,
    IN char *charBuf,
    IN ULONG ulDataLength,
    IN OUT ULONG *lpulBufferOffset
)
/*++
Description:
    This routine parses the ulDataLength bytes of data in charBuf 
    into individual iSCSI PDUs. It copies any left over data to the 
    start of charBuf and sets *lpulBufferOffset to the number of 
    left over bytes.
    
Arguments:
    lpConnection points to the connection struct describing the 
    connection with the target.

    charBuf points to the buffer containing the data received 
    from the initiator.

    ulDataLength contains the total number of unprocessed bytes in charBuf.

    *lpulBufferOffset returns with the number of unprocessed bytes in charBuf.

Return Values:
    A return value of 0 indicates success; otherwise there was an error.

--*/
{
    int nStatus = 0;
    int nRet    = 0;

    ULONG ulDataSegmentLength = 0;
    int   cbPadding           = 0;
    ULONG cbPDUlength         = 0;
    ULONG ul                  = 0;

    BOOL bAddedPDU = FALSE;

    char *charTempBuf = charBuf;

    PISCSI_GENERIC_HEADER q = NULL;

    // The lpConnection argument cannot be NULL.
    TargetAssert(NULL != lpConnection);

    // Hold the connection mutex.
    nRet = lock_connection(lpConnection);
    if (0 != nRet) {
        fprintf(stderr, "parse_received_data(): 
                                  Error calling lock_connection()\n");
        _flushall();
        return 1;
    }
    // Loop until we have dealt with as much of the received data as we can.
    while (1) {
        // Check whether we have less than the minimum 48 byte header.
        if (ulDataLength < 48) {
            *lpulBufferOffset = ulDataLength;
           break;
        }
        // We have at least the header.
        q = (PISCSI_GENERIC_HEADER)charTempBuf;
        cbPDUlength = 48;
        // Get the DataSegmentLength;
        UCHAR3toULONG(q->DataSegmentLength, ulDataSegmentLength);
        cbPDUlength += ulDataSegmentLength;

        // Calculate if there are any padding bytes.
        if (0 != ((ulDataSegmentLength + 48) % 4)) {
            cbPadding = 4 - (ulDataSegmentLength + 48) % 4;
        } else {
            cbPadding = 0;
        }
        cbPDUlength += cbPadding;
        // Check whether we have not yet received the entire DataSegment.
        if (ulDataLength < cbPDUlength) {
            *lpulBufferOffset = ulDataLength;
            break;
        }
        // Create a new PDU element and add it to the list.
        nRet = AddPDUtoList(lpConnection, charTempBuf, cbPDUlength);
        if (0 != nRet) {
            nStatus = 1; // There was an error adding a new PDU 
                         //    element to the list.
            goto label_parse_received_data_finished;
        }
        bAddedPDU = TRUE;
        // Check whether we've reached the end of the buffer.
        if (ulDataLength == cbPDUlength) {
            *lpulBufferOffset = 0; // Reset the buffer to empty.
            break;
        }
        // Update our local pointer into the charBufInitiator buffer.
        charTempBuf = charTempBuf + (cbPDUlength);
        // Update the number of bytes left.
        ulDataLength -= cbPDUlength;

    } // while (1)
    // If necessary, copy the left over data to the start of charBufInitiator.
    if ((charTempBuf != charBuf) && (*lpulBufferOffset > 0)) {
        memcpy(charBuf, charTempBuf, *lpulBufferOffset);
    }
    // If we added a new PDU to the list, then signal the connection mutex.
    if (TRUE == bAddedPDU) {
        nRet = signal_connection(lpConnection);
        if (0 != nRet) {
            fprintf(stderr, "parse_received_data(): 
                                  Error calling signal_connection()\n");
            nStatus = 1;
        }
    }
label_parse_received_data_finished:
    // Release the connection mutex.
    nRet = release_connection(lpConnection);
    if (0 != nRet) {
        fprintf(stderr, "parse_received_data(): 
                                  Error calling release_connection()\n");
        nStatus = 1;
    }
    // Return the status.
    return nStatus;
} // parse_received_data()





3


