mirror of
https://github.com/n64dev/cen64.git
synced 2024-06-04 22:09:35 -04:00
231 lines
5.4 KiB
C
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);
|
|
}
|
|
|