static void handle_secure_message()

in HAP/HAPIPAccessoryServer.c [2227:2503]


static void handle_secure_message(HAPIPSessionDescriptor* session) {
    HAPPrecondition(session);
    HAPPrecondition(session->server);
    HAPAccessoryServer* server = (HAPAccessoryServer*) session->server;
    HAPPrecondition(server->primaryAccessory);
    HAPPrecondition(session->securitySession.type == kHAPIPSecuritySessionType_HAP);
    HAPPrecondition(session->securitySession.isOpen);
    HAPPrecondition(session->securitySession.isSecured || kHAPIPAccessoryServer_SessionSecurityDisabled);
    HAPPrecondition(session->inboundBuffer.data);
    HAPPrecondition(session->inboundBuffer.position <= session->inboundBuffer.limit);
    HAPPrecondition(session->inboundBuffer.limit <= session->inboundBuffer.capacity);
    HAPPrecondition(session->httpReaderPosition <= session->inboundBuffer.position);

    HAPError err;

    // Validate request.
    // Requests use the HAP PDU format.
    // See HomeKit Accessory Protocol Specification R14
    // Section 7.3.3 HAP PDU Format
    if (session->httpContentType != kHAPIPAccessoryServerContentType_Application_OctetStream) {
        HAPLog(&logObject, "Received unexpected Content-Type in /secure-message request.");
        write_msg(&session->outboundBuffer, kHAPIPAccessoryServerResponse_BadRequest);
        return;
    }
    if (!session->httpContentLength.isDefined) {
        HAPLog(&logObject, "Received malformed /secure-message request (no content length).");
        write_msg(&session->outboundBuffer, kHAPIPAccessoryServerResponse_BadRequest);
        return;
    }
    HAPAssert(session->httpContentLength.value <= session->inboundBuffer.position - session->httpReaderPosition);
    uint8_t* requestBytes = (uint8_t*) &session->inboundBuffer.data[session->httpReaderPosition];
    size_t numRequestBytes = session->httpContentLength.value;
    if (numRequestBytes < 5) {
        HAPLog(&logObject, "Received too short /secure-message request.");
        write_msg(&session->outboundBuffer, kHAPIPAccessoryServerResponse_BadRequest);
        return;
    }
    if (requestBytes[0] != ((0 << 7) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0))) {
        HAPLog(&logObject, "Received malformed /secure-message request (control field: 0x%02x).", requestBytes[0]);
        write_msg(&session->outboundBuffer, kHAPIPAccessoryServerResponse_BadRequest);
        return;
    }
    uint8_t opcode = requestBytes[1];
    uint8_t tid = (uint8_t) requestBytes[2];
    uint16_t iid = HAPReadLittleUInt16(&requestBytes[3]);
    HAPTLVReaderRef requestBodyReader;
    if (numRequestBytes <= 5) {
        HAPAssert(numRequestBytes == 5);
        HAPTLVReaderCreate(&requestBodyReader, NULL, 0);
    } else {
        if (numRequestBytes < 7) {
            HAPLog(&logObject, "Received malformed /secure-message request (malformed body length).");
            write_msg(&session->outboundBuffer, kHAPIPAccessoryServerResponse_BadRequest);
            return;
        }
        uint16_t numRequestBodyBytes = HAPReadLittleUInt16(&requestBytes[5]);
        if (numRequestBytes - 7 != numRequestBodyBytes) {
            HAPLog(&logObject, "Received malformed /secure-message request (incorrect body length).");
            write_msg(&session->outboundBuffer, kHAPIPAccessoryServerResponse_BadRequest);
            return;
        }
        HAPTLVReaderCreate(&requestBodyReader, &requestBytes[7], numRequestBodyBytes);
    }

    // Response variables.
    HAPBLEPDUStatus status;
    void* _Nullable responseBodyBytes = NULL;
    size_t numResponseBodyBytes = 0;

    // Validate opcode.
    if (!HAPPDUIsValidOpcode(opcode)) {
        // If an accessory receives a HAP PDU with an opcode that it does not support it shall reject the PDU and
        // respond with a status code Unsupported PDU in its HAP response.
        // See HomeKit Accessory Protocol Specification R14
        // Section 7.3.3.2 HAP Request Format
        HAPLogAccessory(
                &logObject,
                server->primaryAccessory,
                "Rejected /secure-message request with unsupported opcode: 0x%02x.",
                opcode);
        status = kHAPBLEPDUStatus_UnsupportedPDU;
        goto SendResponse;
    }

    // Validate iid.
    // For IP accessories instance ID in the request shall be set to 0.
    // See HomeKit Accessory Protocol Specification R14
    // Section 5.15 Software Authentication Procedure
    if (iid) {
        HAPLogAccessory(
                &logObject,
                server->primaryAccessory,
                "Request's IID [00000000%08X] does not match the addressed IID.",
                iid);
        status = kHAPBLEPDUStatus_InvalidInstanceID;
        goto SendResponse;
    }

