Use the posix socket API wrapper of the zig standard library instead of messing with C code.

This commit is contained in:
IntegratedQuantum 2023-01-05 17:18:11 +01:00
parent d9b81b91f8
commit 43da550385
4 changed files with 52 additions and 149 deletions

View File

@ -39,7 +39,7 @@ pub fn build(b: *std.build.Builder) !void {
std.log.err("Unsupported target: {}\n", .{ target.getOsTag() });
}
}
exe.addCSourceFiles(&[_][]const u8{"lib/glad.c", "lib/stb_image.c", "lib/cross_platform_udp_socket.c"}, &[_][]const u8{"-g"});
exe.addCSourceFiles(&[_][]const u8{"lib/glad.c", "lib/stb_image.c"}, &[_][]const u8{"-g"});
exe.addPackage(freetype.pkg);
exe.addPackage(freetype.harfbuzz_pkg);
freetype.link(b, exe, .{ .harfbuzz = .{} });

View File

@ -1,11 +0,0 @@
#include <stdint.h>
void startup();
int init(unsigned short localPort);
int deinit(int socketID);
int sendTo(int socketID, const char* data, uintptr_t size, uint32_t ip, uint16_t port);
intptr_t receiveFrom(int socketID, char* buffer, uintptr_t size, int timeout, uint32_t* resultIP, uint16_t* resultPort);
uint32_t resolveIP(const char* ip);
int getError();

View File

@ -1,110 +0,0 @@
#ifdef _WIN32
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#endif
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <poll.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <arpa/inet.h>
#endif
#include <stdio.h>
#include <cross_platform_udp_socket.h>
int checkError(int in) {
// TODO: Print the error here.
return in;
}
void startup() {
#ifdef _WIN32
WSADATA d;
if (WSAStartup(MAKEWORD(2, 2), &d)) {
fprintf(stderr, "Failed to initialize.\n");
}
#endif
}
int init(unsigned short localPort) {
int socketID = checkError(socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP));
if(socketID == -1) return -1;
struct sockaddr_in bindingAddr;
bindingAddr.sin_family = AF_INET;
bindingAddr.sin_port = htons(localPort);
bindingAddr.sin_addr.s_addr = INADDR_ANY;
memset(&bindingAddr.sin_zero, 0, 8);
if(checkError(bind(socketID, (const struct sockaddr*)&bindingAddr, sizeof(bindingAddr))) == -1) {
#ifdef _WIN32
closesocket(socketID);
#else
close(socketID);
#endif
return -1;
};
return socketID;
}
int deinit(int socketID) {
#ifdef _WIN32
return checkError(closesocket(socketID));
#else
return checkError(close(socketID));
#endif
}
int sendTo(int socketID, const char* data, uintptr_t size, uint32_t ip, uint16_t port) {
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = ip;
memset(&addr.sin_zero, 0, 8);
return checkError(sendto(socketID, data, size, 0, (const struct sockaddr*)&addr, sizeof(addr)));
}
intptr_t receiveFrom(int socketID, char* buffer, uintptr_t size, int timeout, uint32_t* resultIP, uint16_t* resultPort) {
struct pollfd pfd = {.fd = socketID, .events = POLLIN};
#ifdef _WIN32
intptr_t result = checkError(WSAPoll(&pfd, 1, timeout));
#else
intptr_t result = checkError(poll(&pfd, 1, timeout));
#endif
if(result <= 0) return result;
struct sockaddr_in address;
uint32_t addrLen = sizeof(address);
result = checkError(recvfrom(socketID, buffer, size, 0, (struct sockaddr *)&address, &addrLen));
*resultIP = address.sin_addr.s_addr;
*resultPort = ntohs(address.sin_port);
return result;
}
uint32_t resolveIP(const char* address) {
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
struct addrinfo* res;
int result = getaddrinfo(address, NULL, &hints, &res);
if(result != 0) {
errno = result;
return 0xffffffff;
}
uint32_t ip = ((struct sockaddr_in *)res->ai_addr)->sin_addr.s_addr;
freeaddrinfo(res);
return ip;
}
int getError() {
return errno;
}

View File

