in HAP/HAPBLEPeripheralManager.c [1120:1437]
static HAPError HandleWriteRequest(
HAPPlatformBLEPeripheralManagerRef blePeripheralManager,
HAPPlatformBLEPeripheralManagerConnectionHandle connectionHandle,
HAPPlatformBLEPeripheralManagerAttributeHandle attributeHandle,
void* bytes,
size_t numBytes,
void* _Nullable context) {
HAPPrecondition(blePeripheralManager);
HAPPrecondition(attributeHandle);
HAPPrecondition(bytes);
HAPPrecondition(numBytes);
HAPPrecondition(context);
HAPAccessoryServerRef* server_ = context;
HAPAccessoryServer* server = (HAPAccessoryServer*) server_;
HAPPrecondition(server->ble.storage->session);
HAPSessionRef* session = server->ble.storage->session;
HAPError err;
HAPLogDebug(&logObject, "%s(0x%04x, 0x%04x)", __func__, connectionHandle, attributeHandle);
HAPPrecondition(server->ble.connection.connected);
HAPPrecondition(connectionHandle == server->ble.connection.connectionHandle);
HAPBLEGATTTableElement* _Nullable gattAttribute = GetGATTAttribute(server_, attributeHandle);
HAPPrecondition(gattAttribute);
const HAPBaseCharacteristic* _Nullable characteristic = gattAttribute->characteristic;
const HAPService* _Nullable service = gattAttribute->service;
const HAPAccessory* _Nullable accessory = gattAttribute->accessory;
if (attributeHandle == gattAttribute->valueHandle) {
HAPAssert(characteristic);
HAPAssert(service);
HAPAssert(accessory);
HAPLogCharacteristicDebug(&logObject, characteristic, service, accessory, "GATT Write value.");
// Get HAP-BLE procedure.
HAPBLEProcedureType procedureType;
void* procedure;
bool isNewProcedure;
err = AttachProcedure(server_, session, gattAttribute, &procedureType, &procedure, &isNewProcedure);
if (err) {
HAPAssert(err == kHAPError_InvalidState || err == kHAPError_OutOfResources);
HAPSessionInvalidate(server_, session, /* terminateLink: */ true);
return err;
}
// Process request.
switch (procedureType) {
case kHAPBLEProcedureType_Full: {
HAPBLEProcedureRef* fullProcedure = procedure;
// Process request.
err = HAPBLEProcedureHandleGATTWrite(fullProcedure, bytes, numBytes);
if (err) {
HAPAssert(
err == kHAPError_InvalidState || err == kHAPError_InvalidData ||
err == kHAPError_OutOfResources);
HAPSessionInvalidate(server_, session, /* terminateLink: */ true);
return err;
}
} break;
case kHAPBLEProcedureType_Fallback: {
HAPBLEFallbackProcedure* fallbackProcedure = procedure;
// When Pair Verify is accessed, all fallback procedures are cancelled.
// Therefore, we do not need to remember whether or not the procedure has been secured at start.
if (HAPSessionIsSecured(session)) {
if (numBytes < CHACHA20_POLY1305_TAG_BYTES) {
HAPLogCharacteristic(
&logObject,
characteristic,
service,
accessory,
"Write to fallback procedure malformed (too short for auth tag).");
HAPSessionInvalidate(server_, session, /* terminateLink: */ true);
return kHAPError_InvalidData;
}
err = HAPSessionDecryptControlMessage(server_, session, bytes, bytes, numBytes);
if (err) {
HAPLogCharacteristic(
&logObject,
characteristic,
service,
accessory,
"First fragment of fallback procedure malformed (decryption failed).");
HAPAssert(err == kHAPError_InvalidState || err == kHAPError_InvalidData);
HAPSessionInvalidate(server_, session, /* terminateLink: */ true);
return err;
}
numBytes -= CHACHA20_POLY1305_TAG_BYTES;
}
if (isNewProcedure) {
HAPLogCharacteristicInfo(
&logObject,
characteristic,
service,
accessory,
"Processing first fragment of fallback procedure.");
uint8_t* data = bytes;
if (numBytes < 5) {
HAPLogCharacteristic(
&logObject,
characteristic,
service,
accessory,
"First fragment of fallback procedure malformed (too short).");
HAPSessionInvalidate(server_, session, /* terminateLink: */ true);
return kHAPError_InvalidData;
}
if (data[0] != ((0 << 7) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0))) {
HAPLogCharacteristic(
&logObject,
characteristic,
service,
accessory,
"First fragment of fallback procedure malformed (control field).");
HAPSessionInvalidate(server_, session, /* terminateLink: */ true);
return kHAPError_InvalidData;
}
// Store minimal information to be able to throw error.
fallbackProcedure->transactionID = data[2];
fallbackProcedure->status = kHAPBLEFallbackProcedureStatus_MaxProcedures;
// Handle simple errors.
uint8_t operation = data[1];
uint16_t iid = HAPReadLittleUInt16(&data[3]);
if (HAPPDUIsValidOpcode(operation)) {
uint16_t expectedIID;
if (HAPBLEPDUOpcodeIsServiceOperation((HAPPDUOpcode) operation)) {
HAPAssert(service->iid <= UINT16_MAX);
expectedIID = (uint16_t) service->iid;
} else {
HAPAssert(characteristic->iid <= UINT16_MAX);
expectedIID = (uint16_t) characteristic->iid;
}
if (iid != expectedIID) {
HAPLogCharacteristic(
&logObject,
characteristic,
service,
accessory,
"Invalid IID %u in fallback procedure.",
iid);
fallbackProcedure->status = kHAPBLEFallbackProcedureStatus_InvalidInstanceID;
// If the accessory receives an invalid (eg., 0) Service instance ID in the
// HAP-Service-Signature-Read-Request, it must respond with a valid
// HAP-Service-Signature-Read-Response with Svc Properties set to 0 and Linked Svc
// (if applicable) set to 0 length.
// See HomeKit Accessory Protocol Specification R14
// Section 7.3.4.13 HAP-Service-Signature-Read-Response
if (operation == kHAPPDUOpcode_ServiceSignatureRead && !iid) {
fallbackProcedure->status =
kHAPBLEFallbackProcedureStatus_ZeroInstanceIDServiceSignatureRead;
}
}
}
// Skip body.
if (numBytes > 5) {
if (numBytes < 7) {
HAPLogCharacteristic(
&logObject,
characteristic,
service,
accessory,
"First fragment of fallback procedure on malformed (body length).");
HAPSessionInvalidate(server_, session, /* terminateLink: */ true);
return kHAPError_InvalidData;
}
fallbackProcedure->remainingBodyBytes = HAPReadLittleUInt16(&data[5]);
// Skip body.
if (fallbackProcedure->remainingBodyBytes < numBytes - 7) {
HAPLogCharacteristic(
&logObject,
characteristic,
service,
accessory,
"First fragment of fallback procedure on malformed (body too long).");
HAPSessionInvalidate(server_, session, /* terminateLink: */ true);
return kHAPError_InvalidData;
}
fallbackProcedure->remainingBodyBytes -= (uint16_t)(numBytes - 7);
} else {
fallbackProcedure->remainingBodyBytes = 0;
}
} else {
HAPLogCharacteristicInfo(
&logObject,
characteristic,
service,
accessory,
"Processing continuation of fallback procedure.");
uint8_t* data = bytes;
if (numBytes < 2) {
HAPLogCharacteristic(
&logObject,
characteristic,
service,
accessory,
"Continuation of fallback procedure malformed (too short).");
HAPSessionInvalidate(server_, session, /* terminateLink: */ true);
return kHAPError_InvalidData;
}
if (data[0] != ((1 << 7) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0))) {
HAPLogCharacteristic(
&logObject,
characteristic,
service,
accessory,
"Continuation of fallback procedure malformed (control field).");
HAPSessionInvalidate(server_, session, /* terminateLink: */ true);
return kHAPError_InvalidData;
}
if (data[1] != fallbackProcedure->transactionID) {
HAPLogCharacteristic(
&logObject,
characteristic,
service,
accessory,
"Continuation of fallback procedure malformed (invalid TID).");
HAPSessionInvalidate(server_, session, /* terminateLink: */ true);
return kHAPError_InvalidData;
}
// Skip body.
if (fallbackProcedure->remainingBodyBytes < numBytes - 2) {
HAPLogCharacteristic(
&logObject,
characteristic,
service,
accessory,
"Continuation of fallback procedure malformed (body too long).");
HAPSessionInvalidate(server_, session, /* terminateLink: */ true);
return kHAPError_InvalidData;
}
fallbackProcedure->remainingBodyBytes -= (uint16_t)(numBytes - 2);
}
// Report response being sent.
HAPBLESessionDidSendGATTResponse(server_, session);
} break;
}
// Continue sending events (if security state changed).
SendPendingEventNotifications(server_);
} else if (attributeHandle == gattAttribute->cccDescriptorHandle) {
HAPAssert(characteristic);
HAPAssert(service);
HAPAssert(accessory);
HAPLogCharacteristicDebug(
&logObject,
characteristic,
service,
accessory,
"GATT Write Client Characteristic Configuration descriptor value.");
// Process request.
if (numBytes != 2) {
HAPLogCharacteristic(
&logObject,
characteristic,
service,
accessory,
"Unexpected Client Characteristic Configuration descriptor length: %lu.",
(unsigned long) numBytes);
return kHAPError_InvalidData;
}
uint16_t v = HAPReadLittleUInt16(bytes);
if (v & ~0x0002) {
HAPLogCharacteristic(
&logObject,
characteristic,
service,
accessory,
"Unexpected Client Characteristic Configuration descriptor value: 0x%04x.",
(unsigned int) v);
return kHAPError_InvalidData;
}
bool eventsEnabled = (v & 0x0002) != 0;
SetNotificationsEnabled(server_, session, gattAttribute, eventsEnabled);
} else {
HAPAssert(attributeHandle == gattAttribute->iidHandle);
HAPAssert(service);
HAPAssert(accessory);
if (characteristic) {
HAPLogCharacteristicDebug(
&logObject,
characteristic,
service,
accessory,
"GATT Write Characteristic Instance ID descriptor value.");
// Process request.
HAPLogCharacteristic(
&logObject,
characteristic,
service,
accessory,
"Rejecting write to Characteristic Instance ID descriptor value.");
return kHAPError_InvalidState;
} else {
HAPLogServiceDebug(&logObject, service, accessory, "GATT Write Service Instance ID descriptor value.");
// Process request.
HAPLogService(&logObject, service, accessory, "Rejecting write to Service Instance ID descriptor value.");
return kHAPError_InvalidState;
}
}
return kHAPError_None;
}