cen64/device/netapi.c
2019-05-27 22:31:19 +03:00

231 lines
5.4 KiB
C

//
// device/netapi.c: CEN64 device network API.
//
// CEN64: Cycle-Accurate Nintendo 64 Emulator.
// Copyright (C) 2015, Tyler J. Stachecki.
//
// This file is subject to the terms and conditions defined in
// 'LICENSE', which is part of this source code package.
//
#include "common.h"
#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#else
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#endif
#include "device/device.h"
#include "device/netapi.h"
#include "vr4300/interface.h"
// TODO: Really sloppy.
#ifdef __WIN32__
#define close(x) closesocket(x)
#endif
#define NETAPI_DEBUG_MAGIC 0x40544A53U // "@TJS"
#define NETAPI_DEBUG_VERSION 0U
// Static functions.
static int bind_server_socket(int family, int type, const char *service);
static struct addrinfo *getaddrinfo_helper(int family,
int type, const char *host, const char *service);
static int netapi_debug_handle_request(int sfd, struct cen64_device *device,
const struct netapi_debug_request *req, const uint8_t *req_data);
// Creates a socket of (family, type) and binds it portno.
int bind_server_socket(int family, int type, const char *service) {
struct addrinfo *res, *i;
int sfd, ret;
if ((res = getaddrinfo_helper(family, type, NULL, service)) == NULL)
return -1;
// Walk the list; try to bind a socket.
for (i = res; i != NULL; i = i->ai_next) {
if ((sfd = socket(i->ai_family, i->ai_socktype, i->ai_protocol)) < 0)
continue;
// Disallow IPv4 on IPv6.
if (i->ai_family == AF_INET6) {
#ifdef __WIN32__
char on = 1;
#else
int on = 1;
#endif
if (setsockopt(sfd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) != 0) {
close(sfd);
return -1;
}
}
if ((ret = bind(sfd, i->ai_addr, i->ai_addrlen)) != 0) {
close(sfd);
continue;
}
freeaddrinfo(res);
return sfd;
}
freeaddrinfo(res);
return -2;
}
// Gets a list of address information for a given configuration.
struct addrinfo *getaddrinfo_helper(int family,
int type, const char *host, const char *service) {
struct addrinfo hints, *res;
long int portno;
char *endptr;
memset(&hints, 0, sizeof(hints));
hints.ai_family = family;
hints.ai_socktype = type;
// Port or IANA service?
if (service != NULL) {
portno = strtol(service, &endptr, 10);
if (*endptr == '\0' && *service != '\0') {
if (portno < 0 || portno >= 65536)
return NULL;
#ifndef __WIN32__
hints.ai_flags |= AI_NUMERICSERV;
#endif
}
}
// Allow connects to localhost.
if (host == NULL || !strcmp(host, "localhost")) {
hints.ai_flags |= AI_PASSIVE;
host = NULL;
}
return getaddrinfo(host, service, &hints, &res) != 0
? NULL
: res;
}
// Closes a connection.
void netapi_close_connection(int csfd) {
close(csfd);
}
// Attempts to open an IPv4 socket and binds it, listens.
// TODO: Respect the interface/port passed from the client.
int netapi_open_connection(void) {
struct sockaddr address;
socklen_t addrlen;
int sfd, csfd;
if ((sfd = bind_server_socket(AF_INET, SOCK_STREAM, "64646")) < 0)
return -1;
if (listen(sfd, 1)) {
close(sfd);
return -2;
}
if ((csfd = accept(sfd, &address, &addrlen)) < 0) {
close(sfd);
return -3;
}
// TODO: Might not want to toss this out yet.
close(sfd);
return csfd;
}
// Handles an incoming request from the client.
int netapi_debug_handle_request(int sfd, struct cen64_device *device,
const struct netapi_debug_request *req, const uint8_t *req_data) {
uint8_t buf[576];
size_t length;
unsigned i;
struct netapi_debug_request resp;
uint8_t *data = buf + sizeof(resp);
uint32_t u32;
uint64_t u64;
switch(ntohl(req->type)) {
// Get protocol version.
case NETAPI_DEBUG_GET_PROTOCOL_VERSION:
resp.type = req->type;
length = sizeof(resp) + sizeof(u32);
u32 = htonl(NETAPI_DEBUG_VERSION);
memcpy(data, &u32, sizeof(u32));
break;
// Get VR4300 general purpose registers and $PC.
case NETAPI_DEBUG_GET_VR4300_REGS:
resp.type = req->type;
length = sizeof(uint64_t) * 33;
for (i = 0; i < 32; i++) {
u64 = htonll(vr4300_get_register(device->vr4300, i));
memcpy(data + i * sizeof(u64), &u64, sizeof(u64));
}
u64 = htonll(vr4300_get_pc(device->vr4300));
memcpy(data + 32 * sizeof(u64), &u64, sizeof(u64));
break;
// Unsupported command.
default:
resp.type = htonl(NETAPI_DEBUG_ERROR);
length = sizeof(resp);
break;
}
// Send the response.
resp.magic = htonl(NETAPI_DEBUG_MAGIC);
resp.length = htonl(length);
memcpy(buf, &resp, sizeof(resp));
send(sfd, buf, length, 0);
return 0;
}
// Waits for an incoming request, attempts to handle it.
int netapi_debug_wait(int sfd, struct cen64_device *device) {
struct netapi_debug_request req;
uint8_t buf[576];
// Pull and process the header.
recv(sfd, &req, sizeof(req), 0);
if (ntohl(req.magic) != NETAPI_DEBUG_MAGIC ||
ntohl(req.length) > sizeof(buf)) {
debug("net/debug: Got a bad request packet.\n");
req.magic = htonl(NETAPI_DEBUG_MAGIC);
req.length = htonl(sizeof(req));
req.type = htonl(NETAPI_DEBUG_ERROR);
send(sfd, &req, sizeof(req), 0);
return -1;
}
// Pull the body of the request, if any.
if (ntohl(req.length) > sizeof(req))
recv(sfd, buf, ntohl(req.length) - sizeof(req), 0);
return netapi_debug_handle_request(sfd, device, &req, buf);
}