@ -1,3 +1,4 @@
const builtin = @import("builtin");
const std = @import("std");
const Allocator = std.mem.Allocator;
@ -22,46 +23,65 @@ const Vec3f = vec.Vec3f;
//TODO: Might want to use SSL or something similar to encode the message
const Socket = struct {
const c = @cImport({@cInclude("cross_platform_udp_socket.h");});
socketID: u31,
const os = std.os;
socketID: os.socket_t,
fn checkError(comptime msg: []const u8, comptime T: type, result: T) !std.meta.Int(.unsigned, @bitSizeOf(T) - 1) {
if(result == -1) {
std.log.warn(msg, .{c.getError()});
return error.SocketError;
fn startup() void {
if(builtin.os.tag == .windows) {
_ = os.windows.WSAStartup(2, 2) catch |err| { // TODO: Return the error (this triggers a false depency loop error right now).
std.log.err("Error trying to startup WSA: {}", .{@errorName(err)});
};
}
return @intCast(std.meta.Int(.unsigned, @bitSizeOf(T) - 1), result);
}
fn init(localPort: u16) !Socket {
return Socket{.socketID = try checkError("Socket creation failed with error: {}", c_int, c.init(localPort))};
var self = Socket {
.socketID = try os.socket(os.AF.INET, os.SOCK.DGRAM, os.IPPROTO.UDP),
};
errdefer self.deinit();
const bindingAddr = os.sockaddr.in {
.port = @byteSwap(localPort),
.addr = 0,
};
try os.bind(self.socketID, @ptrCast(*const os.sockaddr, &bindingAddr), @sizeOf(os.sockaddr.in));
return self;
}
fn deinit(self: Socket) void {
_ = checkError("Error while closing socket: {}", c_int, c.deinit(self.socketID)) catch 0;
os.closeSocket(self.socketID);
}
fn send(self: Socket, data: []const u8, destination: Address) !void {
_ = try checkError("Error sending data: {}", isize, c.sendTo(self.socketID, data.ptr, data.len, destination.ip, destination.port));
const addr = os.sockaddr.in {
.port = @byteSwap(destination.port),
.addr = destination.ip,
};
std.debug.assert(data.len == try os.sendto(self.socketID, data, 0, @ptrCast(*const os.sockaddr, &addr), @sizeOf(os.sockaddr.in)));
}
fn receive(self: Socket, buffer: []u8, timeout: c_int, resultAddress: *Address) ![]u8 {
var length = try checkError("Receive failed: {}", isize, c.receiveFrom(self.socketID, buffer.ptr, buffer.len, timeout, &resultAddress.ip, &resultAddress.port));
fn receive(self: Socket, buffer: []u8, timeout: i32, resultAddress: *Address) ![]u8 {
var pfd = [1]os.pollfd {
.{.fd = self.socketID, .events = os.POLL.IN, .revents = undefined},
};
var length = try os.poll(&pfd, timeout);
if(length == 0) return error.Timeout;
var addr: os.sockaddr.in = undefined;
var addrLen: os.socklen_t = @sizeOf(os.sockaddr.in);
length = try os.recvfrom(self.socketID, buffer, 0, @ptrCast(*os.sockaddr, &addr), &addrLen);
resultAddress.ip = addr.addr;
resultAddress.port = @byteSwap(addr.port);
return buffer[0..length];
}
fn resolveIP(ip: [:0]const u8) u32 {
const result: u32 = c.resolveIP(ip.ptr);
if(result == 0xffffffff) {
std.log.warn("Could not resolve address: {s} error: {}", .{ip, c.getError()});
}
return result;
fn resolveIP(addr: []const u8) !u32 {
const list = try std.net.getAddressList(main.threadAllocator, addr, settings.defaultPort);
defer list.deinit();
return list.addrs[0].in.sa.addr;
}
};
pub fn init() void {
Socket.c.startup();
Socket.startup();
inline for(@typeInfo(@TypeOf(Protocols)).Struct.fields) |field| {
if(field.type == type) {
const id = @field(Protocols, field.name).id;
@ -239,9 +259,14 @@ const STUN = struct {
random.fill(data[8..]); // Fill the transaction ID.
var splitter = std.mem.split(u8, server, ":");
var nullTerminatedIP = main.threadAllocator.dupeZ(u8, splitter.first()) catch continue;
defer main.threadAllocator.free(nullTerminatedIP);
var serverAddress = Address{.ip=Socket.resolveIP(nullTerminatedIP), .port=std.fmt.parseUnsigned(u16, splitter.rest(), 10) catch 3478};
const ip = splitter.first();
var serverAddress = Address {
.ip=Socket.resolveIP(ip) catch |err| {
std.log.err("Cannot resolve stun server address: {s}, error: {s}", .{ip, @errorName(err)});
continue;
},
.port=std.fmt.parseUnsigned(u16, splitter.rest(), 10) catch 3478
};
if(connection.sendRequest(connection.allocator, &data, serverAddress, 500*1000000) catch |err| {
std.log.warn("Encountered error: {s} while connecting to STUN server: {s}", .{@errorName(err), server});
continue;
@ -270,7 +295,7 @@ const STUN = struct {
std.log.warn("Couldn't reach STUN server: {s}", .{server});
}
}
return Address{.ip=Socket.resolveIP("127.0.0.1"), .port=settings.defaultPort}; // TODO: Return ip address in LAN.
return Address{.ip=Socket.resolveIP("127.0.0.1") catch unreachable, .port=settings.defaultPort}; // TODO: Return ip address in LAN.
}
fn findIPPort(_data: []const u8) !Address {
@ -330,7 +355,7 @@ pub const ConnectionManager = struct {
thread: std.Thread = undefined,
threadId: std.Thread.Id = undefined,
externalAddress: Address = undefined,
online: std.atomic.Atomic(bool) = std.atomic.Atomic(bool).init(true),
online: std.atomic.Atomic(bool) = std.atomic.Atomic(bool).init(false),
running: std.atomic.Atomic(bool) = std.atomic.Atomic(bool).init(true),
connections: std.ArrayList(*Connection) = undefined,
@ -1248,9 +1273,8 @@ pub const Connection = struct {
std.ArrayList(u32).init(result.allocator),
};
var splitter = std.mem.split(u8, ipPort, ":");
var nullTerminatedIP = try main.threadAllocator.dupeZ(u8, splitter.first());
defer main.threadAllocator.free(nullTerminatedIP);
result.remoteAddress.ip = Socket.resolveIP(nullTerminatedIP);
const ip = splitter.first();
result.remoteAddress.ip = try Socket.resolveIP(ip);
var port = splitter.rest();
if(port.len != 0 and port[0] == '?') {
result.remoteAddress.isSymmetricNAT = true;