| // 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 |