Files
HomeKitADK/PAL/POSIX/HAPPlatformTCPStreamManager.c
2019-12-18 05:20:05 -08:00

787 lines
29 KiB
C

// Copyright (c) 2015-2019 The HomeKit ADK Contributors
//
// Licensed under the Apache License, Version 2.0 (the “License”);
// you may not use this file except in compliance with the License.
// See [CONTRIBUTORS.md] for the list of HomeKit ADK project authors.
#include <errno.h>
#include <fcntl.h>
#include <net/if.h>
#include <netdb.h>
#include <netinet/tcp.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include "HAPPlatform+Init.h"
#include "HAPPlatformLog+Init.h"
#include "HAPPlatformTCPStreamManager+Init.h"
static const HAPLogObject logObject = { .subsystem = kHAPPlatform_LogSubsystem, .category = "TCPStreamManager" };
/**
* Sets all fields of a TCP stream listener to their initial values.
*
* @param tcpStreamListener TCP stream listener.
*/
static void InitializeTCPStreamListener(HAPPlatformTCPStreamListener* tcpStreamListener) {
HAPPrecondition(tcpStreamListener);
tcpStreamListener->tcpStreamManager = NULL;
tcpStreamListener->interfaceIndex = 0;
tcpStreamListener->port = 0;
tcpStreamListener->fileDescriptor = -1;
tcpStreamListener->fileHandle = 0;
tcpStreamListener->callback = NULL;
tcpStreamListener->context = NULL;
}
/**
* Sets all fields of a TCP stream to their initial values.
*
* @param tcpStream TCP stream.
*/
static void InitializeTCPStream(HAPPlatformTCPStream* tcpStream) {
HAPPrecondition(tcpStream);
tcpStream->tcpStreamManager = NULL;
tcpStream->fileDescriptor = -1;
tcpStream->fileHandle = 0;
tcpStream->interests.hasBytesAvailable = false;
tcpStream->interests.hasSpaceAvailable = false;
tcpStream->callback = NULL;
tcpStream->context = NULL;
}
HAP_RESULT_USE_CHECK
HAPNetworkPort HAPPlatformTCPStreamManagerGetListenerPort(HAPPlatformTCPStreamManagerRef tcpStreamManager) {
HAPPrecondition(tcpStreamManager);
HAPPrecondition(tcpStreamManager->tcpStreams);
HAPPrecondition(tcpStreamManager->tcpStreamListener.tcpStreamManager);
return tcpStreamManager->tcpStreamListener.port;
}
/**
* Makes a file descriptor nonblocking.
*
* @param fileDescriptor File descriptor.
*
* @return kHAPError_None If successful.
* @return kHAPError_Unknown If the nonblocking flag could not be set.
*/
HAP_RESULT_USE_CHECK
static HAPError SetNonblocking(int fileDescriptor) {
int e = fcntl(fileDescriptor, F_SETFL, O_NONBLOCK);
if (e == -1) {
HAPPlatformLogPOSIXError(
kHAPLogType_Error,
"System call 'fcntl' to set file descriptor flags to 'non-blocking' failed.",
errno,
__func__,
HAP_FILE,
__LINE__);
return kHAPError_Unknown;
}
return kHAPError_None;
}
/**
* Disables coalescing of small segments on a socket.
*
* @param fileDescriptor Socket file descriptor.
*
* @return kHAPError_None If successful.
* @return kHAPError_Unknown If an error occurred while disabling coalescing of small segments.
*/
HAP_RESULT_USE_CHECK
static HAPError SetNodelay(int fileDescriptor) {
int v = 1;
HAPLogBufferDebug(
&logObject, &v, sizeof v, "setsockopt(%d, %d, %d, <buffer>);", fileDescriptor, IPPROTO_TCP, TCP_NODELAY);
int e = setsockopt(fileDescriptor, IPPROTO_TCP, TCP_NODELAY, &v, sizeof v);
if (e != 0) {
int _errno = errno;
HAPAssert(e == -1);
HAPPlatformLogPOSIXError(
kHAPLogType_Error,
"System call 'setsockopt' to set socket options to 'no delay' failed.",
_errno,
__func__,
HAP_FILE,
__LINE__);
return kHAPError_Unknown;
}
return kHAPError_None;
}
void HAPPlatformTCPStreamManagerCreate(
HAPPlatformTCPStreamManagerRef tcpStreamManager,
const HAPPlatformTCPStreamManagerOptions* options) {
HAPPrecondition(tcpStreamManager);
HAPPrecondition(options);
HAPPrecondition(options->maxConcurrentTCPStreams);
HAPRawBufferZero(tcpStreamManager, sizeof *tcpStreamManager);
if (options->interfaceName) {
size_t numInterfaceNameBytes = HAPStringGetNumBytes(HAPNonnull(options->interfaceName));
if ((numInterfaceNameBytes == 0) ||
(numInterfaceNameBytes >= sizeof tcpStreamManager->tcpStreamListenerConfiguration.interfaceName)) {
HAPLogError(&logObject, "Invalid local network interface name.");
HAPFatalError();
}
HAPRawBufferCopyBytes(
tcpStreamManager->tcpStreamListenerConfiguration.interfaceName,
HAPNonnull(options->interfaceName),
numInterfaceNameBytes);
}
tcpStreamManager->tcpStreamListenerConfiguration.port = options->port;
tcpStreamManager->numTCPStreams = 0;
tcpStreamManager->maxTCPStreams = options->maxConcurrentTCPStreams;
HAPLogDebug(&logObject, "Storage configuration: tcpStreamManager = %lu", (unsigned long) sizeof *tcpStreamManager);
HAPLogDebug(
&logObject, "Storage configuration: maxTCPStreams = %lu", (unsigned long) tcpStreamManager->maxTCPStreams);
HAPLogDebug(
&logObject,
"Storage configuration: tcpStreams = %lu",
(unsigned long) tcpStreamManager->maxTCPStreams * sizeof(HAPPlatformTCPStream));
InitializeTCPStreamListener(&tcpStreamManager->tcpStreamListener);
tcpStreamManager->tcpStreams = malloc(tcpStreamManager->maxTCPStreams * sizeof(HAPPlatformTCPStream));
if (!tcpStreamManager->tcpStreams) {
HAPLogError(&logObject, "Allocating new TCP stream failed: out of memory.");
HAPFatalError();
}
for (size_t i = 0; i < tcpStreamManager->maxTCPStreams; i++) {
InitializeTCPStream(&tcpStreamManager->tcpStreams[i]);
}
// Initialize signal handling.
void (*h)(int);
h = signal(SIGPIPE, SIG_IGN);
if (h == SIG_ERR) {
HAPPlatformLogPOSIXError(
kHAPLogType_Error,
"System call 'signal' to ignore signals of type 'SIGPIPE' failed.",
errno,
__func__,
HAP_FILE,
__LINE__);
HAPFatalError();
}
}
void HAPPlatformTCPStreamManagerRelease(HAPPlatformTCPStreamManagerRef tcpStreamManager) {
HAPPrecondition(tcpStreamManager);
HAPPrecondition(tcpStreamManager->tcpStreams);
HAPPlatformFreeSafe(tcpStreamManager->tcpStreams);
tcpStreamManager->tcpStreams = NULL;
}
HAP_RESULT_USE_CHECK
bool HAPPlatformTCPStreamManagerIsListenerOpen(HAPPlatformTCPStreamManagerRef tcpStreamManager) {
HAPPrecondition(tcpStreamManager);
HAPPrecondition(tcpStreamManager->tcpStreams);
return tcpStreamManager->tcpStreamListener.tcpStreamManager != NULL;
}
static void HandleTCPStreamListenerFileHandleCallback(
HAPPlatformFileHandleRef fileHandle,
HAPPlatformFileHandleEvent fileHandleEvents,
void* _Nullable context);
void HAPPlatformTCPStreamManagerOpenListener(
HAPPlatformTCPStreamManagerRef tcpStreamManager,
HAPPlatformTCPStreamListenerCallback callback,
void* _Nullable context) {
HAPPrecondition(tcpStreamManager);
HAPPrecondition(tcpStreamManager->tcpStreams);
HAPPrecondition(callback);
HAPPrecondition(!tcpStreamManager->tcpStreamListener.tcpStreamManager);
HAPPrecondition(tcpStreamManager->tcpStreamListener.interfaceIndex == 0);
HAPPrecondition(tcpStreamManager->tcpStreamListener.port == 0);
HAPPrecondition(tcpStreamManager->tcpStreamListener.fileDescriptor == -1);
HAPPrecondition(!tcpStreamManager->tcpStreamListener.fileHandle);
HAPPrecondition(!tcpStreamManager->tcpStreamListener.callback);
HAPPrecondition(!tcpStreamManager->tcpStreamListener.context);
HAPError err;
int _errno;
int e;
uint32_t interfaceIndex;
if (tcpStreamManager->tcpStreamListenerConfiguration.interfaceName[0]) {
unsigned int i = if_nametoindex(tcpStreamManager->tcpStreamListenerConfiguration.interfaceName);
if ((i == 0) || (i > UINT32_MAX)) {
HAPLogError(&logObject, "Mapping the local network interface name to its corresponding index failed.");
HAPFatalError();
}
interfaceIndex = (uint32_t) i;
} else {
interfaceIndex = 0;
}
HAPNetworkPort port = tcpStreamManager->tcpStreamListenerConfiguration.port;
int fileDescriptor = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (fileDescriptor == -1) {
HAPLogError(&logObject, "Failed to open TCP stream listener socket.");
HAPFatalError();
}
int v = 1;
HAPLogBufferDebug(&logObject, &v, sizeof v, "setsockopt(%d, SOL_SOCKET, SO_REUSEADDR, <buffer>);", fileDescriptor);
e = setsockopt(fileDescriptor, SOL_SOCKET, SO_REUSEADDR, &v, sizeof v);
if (e != 0) {
_errno = errno;
HAPAssert(e == -1);
HAPPlatformLogPOSIXError(
kHAPLogType_Error,
"System call 'setsockopt' with option 'SO_REUSEADDR' on TCP stream listener socket failed.",
_errno,
__func__,
HAP_FILE,
__LINE__);
HAPFatalError();
}
if (interfaceIndex) {
#if defined(SO_BINDTODEVICE)
HAPLogBufferDebug(
&logObject,
tcpStreamManager->tcpStreamListenerConfiguration.interfaceName,
sizeof tcpStreamManager->tcpStreamListenerConfiguration.interfaceName,
"setsockopt(%d, SOL_SOCKET, SO_BINDTODEVICE, <buffer>);",
fileDescriptor);
e = setsockopt(
fileDescriptor,
SOL_SOCKET,
SO_BINDTODEVICE,
tcpStreamManager->tcpStreamListenerConfiguration.interfaceName,
sizeof tcpStreamManager->tcpStreamListenerConfiguration.interfaceName);
if (e != 0) {
_errno = errno;
HAPAssert(e == -1);
HAPPlatformLogPOSIXError(
kHAPLogType_Error,
"System call 'setsockopt' with option 'SO_BINDTODEVICE' on TCP stream listener socket failed.",
_errno,
__func__,
HAP_FILE,
__LINE__);
HAPFatalError();
}
#else
HAPLog(&logObject, "Ignoring local network interface name on which to bind the TCP stream manager.");
interfaceIndex = 0;
#endif
}
HAPLogDebug(&logObject, "TCP stream listener interface index: %u", (unsigned int) interfaceIndex);
struct sockaddr_in6 sin6;
HAPRawBufferZero(&sin6, sizeof sin6);
sin6.sin6_family = AF_INET6;
sin6.sin6_port = htons(port);
sin6.sin6_addr = in6addr_any;
HAPLogBufferDebug(&logObject, (struct sockaddr*) &sin6, sizeof sin6, "bind(%d, <buffer>);", fileDescriptor);
e = bind(fileDescriptor, (struct sockaddr*) &sin6, sizeof sin6);
if (e != 0) {
_errno = errno;
HAPAssert(e == -1);
HAPPlatformLogPOSIXError(
kHAPLogType_Error,
"System call 'bind' on TCP stream listener socket failed.",
_errno,
__func__,
HAP_FILE,
__LINE__);
HAPFatalError();
}
if (!port) {
socklen_t sin6_len = sizeof sin6;
HAPRawBufferZero(&sin6, sizeof sin6);
e = getsockname(fileDescriptor, (struct sockaddr*) &sin6, &sin6_len);
if (e != 0) {
_errno = errno;
HAPAssert(e == -1);
HAPPlatformLogPOSIXError(
kHAPLogType_Error,
"System call 'getsockname' on TCP stream listener socket failed.",
_errno,
__func__,
HAP_FILE,
__LINE__);
HAPFatalError();
}
HAPAssert(sin6.sin6_port);
port = ntohs(sin6.sin6_port);
}
HAPLogDebug(&logObject, "TCP stream listener port: %u.", port);
HAPLogDebug(&logObject, "listen(%d, 64);", fileDescriptor);
e = listen(fileDescriptor, 64);
if (e != 0) {
_errno = errno;
HAPAssert(e == -1);
HAPPlatformLogPOSIXError(
kHAPLogType_Error,
"System call 'listen' on TCP stream listener socket failed.",
_errno,
__func__,
HAP_FILE,
__LINE__);
HAPFatalError();
}
HAPPlatformFileHandleRef fileHandle;
err = HAPPlatformFileHandleRegister(
&fileHandle,
fileDescriptor,
(HAPPlatformFileHandleEvent) {
.isReadyForReading = true, .isReadyForWriting = false, .hasErrorConditionPending = false },
HandleTCPStreamListenerFileHandleCallback,
&tcpStreamManager->tcpStreamListener);
if (err) {
HAPLogError(&logObject, "Failed to register TCP stream listener file handle.");
HAPFatalError();
}
HAPAssert(fileHandle);
tcpStreamManager->tcpStreamListener.tcpStreamManager = tcpStreamManager;
tcpStreamManager->tcpStreamListener.port = port;
tcpStreamManager->tcpStreamListener.interfaceIndex = interfaceIndex;
tcpStreamManager->tcpStreamListener.fileDescriptor = fileDescriptor;
tcpStreamManager->tcpStreamListener.fileHandle = fileHandle;
tcpStreamManager->tcpStreamListener.callback = callback;
tcpStreamManager->tcpStreamListener.context = context;
}
void HAPPlatformTCPStreamManagerCloseListener(HAPPlatformTCPStreamManagerRef tcpStreamManager) {
HAPPrecondition(tcpStreamManager);
HAPPrecondition(tcpStreamManager->tcpStreams);
HAPPrecondition(tcpStreamManager->tcpStreamListener.tcpStreamManager == tcpStreamManager);
HAPPrecondition(tcpStreamManager->tcpStreamListener.fileDescriptor != -1);
HAPPrecondition(tcpStreamManager->tcpStreamListener.fileHandle);
HAPPrecondition(tcpStreamManager->tcpStreamListener.callback);
int e;
HAPPlatformFileHandleDeregister(tcpStreamManager->tcpStreamListener.fileHandle);
HAPLogDebug(&logObject, "shutdown(%d, SHUT_RDWR);", tcpStreamManager->tcpStreamListener.fileDescriptor);
e = shutdown(tcpStreamManager->tcpStreamListener.fileDescriptor, SHUT_RDWR);
if (e != 0) {
int _errno = errno;
HAPAssert(e == -1);
HAPPlatformLogPOSIXError(
kHAPLogType_Debug,
"System call 'shutdown' on TCP stream listener socket failed.",
_errno,
__func__,
HAP_FILE,
__LINE__);
}
HAPLogDebug(&logObject, "close(%d);", tcpStreamManager->tcpStreamListener.fileDescriptor);
e = close(tcpStreamManager->tcpStreamListener.fileDescriptor);
if (e != 0) {
int _errno = errno;
HAPAssert(e == -1);
HAPPlatformLogPOSIXError(
kHAPLogType_Debug,
"System call 'close' on TCP stream listener socket failed.",
_errno,
__func__,
HAP_FILE,
__LINE__);
}
InitializeTCPStreamListener(&tcpStreamManager->tcpStreamListener);
}
static void HandleTCPStreamFileHandleCallback(
HAPPlatformFileHandleRef fileHandle,
HAPPlatformFileHandleEvent fileHandleEvents,
void* _Nullable context);
HAP_RESULT_USE_CHECK
HAPError HAPPlatformTCPStreamManagerAcceptTCPStream(
HAPPlatformTCPStreamManagerRef tcpStreamManager,
HAPPlatformTCPStreamRef* tcpStream_) {
HAPPrecondition(tcpStreamManager);
HAPPrecondition(tcpStreamManager->tcpStreams);
HAPPrecondition(tcpStreamManager->tcpStreamListener.tcpStreamManager == tcpStreamManager);
HAPPrecondition(tcpStreamManager->tcpStreamListener.fileDescriptor != -1);
HAPPrecondition(tcpStreamManager->tcpStreamListener.fileHandle);
HAPPrecondition(tcpStream_);
HAPError err;
if (tcpStreamManager->numTCPStreams == tcpStreamManager->maxTCPStreams) {
HAPLog(&logObject, "Cannot accept more TCP streams.");
*tcpStream_ = (HAPPlatformTCPStreamRef) NULL;
return kHAPError_OutOfResources;
}
HAPAssert(tcpStreamManager->numTCPStreams < tcpStreamManager->maxTCPStreams);
// Find free TCP stream.
size_t i = 0;
while ((i < tcpStreamManager->maxTCPStreams) && (tcpStreamManager->tcpStreams[i].fileDescriptor != -1)) {
i++;
}
HAPAssert(i < tcpStreamManager->maxTCPStreams);
HAPPlatformTCPStream* tcpStream = &tcpStreamManager->tcpStreams[i];
HAPAssert(!tcpStream->tcpStreamManager);
HAPAssert(tcpStream->fileDescriptor == -1);
HAPAssert(!tcpStream->fileHandle);
HAPLogDebug(&logObject, "accept(%d, NULL, NULL);", tcpStreamManager->tcpStreamListener.fileDescriptor);
int fileDescriptor = accept(tcpStreamManager->tcpStreamListener.fileDescriptor, NULL, NULL);
if (fileDescriptor == -1) {
if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR && errno != ECONNABORTED && errno != EPROTO) {
HAPPlatformLogPOSIXError(
kHAPLogType_Error,
"System call 'accept' on TCP stream listener socket failed.",
errno,
__func__,
HAP_FILE,
__LINE__);
*tcpStream_ = (HAPPlatformTCPStreamRef) NULL;
return kHAPError_Unknown;
}
HAPLogDebug(&logObject, "System call 'accept' on TCP stream listener socket is busy.");
*tcpStream_ = (HAPPlatformTCPStreamRef) NULL;
return kHAPError_Busy;
}
// Configure socket.
int e = SetNonblocking(fileDescriptor);
if (e != 0) {
HAPLogError(&logObject, "Failed to configure TCP stream socket as non-blocking.");
HAPFatalError();
}
e = SetNodelay(fileDescriptor);
if (e != 0) {
HAPLogError(&logObject, "Failed to disable Nagle's algorithm for TCP stream socket.");
HAPFatalError();
}
HAPPlatformFileHandleRef fileHandle;
err = HAPPlatformFileHandleRegister(
&fileHandle,
fileDescriptor,
(HAPPlatformFileHandleEvent) {
.isReadyForReading = false, .isReadyForWriting = false, .hasErrorConditionPending = false },
HandleTCPStreamFileHandleCallback,
tcpStream);
if (err) {
HAPLogError(&logObject, "Failed to register TCP stream file handle.");
HAPFatalError();
}
HAPAssert(fileHandle);
tcpStream->tcpStreamManager = tcpStreamManager;
tcpStream->fileDescriptor = fileDescriptor;
tcpStream->fileHandle = fileHandle;
HAPAssert(!tcpStream->interests.hasBytesAvailable);
HAPAssert(!tcpStream->interests.hasSpaceAvailable);
HAPAssert(!tcpStream->callback);
HAPAssert(!tcpStream->context);
*tcpStream_ = (HAPPlatformTCPStreamRef) tcpStream;
tcpStreamManager->numTCPStreams++;
if (tcpStreamManager->maxTCPStreams - tcpStreamManager->numTCPStreams == 0) {
HAPLogInfo(&logObject, "Suspending accepting new TCP streams on TCP stream listener socket.");
HAPPlatformFileHandleUpdateInterests(
tcpStreamManager->tcpStreamListener.fileHandle,
(HAPPlatformFileHandleEvent) {
.isReadyForReading = false, .isReadyForWriting = false, .hasErrorConditionPending = false },
HandleTCPStreamListenerFileHandleCallback,
&tcpStreamManager->tcpStreamListener);
}
return kHAPError_None;
}
void HAPPlatformTCPStreamCloseOutput(
HAPPlatformTCPStreamManagerRef tcpStreamManager,
HAPPlatformTCPStreamRef tcpStream_) {
HAPPrecondition(tcpStreamManager);
HAPPrecondition(tcpStreamManager->tcpStreams);
HAPPrecondition(tcpStream_);
HAPPlatformTCPStream* tcpStream = (HAPPlatformTCPStream*) tcpStream_;
HAPPrecondition(tcpStream->tcpStreamManager == tcpStreamManager);
HAPPrecondition(tcpStream->fileDescriptor != -1);
HAPPrecondition(tcpStream->fileHandle);
HAPLogDebug(&logObject, "shutdown(%d, SHUT_WR);", tcpStream->fileDescriptor);
int e = shutdown(tcpStream->fileDescriptor, SHUT_WR);
if (e != 0) {
int _errno = errno;
HAPAssert(e == -1);
HAPPlatformLogPOSIXError(
kHAPLogType_Error,
"System call 'shutdown' on TCP stream listener socket failed.",
_errno,
__func__,
HAP_FILE,
__LINE__);
}
}
void HAPPlatformTCPStreamClose(HAPPlatformTCPStreamManagerRef tcpStreamManager, HAPPlatformTCPStreamRef tcpStream_) {
HAPPrecondition(tcpStreamManager);
HAPPrecondition(tcpStreamManager->tcpStreams);
HAPPrecondition(tcpStream_);
HAPPlatformTCPStream* tcpStream = (HAPPlatformTCPStream*) tcpStream_;
HAPPrecondition(tcpStream->tcpStreamManager == tcpStreamManager);
HAPPrecondition(tcpStream->fileDescriptor != -1);
HAPPrecondition(tcpStream->fileHandle);
int e;
HAPPlatformFileHandleDeregister(tcpStream->fileHandle);
HAPLogDebug(&logObject, "shutdown(%d, SHUT_RDWR);", tcpStream->fileDescriptor);
e = shutdown(tcpStream->fileDescriptor, SHUT_RDWR);
if (e != 0) {
int _errno = errno;
HAPAssert(e == -1);
HAPPlatformLogPOSIXError(
kHAPLogType_Debug,
"System call 'shutdown' on TCP stream socket failed.",
_errno,
__func__,
HAP_FILE,
__LINE__);
}
HAPLogDebug(&logObject, "close(%d);", tcpStream->fileDescriptor);
e = close(tcpStream->fileDescriptor);
if (e != 0) {
int _errno = errno;
HAPAssert(e == -1);
HAPPlatformLogPOSIXError(
kHAPLogType_Debug,
"System call 'close' on TCP stream socket failed.",
_errno,
__func__,
HAP_FILE,
__LINE__);
}
InitializeTCPStream(tcpStream);
HAPAssert(tcpStreamManager->numTCPStreams <= tcpStreamManager->maxTCPStreams);
HAPAssert(tcpStreamManager->numTCPStreams > 0);
tcpStreamManager->numTCPStreams--;
if (tcpStreamManager->tcpStreamListener.fileDescriptor != -1) {
HAPAssert(tcpStreamManager->tcpStreamListener.tcpStreamManager == tcpStreamManager);
HAPAssert(tcpStreamManager->tcpStreamListener.fileHandle);
if (tcpStreamManager->maxTCPStreams - tcpStreamManager->numTCPStreams == 1) {
HAPLogInfo(&logObject, "Resuming accepting new TCP streams on TCP stream listener socket.");
HAPPlatformFileHandleUpdateInterests(
tcpStreamManager->tcpStreamListener.fileHandle,
(HAPPlatformFileHandleEvent) {
.isReadyForReading = true, .isReadyForWriting = false, .hasErrorConditionPending = false },
HandleTCPStreamListenerFileHandleCallback,
&tcpStreamManager->tcpStreamListener);
}
} else {
HAPAssert(!tcpStreamManager->tcpStreamListener.tcpStreamManager);
HAPAssert(!tcpStreamManager->tcpStreamListener.fileHandle);
}
}
void HAPPlatformTCPStreamUpdateInterests(
HAPPlatformTCPStreamManagerRef tcpStreamManager,
HAPPlatformTCPStreamRef tcpStream_,
HAPPlatformTCPStreamEvent interests,
HAPPlatformTCPStreamEventCallback _Nullable callback,
void* _Nullable context) {
HAPPrecondition(tcpStreamManager);
HAPPrecondition(tcpStreamManager->tcpStreams);
HAPPrecondition(tcpStream_);
HAPPrecondition(!(interests.hasBytesAvailable || interests.hasSpaceAvailable) || callback != NULL);
HAPPlatformTCPStream* tcpStream = (HAPPlatformTCPStream*) tcpStream_;
HAPPrecondition(tcpStream->tcpStreamManager == tcpStreamManager);
HAPPrecondition(tcpStream->fileDescriptor != -1);
HAPPrecondition(tcpStream->fileHandle);
tcpStream->interests.hasBytesAvailable = interests.hasBytesAvailable;
tcpStream->interests.hasSpaceAvailable = interests.hasSpaceAvailable;
tcpStream->callback = callback;
tcpStream->context = context;
HAPPlatformFileHandleUpdateInterests(
tcpStream->fileHandle,
(HAPPlatformFileHandleEvent) { .isReadyForReading = tcpStream->interests.hasBytesAvailable,
.isReadyForWriting = tcpStream->interests.hasSpaceAvailable,
.hasErrorConditionPending = false },
HandleTCPStreamFileHandleCallback,
tcpStream);
}
HAP_RESULT_USE_CHECK
HAPError HAPPlatformTCPStreamRead(
HAPPlatformTCPStreamManagerRef tcpStreamManager,
HAPPlatformTCPStreamRef tcpStream_,
void* bytes,
size_t maxBytes,
size_t* numBytes) {
HAPPrecondition(tcpStreamManager);
HAPPrecondition(tcpStreamManager->tcpStreams);
HAPPrecondition(tcpStream_);
HAPPrecondition(bytes);
HAPPrecondition(numBytes);
HAPPlatformTCPStream* tcpStream = (HAPPlatformTCPStream*) tcpStream_;
HAPPrecondition(tcpStream->tcpStreamManager == tcpStreamManager);
HAPPrecondition(tcpStream->fileDescriptor != -1);
HAPPrecondition(tcpStream->fileHandle);
ssize_t n;
do {
n = recv(tcpStream->fileDescriptor, bytes, maxBytes, 0);
} while ((n == -1) && (errno == EINTR));
if (n == -1) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
HAPPlatformLogPOSIXError(
kHAPLogType_Default,
"System call 'recv' on TCP stream socket failed.",
errno,
__func__,
HAP_FILE,
__LINE__);
*numBytes = 0;
return kHAPError_Unknown;
}
HAPLogDebug(&logObject, "System call 'recv' on TCP stream socket is busy.");
*numBytes = 0;
return kHAPError_Busy;
}
HAPAssert(n >= 0);
HAPAssert((size_t) n <= maxBytes);
*numBytes = (size_t) n;
return kHAPError_None;
}
HAP_RESULT_USE_CHECK
HAPError HAPPlatformTCPStreamWrite(
HAPPlatformTCPStreamManagerRef tcpStreamManager,
HAPPlatformTCPStreamRef tcpStream_,
const void* bytes,
size_t maxBytes,
size_t* numBytes) {
HAPPrecondition(tcpStreamManager);
HAPPrecondition(tcpStreamManager->tcpStreams);
HAPPrecondition(tcpStream_);
HAPPrecondition(bytes);
HAPPrecondition(numBytes);
HAPPlatformTCPStream* tcpStream = (HAPPlatformTCPStream*) tcpStream_;
HAPPrecondition(tcpStream->tcpStreamManager == tcpStreamManager);
HAPPrecondition(tcpStream->fileDescriptor != -1);
HAPPrecondition(tcpStream->fileHandle);
ssize_t n;
do {
n = send(tcpStream->fileDescriptor, bytes, maxBytes, 0);
} while ((n == -1) && (errno == EINTR));
if (n == -1) {
if ((errno != EAGAIN) && (errno != EWOULDBLOCK)) {
HAPPlatformLogPOSIXError(
kHAPLogType_Default,
"System call 'send' on TCP stream socket failed.",
errno,
__func__,
HAP_FILE,
__LINE__);
*numBytes = 0;
return kHAPError_Unknown;
}
HAPLogDebug(&logObject, "System call 'send' on TCP stream socket is busy.");
*numBytes = 0;
return kHAPError_Busy;
}
HAPAssert(n >= 0);
HAPAssert((size_t) n <= maxBytes);
*numBytes = (size_t) n;
return kHAPError_None;
}
static void HandleTCPStreamListenerFileHandleCallback(
HAPPlatformFileHandleRef fileHandle,
HAPPlatformFileHandleEvent fileHandleEvents,
void* _Nullable context) {
HAPAssert(fileHandle);
HAPAssert(context);
HAPPlatformTCPStreamListener* listener = (HAPPlatformTCPStreamListener*) context;
HAPAssert(listener->tcpStreamManager);
HAPAssert(listener->fileDescriptor != -1);
HAPAssert(listener->fileHandle == fileHandle);
HAPAssert(listener->callback);
HAPAssert(fileHandleEvents.isReadyForReading);
listener->callback(listener->tcpStreamManager, listener->context);
}
static void HandleTCPStreamFileHandleCallback(
HAPPlatformFileHandleRef fileHandle,
HAPPlatformFileHandleEvent fileHandleEvents,
void* _Nullable context) {
HAPAssert(fileHandle);
HAPAssert(context);
HAPPlatformTCPStream* tcpStream = (HAPPlatformTCPStream*) context;
HAPAssert(tcpStream->tcpStreamManager);
HAPAssert(tcpStream->fileDescriptor != -1);
HAPAssert(tcpStream->fileHandle == fileHandle);
HAPAssert(fileHandleEvents.isReadyForReading || fileHandleEvents.isReadyForWriting);
HAPPlatformTCPStreamEvent tcpStreamEvents;
tcpStreamEvents.hasBytesAvailable = tcpStream->interests.hasBytesAvailable && fileHandleEvents.isReadyForReading;
tcpStreamEvents.hasSpaceAvailable = tcpStream->interests.hasSpaceAvailable && fileHandleEvents.isReadyForWriting;
if (tcpStreamEvents.hasBytesAvailable || tcpStreamEvents.hasSpaceAvailable) {
HAPAssert(tcpStream->callback);
HAPPlatformTCPStreamRef tcpStream_ = (HAPPlatformTCPStreamRef) tcpStream;
tcpStream->callback(tcpStream->tcpStreamManager, tcpStream_, tcpStreamEvents, tcpStream->context);
}
}