diff --git a/src/xenia/core/socket.h b/src/xenia/core/socket.h index c19b82308..f1052f4cd 100644 --- a/src/xenia/core/socket.h +++ b/src/xenia/core/socket.h @@ -14,7 +14,7 @@ #include -typedef int socket_t; +typedef intptr_t socket_t; #define XE_INVALID_SOCKET -1 @@ -29,6 +29,7 @@ void xe_socket_set_nodelay(socket_t socket, bool value); void xe_socket_set_nonblock(socket_t socket, bool value); int xe_socket_bind(socket_t socket, uint32_t port); +int xe_socket_bind_loopback(socket_t socket); int xe_socket_listen(socket_t socket); typedef struct { diff --git a/src/xenia/core/socket_posix.cc b/src/xenia/core/socket_posix.cc index 5ba16271a..1d9acbf5d 100644 --- a/src/xenia/core/socket_posix.cc +++ b/src/xenia/core/socket_posix.cc @@ -77,6 +77,18 @@ int xe_socket_bind(socket_t socket, uint32_t port) { return 0; } +int xe_socket_bind_loopback(socket_t socket) { + struct sockaddr_in socket_addr; + socket_addr.sin_family = AF_INET; + socket_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + socket_addr.sin_port = htons(0); + int r = bind(socket, (struct sockaddr*)&socket_addr, sizeof(socket_addr)); + if (r == SOCKET_ERROR) { + return 1; + } + return 0; +} + int xe_socket_listen(socket_t socket) { int r = listen(socket, 5); if (r < 0) { @@ -88,8 +100,8 @@ int xe_socket_listen(socket_t socket) { int xe_socket_accept(socket_t socket, xe_socket_connection_t* out_client_info) { struct sockaddr_in client_addr; socklen_t client_count = sizeof(client_addr); - int client_socket_id = accept(socket, (struct sockaddr*)&client_addr, - &client_count); + socket_t client_socket_id = accept( + socket, (struct sockaddr*)&client_addr, &client_count); if (client_socket_id < 0) { return 1; } diff --git a/src/xenia/core/socket_win.cc b/src/xenia/core/socket_win.cc index dec54c8e3..67d4b679a 100644 --- a/src/xenia/core/socket_win.cc +++ b/src/xenia/core/socket_win.cc @@ -10,89 +10,276 @@ #include #include +#include +#include // TODO(benvanik): win32 calls void xe_socket_init() { - // TODO(benvanik): do WSA init + WSADATA wsa_data; + int result = WSAStartup(MAKEWORD(2, 2), &wsa_data); + if (result) { + XELOGE("Winsock failed to initialize: %d", result); + return; + } } socket_t xe_socket_create_tcp() { - return 0; + socket_t socket_result = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (socket_result < 1) { + return XE_INVALID_SOCKET; + } + return socket_result; } void xe_socket_close(socket_t socket) { + shutdown(socket, SD_SEND); + closesocket(socket); } void xe_socket_set_keepalive(socket_t socket, bool value) { + struct tcp_keepalive alive; + alive.onoff = TRUE; + alive.keepalivetime = 7200000; + alive.keepaliveinterval = 6000; + DWORD bytes_returned; + WSAIoctl(socket, SIO_KEEPALIVE_VALS, &alive, sizeof(alive), + NULL, 0, &bytes_returned, NULL, NULL); } void xe_socket_set_reuseaddr(socket_t socket, bool value) { + int opt_value = value ? 1 : 0; + setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, + (const char*)&opt_value, sizeof(opt_value)); } void xe_socket_set_nodelay(socket_t socket, bool value) { + int opt_value = value ? 1 : 0; + setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, + (const char*)&opt_value, sizeof(opt_value)); } void xe_socket_set_nonblock(socket_t socket, bool value) { + u_long mode = value ? 1 : 0; + ioctlsocket(socket, FIONBIO, &mode); } int xe_socket_bind(socket_t socket, uint32_t port) { + struct sockaddr_in socket_addr; + socket_addr.sin_family = AF_INET; + socket_addr.sin_addr.s_addr = htonl(INADDR_ANY); + socket_addr.sin_port = htons(port); + int r = bind(socket, (struct sockaddr*)&socket_addr, sizeof(socket_addr)); + if (r == SOCKET_ERROR) { + return 1; + } + return 0; +} + +int xe_socket_bind_loopback(socket_t socket) { + struct sockaddr_in socket_addr; + socket_addr.sin_family = AF_INET; + socket_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + socket_addr.sin_port = htons(0); + int r = bind(socket, (struct sockaddr*)&socket_addr, sizeof(socket_addr)); + if (r == SOCKET_ERROR) { + return 1; + } return 0; } int xe_socket_listen(socket_t socket) { + int r = listen(socket, 5); + if (r == SOCKET_ERROR) { + return 1; + } return 0; } int xe_socket_accept(socket_t socket, xe_socket_connection_t* out_client_info) { + struct sockaddr_in client_addr; + int client_count = sizeof(client_addr); + socket_t client_socket_id = accept( + socket, (struct sockaddr*)&client_addr, &client_count); + if (client_socket_id == INVALID_SOCKET) { + return 1; + } + + out_client_info->socket = client_socket_id; + + int client_ip = client_addr.sin_addr.s_addr; + inet_ntop(AF_INET, &client_ip, + out_client_info->addr, XECOUNT(out_client_info->addr)); + return 0; } int64_t xe_socket_send(socket_t socket, const uint8_t* data, size_t length, int flags, int* out_error_code) { - return 0; + int result = send(socket, (const char*)data, (int)length, flags); + int error_code = WSAGetLastError(); + if (error_code == WSAEWOULDBLOCK) { + *out_error_code = EWOULDBLOCK; + } else { + *out_error_code = error_code; + } + return result; } int64_t xe_socket_recv(socket_t socket, uint8_t* data, size_t length, int flags, int* out_error_code) { - return 0; + int result = recv(socket, (char*)data, (int)length, flags); + int error_code = WSAGetLastError(); + if (error_code == WSAEWOULDBLOCK) { + *out_error_code = EWOULDBLOCK; + } else { + *out_error_code = error_code; + } + return result; } struct xe_socket_loop { socket_t socket; + + socket_t notify_rd_id; + socket_t notify_wr_id; + + WSAPOLLFD events[2]; + + bool pending_queued_write; + bool pending_recv; + bool pending_send; }; +namespace { +int Win32SocketPair(socket_t sockets[2]) { + sockets[0] = sockets[1] = INVALID_SOCKET; + + int r; + + socket_t listener = xe_socket_create_tcp(); + xe_socket_set_reuseaddr(listener, true); + r = xe_socket_bind_loopback(listener); + r = xe_socket_listen(listener); + + sockaddr listener_name; + int listener_name_len = sizeof(listener_name); + r = getsockname(listener, &listener_name, &listener_name_len); + + socket_t client = xe_socket_create_tcp(); + r = connect(client, &listener_name, listener_name_len); + + socket_t server = accept(listener, &listener_name, &listener_name_len); + + sockaddr client_name; + int client_name_len = sizeof(client_name); + r = getsockname(client, &client_name, &client_name_len); + const char *pc = (const char*)&client_name; + const char *pn = (const char*)&listener_name; + for (size_t n = 0; n < sizeof(client_name); n++) { + if (pc[n] != pn[n]) { + closesocket(listener); + return 1; + } + } + + xe_socket_set_nonblock(client, true); + + sockets[0] = client; + sockets[1] = server; + + closesocket(listener); + return 0; +} +} + xe_socket_loop_t* xe_socket_loop_create(socket_t socket) { xe_socket_loop_t* loop = (xe_socket_loop_t*)xe_calloc( sizeof(xe_socket_loop_t)); loop->socket = socket; + socket_t notify_ids[2] = { 0, 0 }; + for (int retry = 0; retry < 5; retry++) { + if (!Win32SocketPair(notify_ids)) { + break; + } + } + if (!notify_ids[0]) { + xe_free(loop); + return NULL; + } + loop->notify_rd_id = notify_ids[0]; + loop->notify_wr_id = notify_ids[1]; + + loop->events[0].fd = socket; + loop->events[0].events = POLLIN; + loop->events[1].fd = loop->notify_rd_id; + loop->events[1].events = POLLIN; + return loop; } void xe_socket_loop_destroy(xe_socket_loop_t* loop) { + closesocket(loop->notify_rd_id); + closesocket(loop->notify_wr_id); xe_free(loop); } int xe_socket_loop_poll(xe_socket_loop_t* loop, bool check_read, bool check_write) { + // Prep events object. + if (check_read) { + loop->events[0].events |= POLLIN; + } + if (check_write) { + loop->events[0].events |= POLLOUT; + } + + // Poll. + int r = WSAPoll(loop->events, XECOUNT(loop->events), -1); + if (r == -1) { + return 1; + } + + // If we failed, die. + if (loop->events[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { + return 2; + } + + // Check queued write. + loop->pending_queued_write = loop->events[1].revents != 0; + if (loop->pending_queued_write) { + char dummy; + XEIGNORE(recv(loop->notify_rd_id, &dummy, 1, 0)); + } + loop->events[1].revents = 0; + loop->events[1].events = POLLIN; + + // Check send/recv. + loop->pending_recv = (loop->events[0].revents & POLLIN) != 0; + loop->pending_send = (loop->events[0].revents & POLLOUT) != 0; + loop->events[0].revents = 0; + loop->events[0].events = 0; + return 0; } void xe_socket_loop_set_queued_write(xe_socket_loop_t* loop) { + char b = (char)0xFF; + send(loop->notify_wr_id, &b, 1, 0); } bool xe_socket_loop_check_queued_write(xe_socket_loop_t* loop) { - return false; + return loop->pending_queued_write; } bool xe_socket_loop_check_socket_recv(xe_socket_loop_t* loop) { - return false; + return loop->pending_recv; } bool xe_socket_loop_check_socket_send(xe_socket_loop_t* loop) { - return false; + return loop->pending_send; } diff --git a/src/xenia/debug/protocols/gdb/gdb_client.cc b/src/xenia/debug/protocols/gdb/gdb_client.cc index 87468b1fa..bc42560eb 100644 --- a/src/xenia/debug/protocols/gdb/gdb_client.cc +++ b/src/xenia/debug/protocols/gdb/gdb_client.cc @@ -17,7 +17,7 @@ using namespace xe::debug; using namespace xe::debug::protocols::gdb; #if 0 -GDBClient::GDBClient(DebugServer* debug_server, int socket_id) : +GDBClient::GDBClient(DebugServer* debug_server, socket_t socket_id) : DebugClient(debug_server), thread_(NULL), socket_id_(socket_id) { diff --git a/src/xenia/debug/protocols/gdb/gdb_client.h b/src/xenia/debug/protocols/gdb/gdb_client.h index eefa62b23..45060677f 100644 --- a/src/xenia/debug/protocols/gdb/gdb_client.h +++ b/src/xenia/debug/protocols/gdb/gdb_client.h @@ -26,7 +26,7 @@ namespace gdb { class GDBClient : public DebugClient { public: - GDBClient(DebugServer* debug_server, int socket_id); + GDBClient(DebugServer* debug_server, socket_t socket_id); virtual ~GDBClient(); socket_t socket_id(); diff --git a/src/xenia/debug/protocols/ws/ws_client.cc b/src/xenia/debug/protocols/ws/ws_client.cc index def967e2b..f50be5ef4 100644 --- a/src/xenia/debug/protocols/ws/ws_client.cc +++ b/src/xenia/debug/protocols/ws/ws_client.cc @@ -25,7 +25,7 @@ using namespace xe::debug; using namespace xe::debug::protocols::ws; -WSClient::WSClient(DebugServer* debug_server, int socket_id) : +WSClient::WSClient(DebugServer* debug_server, socket_t socket_id) : DebugClient(debug_server), thread_(NULL), socket_id_(socket_id) { @@ -52,10 +52,6 @@ WSClient::~WSClient() { xe_thread_release(thread_); } -int WSClient::socket_id() { - return socket_id_; -} - int WSClient::Setup() { // Prep the socket. xe_socket_set_keepalive(socket_id_, true); diff --git a/src/xenia/debug/protocols/ws/ws_client.h b/src/xenia/debug/protocols/ws/ws_client.h index 016ad0ae6..0e316d912 100644 --- a/src/xenia/debug/protocols/ws/ws_client.h +++ b/src/xenia/debug/protocols/ws/ws_client.h @@ -29,10 +29,10 @@ namespace ws { class WSClient : public DebugClient { public: - WSClient(DebugServer* debug_server, int socket_id); + WSClient(DebugServer* debug_server, socket_t socket_id); virtual ~WSClient(); - socket_t socket_id(); + socket_t socket_id() const { return socket_id_; } virtual int Setup(); diff --git a/xenia.gyp b/xenia.gyp index db2e89f9e..133913ff9 100644 --- a/xenia.gyp +++ b/xenia.gyp @@ -278,6 +278,7 @@ 'user32', 'ole32', 'wsock32', + 'Ws2_32', 'dxgi', 'd3d11', 'd3dcompiler',