mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-05 08:35:26 +00:00
9133e20f81
The cast-and-assign pattern for retrieving file descriptors is prone to SIGBUS signals on platforms that don't support unaligned memory access. This patch fixes the problem by copying the received data directly.
326 lines
7.0 KiB
C++
326 lines
7.0 KiB
C++
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
|
/* vim: set ts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "BluetoothSocketMessageWatcher.h"
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <sys/socket.h>
|
|
#include "BluetoothInterface.h"
|
|
#include "nsClassHashtable.h"
|
|
|
|
BEGIN_BLUETOOTH_NAMESPACE
|
|
|
|
//
|
|
// SocketMessageWatcherWrapper
|
|
//
|
|
|
|
/* |SocketMessageWatcherWrapper| wraps SocketMessageWatcher to keep it from
|
|
* being released by hash table's Remove() method.
|
|
*/
|
|
class SocketMessageWatcherWrapper
|
|
{
|
|
public:
|
|
SocketMessageWatcherWrapper(SocketMessageWatcher* aSocketMessageWatcher)
|
|
: mSocketMessageWatcher(aSocketMessageWatcher)
|
|
{
|
|
MOZ_ASSERT(mSocketMessageWatcher);
|
|
}
|
|
|
|
SocketMessageWatcher* GetSocketMessageWatcher()
|
|
{
|
|
return mSocketMessageWatcher;
|
|
}
|
|
|
|
private:
|
|
SocketMessageWatcher* mSocketMessageWatcher;
|
|
};
|
|
|
|
/* |sWatcherHashTable| maps result handlers to corresponding watchers */
|
|
static nsClassHashtable<nsRefPtrHashKey<BluetoothSocketResultHandler>,
|
|
SocketMessageWatcherWrapper>
|
|
sWatcherHashtable;
|
|
|
|
//
|
|
// SocketMessageWatcher
|
|
//
|
|
|
|
SocketMessageWatcher::SocketMessageWatcher(
|
|
int aFd, BluetoothSocketResultHandler* aRes)
|
|
: mFd(aFd)
|
|
, mClientFd(-1)
|
|
, mLen(0)
|
|
, mRes(aRes)
|
|
{
|
|
MOZ_ASSERT(mRes);
|
|
}
|
|
|
|
SocketMessageWatcher::~SocketMessageWatcher()
|
|
{ }
|
|
|
|
void
|
|
SocketMessageWatcher::OnFileCanReadWithoutBlocking(int aFd)
|
|
{
|
|
BluetoothStatus status;
|
|
|
|
switch (mLen) {
|
|
case 0:
|
|
status = RecvMsg1();
|
|
break;
|
|
case MSG1_SIZE:
|
|
status = RecvMsg2();
|
|
break;
|
|
default:
|
|
/* message-size error */
|
|
status = STATUS_FAIL;
|
|
break;
|
|
}
|
|
|
|
if (IsComplete() || status != STATUS_SUCCESS) {
|
|
StopWatching();
|
|
Proceed(status);
|
|
}
|
|
}
|
|
|
|
void
|
|
SocketMessageWatcher::OnFileCanWriteWithoutBlocking(int aFd)
|
|
{ }
|
|
|
|
void
|
|
SocketMessageWatcher::Watch()
|
|
{
|
|
// add this watcher and its result handler to hash table
|
|
sWatcherHashtable.Put(mRes, new SocketMessageWatcherWrapper(this));
|
|
|
|
MessageLoopForIO::current()->WatchFileDescriptor(
|
|
mFd,
|
|
true,
|
|
MessageLoopForIO::WATCH_READ,
|
|
&mWatcher,
|
|
this);
|
|
}
|
|
|
|
void
|
|
SocketMessageWatcher::StopWatching()
|
|
{
|
|
mWatcher.StopWatchingFileDescriptor();
|
|
|
|
// remove this watcher and its result handler from hash table
|
|
sWatcherHashtable.Remove(mRes);
|
|
}
|
|
|
|
bool
|
|
SocketMessageWatcher::IsComplete() const
|
|
{
|
|
return mLen == (MSG1_SIZE + MSG2_SIZE);
|
|
}
|
|
|
|
int
|
|
SocketMessageWatcher::GetFd() const
|
|
{
|
|
return mFd;
|
|
}
|
|
|
|
int32_t
|
|
SocketMessageWatcher::GetChannel1() const
|
|
{
|
|
return ReadInt32(OFF_CHANNEL1);
|
|
}
|
|
|
|
int32_t
|
|
SocketMessageWatcher::GetSize() const
|
|
{
|
|
return ReadInt16(OFF_SIZE);
|
|
}
|
|
|
|
nsString
|
|
SocketMessageWatcher::GetBdAddress() const
|
|
{
|
|
nsString bdAddress;
|
|
ReadBdAddress(OFF_BDADDRESS, bdAddress);
|
|
return bdAddress;
|
|
}
|
|
|
|
int32_t
|
|
SocketMessageWatcher::GetChannel2() const
|
|
{
|
|
return ReadInt32(OFF_CHANNEL2);
|
|
}
|
|
|
|
int32_t
|
|
SocketMessageWatcher::GetConnectionStatus() const
|
|
{
|
|
return ReadInt32(OFF_STATUS);
|
|
}
|
|
|
|
int
|
|
SocketMessageWatcher::GetClientFd() const
|
|
{
|
|
return mClientFd;
|
|
}
|
|
|
|
BluetoothSocketResultHandler*
|
|
SocketMessageWatcher::GetResultHandler() const
|
|
{
|
|
return mRes;
|
|
}
|
|
|
|
BluetoothStatus
|
|
SocketMessageWatcher::RecvMsg1()
|
|
{
|
|
struct iovec iv;
|
|
memset(&iv, 0, sizeof(iv));
|
|
iv.iov_base = mBuf;
|
|
iv.iov_len = MSG1_SIZE;
|
|
|
|
struct msghdr msg;
|
|
memset(&msg, 0, sizeof(msg));
|
|
msg.msg_iov = &iv;
|
|
msg.msg_iovlen = 1;
|
|
|
|
ssize_t res = TEMP_FAILURE_RETRY(recvmsg(mFd, &msg, MSG_NOSIGNAL));
|
|
if (res <= 0) {
|
|
return STATUS_FAIL;
|
|
}
|
|
|
|
mLen += res;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
#define CMSGHDR_CONTAINS_FD(_cmsghdr) \
|
|
( ((_cmsghdr)->cmsg_level == SOL_SOCKET) && \
|
|
((_cmsghdr)->cmsg_type == SCM_RIGHTS) )
|
|
|
|
BluetoothStatus
|
|
SocketMessageWatcher::RecvMsg2()
|
|
{
|
|
struct iovec iv;
|
|
memset(&iv, 0, sizeof(iv));
|
|
iv.iov_base = mBuf + MSG1_SIZE;
|
|
iv.iov_len = MSG2_SIZE;
|
|
|
|
struct msghdr msg;
|
|
struct cmsghdr cmsgbuf[CMSG_SPACE(sizeof(int))];
|
|
memset(&msg, 0, sizeof(msg));
|
|
msg.msg_iov = &iv;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_control = cmsgbuf;
|
|
msg.msg_controllen = sizeof(cmsgbuf);
|
|
|
|
ssize_t res = TEMP_FAILURE_RETRY(recvmsg(mFd, &msg, MSG_NOSIGNAL));
|
|
if (res <= 0) {
|
|
return STATUS_FAIL;
|
|
}
|
|
|
|
mLen += res;
|
|
|
|
if (msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) {
|
|
return STATUS_FAIL;
|
|
}
|
|
|
|
struct cmsghdr *cmsgptr = CMSG_FIRSTHDR(&msg);
|
|
|
|
// Extract client fd from message header
|
|
for (; cmsgptr; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
|
|
if (CMSGHDR_CONTAINS_FD(cmsgptr)) {
|
|
// if multiple file descriptors have been sent, we close
|
|
// all but the final one.
|
|
if (mClientFd != -1) {
|
|
TEMP_FAILURE_RETRY(close(mClientFd));
|
|
}
|
|
// retrieve sent client fd
|
|
memcpy(&mClientFd, CMSG_DATA(cmsgptr), sizeof(mClientFd));
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
int16_t
|
|
SocketMessageWatcher::ReadInt16(unsigned long aOffset) const
|
|
{
|
|
/* little-endian buffer */
|
|
return (static_cast<int16_t>(mBuf[aOffset + 1]) << 8) |
|
|
static_cast<int16_t>(mBuf[aOffset]);
|
|
}
|
|
|
|
int32_t
|
|
SocketMessageWatcher::ReadInt32(unsigned long aOffset) const
|
|
{
|
|
/* little-endian buffer */
|
|
return (static_cast<int32_t>(mBuf[aOffset + 3]) << 24) |
|
|
(static_cast<int32_t>(mBuf[aOffset + 2]) << 16) |
|
|
(static_cast<int32_t>(mBuf[aOffset + 1]) << 8) |
|
|
static_cast<int32_t>(mBuf[aOffset]);
|
|
}
|
|
|
|
void
|
|
SocketMessageWatcher::ReadBdAddress(unsigned long aOffset,
|
|
nsAString& aBdAddress) const
|
|
{
|
|
char str[BLUETOOTH_ADDRESS_LENGTH + 1];
|
|
|
|
int res = snprintf(str, sizeof(str), "%02x:%02x:%02x:%02x:%02x:%02x",
|
|
static_cast<int>(mBuf[aOffset + 0]),
|
|
static_cast<int>(mBuf[aOffset + 1]),
|
|
static_cast<int>(mBuf[aOffset + 2]),
|
|
static_cast<int>(mBuf[aOffset + 3]),
|
|
static_cast<int>(mBuf[aOffset + 4]),
|
|
static_cast<int>(mBuf[aOffset + 5]));
|
|
if (res < 0) {
|
|
aBdAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
|
|
} else if ((size_t)res >= sizeof(str)) { /* string buffer too small */
|
|
aBdAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
|
|
} else {
|
|
aBdAddress = NS_ConvertUTF8toUTF16(str);
|
|
}
|
|
}
|
|
|
|
//
|
|
// SocketMessageWatcherTask
|
|
//
|
|
|
|
SocketMessageWatcherTask::SocketMessageWatcherTask(
|
|
SocketMessageWatcher* aWatcher)
|
|
: mWatcher(aWatcher)
|
|
{
|
|
MOZ_ASSERT(mWatcher);
|
|
}
|
|
|
|
void
|
|
SocketMessageWatcherTask::Run()
|
|
{
|
|
mWatcher->Watch();
|
|
}
|
|
|
|
//
|
|
// DeleteSocketMessageWatcherTask
|
|
//
|
|
|
|
DeleteSocketMessageWatcherTask::DeleteSocketMessageWatcherTask(
|
|
BluetoothSocketResultHandler* aRes)
|
|
: mRes(aRes)
|
|
{
|
|
MOZ_ASSERT(mRes);
|
|
}
|
|
|
|
void
|
|
DeleteSocketMessageWatcherTask::Run()
|
|
{
|
|
// look up hash table for the watcher corresponding to |mRes|
|
|
SocketMessageWatcherWrapper* wrapper = sWatcherHashtable.Get(mRes);
|
|
if (!wrapper) {
|
|
return;
|
|
}
|
|
|
|
// stop the watcher if it exists
|
|
SocketMessageWatcher* watcher = wrapper->GetSocketMessageWatcher();
|
|
watcher->StopWatching();
|
|
watcher->Proceed(STATUS_DONE);
|
|
}
|
|
|
|
END_BLUETOOTH_NAMESPACE
|