blob: 7a74c9eae41b0ba1749b233d5b06e8d06b94a442 [file] [log] [blame]
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "starboard/shared/posix/socket_internal.h"
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include "starboard/log.h"
#include "starboard/memory.h"
namespace starboard {
namespace shared {
namespace posix {
namespace {
const socklen_t kAddressLengthIpv4 = 4;
const socklen_t kAddressStructLengthIpv4 =
static_cast<socklen_t>(sizeof(struct sockaddr_in));
#if SB_HAS(IPV6)
const socklen_t kAddressLengthIpv6 = 16;
const socklen_t kAddressStructLengthIpv6 =
static_cast<socklen_t>(sizeof(struct sockaddr_in6));
#endif
}
SbSocketError TranslateSocketErrno(int error) {
switch (error) {
case 0:
return kSbSocketOk;
case EINPROGRESS:
case EAGAIN:
#if EWOULDBLOCK != EAGAIN
case EWOULDBLOCK:
#endif
return kSbSocketPending;
}
// Here's where we would be more nuanced if we need to be.
return kSbSocketErrorFailed;
}
bool SetBooleanSocketOption(SbSocket socket,
int level,
int option_code,
const char* option_name,
bool value) {
if (!SbSocketIsValid(socket)) {
errno = EBADF;
return false;
}
SB_DCHECK(socket->socket_fd >= 0);
int on = value ? 1 : 0;
int result =
setsockopt(socket->socket_fd, level, option_code, &on, sizeof(on));
if (result != 0) {
SB_DLOG(ERROR) << "Failed to set " << option_name << " on socket "
<< socket->socket_fd << ", errno = " << errno;
socket->error = TranslateSocketErrno(errno);
return false;
}
socket->error = kSbSocketOk;
return true;
}
bool SetIntegerSocketOption(SbSocket socket,
int level,
int option_code,
const char* option_name,
int value) {
if (!SbSocketIsValid(socket)) {
errno = EBADF;
return false;
}
SB_DCHECK(socket->socket_fd >= 0);
int result =
setsockopt(socket->socket_fd, level, option_code, &value, sizeof(value));
if (result != 0) {
SB_DLOG(ERROR) << "Failed to set " << option_name << " on socket "
<< socket->socket_fd << ", errno = " << errno;
socket->error = TranslateSocketErrno(errno);
return false;
}
socket->error = kSbSocketOk;
return true;
}
bool SockAddr::FromSbSocketAddress(const SbSocketAddress* address) {
if (!address) {
return false;
}
length = sizeof(storage_);
switch (address->type) {
case kSbSocketAddressTypeIpv4: {
struct sockaddr_in* addr = sockaddr_in();
length = kAddressStructLengthIpv4;
SbMemorySet(addr, 0, length);
addr->sin_family = AF_INET;
addr->sin_port = htons(address->port);
SbMemoryCopy(&addr->sin_addr, address->address, kAddressLengthIpv4);
break;
}
#if SB_HAS(IPV6)
case kSbSocketAddressTypeIpv6: {
struct sockaddr_in6* addr6 = sockaddr_in6();
length = kAddressStructLengthIpv6;
SbMemorySet(addr6, 0, length);
addr6->sin6_family = AF_INET6;
addr6->sin6_port = htons(address->port);
SbMemoryCopy(&addr6->sin6_addr, address->address, kAddressLengthIpv6);
break;
}
#endif
default:
SB_NOTREACHED() << "Unrecognized address type: " << address->type;
return false;
}
return true;
}
bool SockAddr::ToSbSocketAddress(SbSocketAddress* out_address) const {
if (!out_address) {
return false;
}
// Check that we have been properly initialized.
#if SB_HAS(IPV6)
SB_DCHECK(length == kAddressStructLengthIpv4 ||
length == kAddressStructLengthIpv6);
#else
SB_DCHECK(length == kAddressStructLengthIpv4);
#endif
if (family() == AF_INET) {
const struct sockaddr_in* addr = sockaddr_in();
if (length < kAddressStructLengthIpv4) {
SB_NOTREACHED() << "Insufficient INET size: " << length;
return false;
}
SbMemoryCopy(out_address->address, &addr->sin_addr, kAddressLengthIpv4);
out_address->port = ntohs(addr->sin_port);
out_address->type = kSbSocketAddressTypeIpv4;
return true;
}
#if SB_HAS(IPV6)
if (family() == AF_INET6) {
const struct sockaddr_in6* addr6 = sockaddr_in6();
if (length < kAddressStructLengthIpv6) {
SB_NOTREACHED() << "Insufficient INET6 size: " << length;
return false;
}
SbMemoryCopy(out_address->address, &addr6->sin6_addr, kAddressLengthIpv6);
out_address->port = ntohs(addr6->sin6_port);
out_address->type = kSbSocketAddressTypeIpv6;
return true;
}
#endif
SB_NOTREACHED() << "Unrecognized address family: " << family();
return false;
}
bool SockAddr::FromSockaddr(const struct sockaddr* sock_addr) {
if (!sock_addr) {
return false;
}
int family = sock_addr->sa_family;
if (family == AF_INET) {
const struct sockaddr_in* addr =
reinterpret_cast<const struct sockaddr_in*>(sock_addr);
*sockaddr_in() = *addr;
length = static_cast<socklen_t>(sizeof(*addr));
return true;
#if SB_HAS(IPV6)
} else if (family == AF_INET6) {
const struct sockaddr_in6* addr =
reinterpret_cast<const struct sockaddr_in6*>(sock_addr);
*sockaddr_in6() = *addr;
length = static_cast<socklen_t>(sizeof(*addr));
return true;
#endif
}
SB_DLOG(WARNING) << "Unrecognized address family: " << family;
return false;
}
} // namespace posix
} // namespace shared
} // namespace starboard