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

246 lines
6.5 KiB
C++
Raw Normal View History

2016-11-29 04:06:42 +00:00
/**
******************************************************************************
* 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 "xenia/base/platform.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/xam/xam_module.h"
// #include "xenia/kernel/xnet.h"
#if XE_PLATFORM_WIN32
#include <WinSock2.h>
#elif XE_PLATFORM_LINUX
#error TODO: Proper network includes
#endif
namespace xe {
namespace kernel {
XSocket::XSocket(KernelState* kernel_state)
: XObject(kernel_state, XObject::kTypeSocket) {}
XSocket::XSocket(KernelState* kernel_state, uint64_t native_handle)
: XObject(kernel_state, XObject::kTypeSocket),
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
#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) {
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;
}
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;
int n_name_len = sizeof(sockaddr);
SOCKET ret = accept(native_handle_, &n_sockaddr, &n_name_len);
if (ret == INVALID_SOCKET) {
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::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;
int 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;
memset(&from->sin_zero, 0, 8);
}
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_un.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