xenia-canary/src/xenia/kernel/xsocket.cc

261 lines
6.8 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "src/xenia/kernel/xsocket.h"
#include <cstring>
#include "xenia/base/platform.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/xam/xam_module.h"
// #include "xenia/kernel/xnet.h"
#ifdef XE_PLATFORM_WIN32
// clang-format off
#include "xenia/base/platform_win.h"
#include <WS2tcpip.h>
#include <WinSock2.h>
// clang-format on
#else
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <unistd.h>
#endif
namespace xe {
namespace kernel {
XSocket::XSocket(KernelState* kernel_state)
: XObject(kernel_state, kObjectType) {}
XSocket::XSocket(KernelState* kernel_state, uint64_t native_handle)
: XObject(kernel_state, kObjectType), native_handle_(native_handle) {}
XSocket::~XSocket() { Close(); }
X_STATUS XSocket::Initialize(AddressFamily af, Type type, Protocol proto) {
af_ = af;
type_ = type;
proto_ = proto;
if (proto == Protocol::IPPROTO_VDP) {
// VDP is a layer on top of UDP.
proto = Protocol::IPPROTO_UDP;
}
native_handle_ = socket(af, type, proto);
if (native_handle_ == -1) {
return X_STATUS_UNSUCCESSFUL;
}
return X_STATUS_SUCCESS;
}
X_STATUS XSocket::Close() {
#if XE_PLATFORM_WIN32
int ret = closesocket(native_handle_);
#elif XE_PLATFORM_LINUX
int ret = close(native_handle_);
#endif
if (ret != 0) {
return X_STATUS_UNSUCCESSFUL;
}
return X_STATUS_SUCCESS;
}
X_STATUS XSocket::SetOption(uint32_t level, uint32_t optname, void* optval_ptr,
uint32_t optlen) {
if (level == 0xFFFF && (optname == 0x5801 || optname == 0x5802)) {
// Disable socket encryption
secure_ = false;
return X_STATUS_SUCCESS;
}
int ret =
setsockopt(native_handle_, level, optname, (char*)optval_ptr, optlen);
if (ret < 0) {
// TODO: WSAGetLastError()
return X_STATUS_UNSUCCESSFUL;
}
// SO_BROADCAST
if (level == 0xFFFF && optname == 0x0020) {
broadcast_socket_ = true;
}
return X_STATUS_SUCCESS;
}
X_STATUS XSocket::IOControl(uint32_t cmd, uint8_t* arg_ptr) {
#ifdef XE_PLATFORM_WIN32
int ret = ioctlsocket(native_handle_, cmd, (u_long*)arg_ptr);
if (ret < 0) {
// TODO: Get last error
return X_STATUS_UNSUCCESSFUL;
}
return X_STATUS_SUCCESS;
#elif XE_PLATFORM_LINUX
return X_STATUS_UNSUCCESSFUL;
#endif
}
X_STATUS XSocket::Connect(N_XSOCKADDR* name, int name_len) {
int ret = connect(native_handle_, (sockaddr*)name, name_len);
if (ret < 0) {
return X_STATUS_UNSUCCESSFUL;
}
return X_STATUS_SUCCESS;
}
X_STATUS XSocket::Bind(N_XSOCKADDR_IN* name, int name_len) {
int ret = bind(native_handle_, (sockaddr*)name, name_len);
if (ret < 0) {
return X_STATUS_UNSUCCESSFUL;
}
bound_ = true;
bound_port_ = name->sin_port;
return X_STATUS_SUCCESS;
}
X_STATUS XSocket::Listen(int backlog) {
int ret = listen(native_handle_, backlog);
if (ret < 0) {
return X_STATUS_UNSUCCESSFUL;
}
return X_STATUS_SUCCESS;
}
object_ref<XSocket> XSocket::Accept(N_XSOCKADDR* name, int* name_len) {
sockaddr n_sockaddr;
socklen_t n_name_len = sizeof(sockaddr);
uintptr_t ret = accept(native_handle_, &n_sockaddr, &n_name_len);
if (ret == -1) {
std::memset(name, 0, *name_len);
*name_len = 0;
return nullptr;
}
std::memcpy(name, &n_sockaddr, n_name_len);
*name_len = n_name_len;
// Create a kernel object to represent the new socket, and copy parameters
// over.
auto socket = object_ref<XSocket>(new XSocket(kernel_state_, ret));
socket->af_ = af_;
socket->type_ = type_;
socket->proto_ = proto_;
return socket;
}
int XSocket::Shutdown(int how) { return shutdown(native_handle_, how); }
int XSocket::Recv(uint8_t* buf, uint32_t buf_len, uint32_t flags) {
return recv(native_handle_, reinterpret_cast<char*>(buf), buf_len, flags);
}
int XSocket::RecvFrom(uint8_t* buf, uint32_t buf_len, uint32_t flags,
N_XSOCKADDR_IN* from, uint32_t* from_len) {
// Pop from secure packets first
// TODO(DrChat): Enable when I commit XNet
/*
{
std::lock_guard<std::mutex> lock(incoming_packet_mutex_);
if (incoming_packets_.size()) {
packet* pkt = (packet*)incoming_packets_.front();
int data_len = pkt->data_len;
std::memcpy(buf, pkt->data, std::min((uint32_t)pkt->data_len, buf_len));
from->sin_family = 2;
from->sin_addr = pkt->src_ip;
from->sin_port = pkt->src_port;
incoming_packets_.pop();
uint8_t* pkt_ui8 = (uint8_t*)pkt;
delete[] pkt_ui8;
return data_len;
}
}
*/
sockaddr_in nfrom;
socklen_t nfromlen = sizeof(sockaddr_in);
int ret = recvfrom(native_handle_, reinterpret_cast<char*>(buf), buf_len,
flags, (sockaddr*)&nfrom, &nfromlen);
if (from) {
from->sin_family = nfrom.sin_family;
from->sin_addr = ntohl(nfrom.sin_addr.s_addr); // BE <- BE
from->sin_port = nfrom.sin_port;
std::memset(from->x_sin_zero, 0, sizeof(from->x_sin_zero));
}
if (from_len) {
*from_len = nfromlen;
}
return ret;
}
int XSocket::Send(const uint8_t* buf, uint32_t buf_len, uint32_t flags) {
return send(native_handle_, reinterpret_cast<const char*>(buf), buf_len,
flags);
}
int XSocket::SendTo(uint8_t* buf, uint32_t buf_len, uint32_t flags,
N_XSOCKADDR_IN* to, uint32_t to_len) {
// Send 2 copies of the packet: One to XNet (for network security) and an
// unencrypted copy for other Xenia hosts.
// TODO(DrChat): Enable when I commit XNet.
/*
auto xam = kernel_state()->GetKernelModule<xam::XamModule>("xam.xex");
auto xnet = xam->xnet();
if (xnet) {
xnet->SendPacket(this, to, buf, buf_len);
}
*/
sockaddr_in nto;
if (to) {
nto.sin_addr.s_addr = to->sin_addr;
nto.sin_family = to->sin_family;
nto.sin_port = to->sin_port;
}
return sendto(native_handle_, reinterpret_cast<char*>(buf), buf_len, flags,
to ? (sockaddr*)&nto : nullptr, to_len);
}
bool XSocket::QueuePacket(uint32_t src_ip, uint16_t src_port,
const uint8_t* buf, size_t len) {
packet* pkt = reinterpret_cast<packet*>(new uint8_t[sizeof(packet) + len]);
pkt->src_ip = src_ip;
pkt->src_port = src_port;
pkt->data_len = (uint16_t)len;
std::memcpy(pkt->data, buf, len);
std::lock_guard<std::mutex> lock(incoming_packet_mutex_);
incoming_packets_.push((uint8_t*)pkt);
// TODO: Limit on number of incoming packets?
return true;
}
} // namespace kernel
} // namespace xe