261 lines
6.8 KiB
C++
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
|