blob: a5ccb90aa4713eb46b386ffa2235bba086332f9a [file] [log] [blame]
#include "pw_data_link/server_socket.h"
#include <cstdint>
#if defined(_WIN32) && _WIN32
// TODO(cachinchilla): add support for windows.
#error Windows not supported yet!
#else
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#endif // defined(_WIN32) && _WIN32
#include "pw_status/status.h"
namespace pw::data_link {
// Listen for connections on the given port.
// If port is 0, a random unused port is chosen and can be retrieved with
// port().
Status ServerSocket::Listen(uint16_t port) {
socket_fd_ = socket(AF_INET6, SOCK_STREAM, 0);
if (socket_fd_ == kInvalidFd) {
return Status::Unknown();
}
// Allow binding to an address that may still be in use by a closed socket.
constexpr int value = 1;
setsockopt(socket_fd_,
SOL_SOCKET,
SO_REUSEADDR,
reinterpret_cast<const char*>(&value),
sizeof(int));
if (port != 0) {
struct sockaddr_in6 addr = {};
socklen_t addr_len = sizeof(addr);
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(port);
addr.sin6_addr = in6addr_any;
if (bind(socket_fd_, reinterpret_cast<sockaddr*>(&addr), addr_len) < 0) {
return Status::Unknown();
}
}
if (listen(socket_fd_, backlog_) < 0) {
return Status::Unknown();
}
// Find out which port the socket is listening on, and fill in port_.
struct sockaddr_in6 addr = {};
socklen_t addr_len = sizeof(addr);
if (getsockname(socket_fd_, reinterpret_cast<sockaddr*>(&addr), &addr_len) <
0 ||
static_cast<size_t>(addr_len) > sizeof(addr)) {
close(socket_fd_);
return Status::Unknown();
}
port_ = ntohs(addr.sin6_port);
return OkStatus();
}
Result<int> ServerSocket::Accept() {
struct sockaddr_in6 sockaddr_client_ = {};
socklen_t len = sizeof(sockaddr_client_);
const int connection_fd =
accept(socket_fd_, reinterpret_cast<sockaddr*>(&sockaddr_client_), &len);
if (connection_fd == kInvalidFd) {
return Status::Unknown();
}
return connection_fd;
}
// Close the server socket, preventing further connections.
void ServerSocket::Close() {
if (socket_fd_ != kInvalidFd) {
close(socket_fd_);
socket_fd_ = kInvalidFd;
}
}
} // namespace pw::data_link