#define DestroyRequestBodyAndCreateResponseBodyWriter(responseWriter) \
    do { \
        size_t numBytes = server->ip.storage->scratchBuffer.numBytes; \
        if (numBytes > UINT16_MAX) { \
            /* Maximum for HAP-BLE PDU. */ \
            numBytes = UINT16_MAX; \
        } \
        HAPTLVWriterCreate(responseWriter, server->ip.storage->scratchBuffer.bytes, numBytes); \
    } while (0)

    // Handle request.
    HAPAssert(sizeof opcode == sizeof(HAPPDUOpcode));
    switch ((HAPPDUOpcode) opcode) {
        case kHAPPDUOpcode_ServiceSignatureRead:
        case kHAPPDUOpcode_CharacteristicSignatureRead:
        case kHAPPDUOpcode_CharacteristicConfiguration:
        case kHAPPDUOpcode_ProtocolConfiguration:
        case kHAPPDUOpcode_CharacteristicTimedWrite:
        case kHAPPDUOpcode_CharacteristicExecuteWrite:
        case kHAPPDUOpcode_CharacteristicWrite:
        case kHAPPDUOpcode_CharacteristicRead: {
            HAPLogAccessory(
                    &logObject,
                    server->primaryAccessory,
                    "Rejected /secure-message request with opcode that is not supported by IP: 0x%02x.",
                    opcode);
            status = kHAPBLEPDUStatus_UnsupportedPDU;
        }
            goto SendResponse;
        case kHAPPDUOpcode_Token: {
            // See HomeKit Accessory Protocol Specification R14
            // Section 5.15.1 HAP-Token-Request
            HAPAssert(!iid);
            HAPAssert(session->securitySession.isSecured || kHAPIPAccessoryServer_SessionSecurityDisabled);

            // HAP-Token-Request ok.
            HAPTLVWriterRef writer;
            DestroyRequestBodyAndCreateResponseBodyWriter(&writer);

            // Serialize HAP-Token-Response.
            err = HAPMFiTokenAuthGetTokenResponse(
                    HAPNonnull(session->server),
                    &session->securitySession._.hap,
                    HAPNonnull(server->primaryAccessory),
                    &writer);
            if (err) {
                HAPAssert(err == kHAPError_Unknown || err == kHAPError_InvalidState || err == kHAPError_OutOfResources);
                HAPLogAccessory(
                        &logObject,
                        server->primaryAccessory,
                        "Rejected token request: Request handling failed with error %u.",
                        err);
                status = kHAPBLEPDUStatus_InvalidRequest;
                goto SendResponse;
            }
            HAPTLVWriterGetBuffer(&writer, &responseBodyBytes, &numResponseBodyBytes);
            status = kHAPBLEPDUStatus_Success;
        }
            goto SendResponse;
        case kHAPPDUOpcode_TokenUpdate: {
            // See HomeKit Accessory Protocol Specification R14
            // Section 5.15.3 HAP-Token-Update-Request
            HAPAssert(!iid);
            HAPAssert(session->securitySession.isSecured || kHAPIPAccessoryServer_SessionSecurityDisabled);

            // Handle HAP-Token-Update-Request.
            err = HAPMFiTokenAuthHandleTokenUpdateRequest(
                    HAPNonnull(session->server),
                    &session->securitySession._.hap,
                    HAPNonnull(server->primaryAccessory),
                    &requestBodyReader);
            if (err) {
                HAPAssert(err == kHAPError_Unknown || err == kHAPError_InvalidData);
                HAPLogAccessory(
                        &logObject,
                        server->primaryAccessory,
                        "Rejected token update request: Request handling failed with error %u.",
                        err);
                status = kHAPBLEPDUStatus_InvalidRequest;
                goto SendResponse;
            }

            // Send HAP-Token-Update-Response.
            status = kHAPBLEPDUStatus_Success;
        }
            goto SendResponse;
        case kHAPPDUOpcode_Info: {
            // See HomeKit Accessory Protocol Specification R14
            // Section 5.15.5 HAP-Info-Request
            HAPAssert(!iid);
            HAPAssert(session->securitySession.isSecured || kHAPIPAccessoryServer_SessionSecurityDisabled);

            // HAP-Info-Request ok.
            HAPTLVWriterRef writer;
            DestroyRequestBodyAndCreateResponseBodyWriter(&writer);

            // Serialize HAP-Info-Response.
            err = HAPAccessoryGetInfoResponse(
                    HAPNonnull(session->server),
                    &session->securitySession._.hap,
                    HAPNonnull(server->primaryAccessory),
                    &writer);
            if (err) {
                HAPAssert(err == kHAPError_Unknown || err == kHAPError_OutOfResources);
                HAPLogAccessory(
                        &logObject,
                        server->primaryAccessory,
                        "Rejected info request: Request handling failed with error %u.",
                        err);
                status = kHAPBLEPDUStatus_InvalidRequest;
                goto SendResponse;
            }
            HAPTLVWriterGetBuffer(&writer, &responseBodyBytes, &numResponseBodyBytes);
            status = kHAPBLEPDUStatus_Success;
        }
            goto SendResponse;
    }

#undef DestroyRequestBodyAndCreateResponseBodyWriter

    HAPFatalError();
SendResponse : {
    // Serialize response.
    // Responses use the HAP PDU format.
    // See HomeKit Accessory Protocol Specification R14
    // Section 7.3.3 HAP PDU Format
    size_t mark = session->outboundBuffer.position;
    size_t numResponseBytes = 3;
    if (responseBodyBytes) {
        numResponseBytes += 2;
        numResponseBytes += numResponseBodyBytes;
    }
    HAP_DIAGNOSTIC_IGNORED_ICCARM(Pa084)
    if (numResponseBytes > UINT32_MAX) {
        HAPLog(&logObject, "/secure-message response: Content length exceeds UINT32_MAX.");
        session->outboundBuffer.position = mark;
        write_msg(&session->outboundBuffer, kHAPIPAccessoryServerResponse_OutOfResources);
        return;
    }
    HAP_DIAGNOSTIC_RESTORE_ICCARM(Pa084)
    const char* contentType = "application/octet-stream";
    err = HAPIPByteBufferAppendStringWithFormat(
            &session->outboundBuffer,
            "HTTP/1.1 200 OK\r\n"
            "Content-Type: %s\r\n"
            "Content-Length: %lu\r\n\r\n",
            contentType,
            (unsigned long) numResponseBytes);
    if (err) {
        HAPAssert(err == kHAPError_OutOfResources);
        session->outboundBuffer.position = mark;
        HAPLog(&logObject, "/secure-message response: Invalid configuration (outbound buffer too small for headers).");
        write_msg(&session->outboundBuffer, kHAPIPAccessoryServerResponse_InternalServerError);
        return;
    }
    if (numResponseBytes > session->outboundBuffer.limit - session->outboundBuffer.position) {
        HAPAssert(err == kHAPError_OutOfResources);
        session->outboundBuffer.position = mark;
        HAPLog(&logObject, "/secure-message response: Invalid configuration (outbound buffer too small for body).");
        write_msg(&session->outboundBuffer, kHAPIPAccessoryServerResponse_InternalServerError);
        return;
    }
    session->outboundBuffer.data[session->outboundBuffer.position++] =
            (0 << 7) | (0 << 4) | (0 << 3) | (0 << 2) | (1 << 1) | (0 << 0);
    session->outboundBuffer.data[session->outboundBuffer.position++] = (char) tid;
    session->outboundBuffer.data[session->outboundBuffer.position++] = (char) status;
    if (responseBodyBytes) {
        HAPWriteLittleUInt16(&session->outboundBuffer.data[session->outboundBuffer.position], numResponseBodyBytes);
        session->outboundBuffer.position += 2;

        HAPRawBufferCopyBytes(
                &session->outboundBuffer.data[session->outboundBuffer.position],
                HAPNonnullVoid(responseBodyBytes),
                numResponseBodyBytes);
        session->outboundBuffer.position += numResponseBodyBytes;
    }
    HAPAssert(session->outboundBuffer.limit >= session->outboundBuffer.position);
}
}