Files
Shrine/SnailNet/NativeSocket.HC
2017-06-17 23:17:17 +02:00

265 lines
6.3 KiB
HolyC

// vim: set ft=c:
#define SOCK_STREAM 1
#define SOCK_DGRAM 2
#define SOCK_RAW 3
#define AF_UNSPEC 0
#define AF_INET 2
#define AF_INET6 10
#define INADDR_ANY 0
#define SOL_SOCKET 1
// optval = I64*
#define SO_RCVTIMEO_MS 1
#define AI_CACHED 0x8000
class in_addr {
U32 s_addr;
};
class sockaddr {
U16 sa_family;
U8 sa_data[14];
};
class sockaddr_in {
I16 sin_family;
U16 sin_port;
in_addr sin_addr;
U8 sin_zero[8];
};
class addrinfo {
I32 ai_flags;
I32 ai_family;
I32 ai_socktype;
I32 ai_protocol;
I64 ai_addrlen;
sockaddr* ai_addr;
U8* ai_canonname;
addrinfo* ai_next;
};
I64 inet_aton(U8* cp, in_addr* inp) {
// FIXME: error handling
I64 a, b, c, d;
StrScan(cp, "%d.%d.%d.%d", &a, &b, &c, &d);
inp->s_addr = (a | (b << 8) | (c << 16) | (d << 24));
return 0;
}
U8* inet_ntoa(in_addr in) {
static U8 buffer[16];
StrPrint(buffer, "%d.%d.%d.%d", in.s_addr & 0xff, (in.s_addr >> 8) & 0xff,
(in.s_addr >> 16) & 0xff, (in.s_addr >> 24) & 0xff);
return buffer;
}
class CSocket {
I64 (*accept)(CSocket* s, sockaddr* src_addr, I64 addrlen);
I64 (*bind)(CSocket* s, sockaddr* addr, I64 addrlen);
I64 (*close)(CSocket* s);
I64 (*connect)(CSocket* s, sockaddr* addr, I64 addrlen);
I64 (*listen)(CSocket* s, I64 backlog);
I64 (*recvfrom)(CSocket* s, U8* buf, I64 len, I64 flags, sockaddr* src_addr, I64 addrlen);
I64 (*sendto)(CSocket* s, U8* buf, I64 len, I64 flags, sockaddr* dest_addr, I64 addrlen);
I64 (*setsockopt)(CSocket* s, I64 level, I64 optname, U8* optval, I64 optlen);
};
class CSocketClass {
CSocketClass* next;
U16 domain;
U16 type;
U8 padding[4];
CSocket* (*socket)(U16 domain, U16 type);
};
class CAddrResolver {
// TODO: allow different resolvers for different socket domains
I64 (*getaddrinfo)(U8* node, U8* service, addrinfo* hints, addrinfo** res);
};
static CSocketClass* socket_classes = NULL;
static CAddrResolver* socket_addr_resolver = NULL;
static CSocketClass* FindSocketClass(U16 domain, U16 type) {
CSocketClass* cls = socket_classes;
while (cls) {
if (cls->domain == domain && cls->type == type)
return cls;
cls = cls->next;
}
return NULL;
}
I64 SocketInit() {
return 0;
}
I64 socket(I64 domain, I64 type) {
CSocketClass* cls = FindSocketClass(domain, type);
if (cls) return cls->socket(domain, type)(I64);
else return -1;
}
I64 accept(I64 sockfd, sockaddr* addr, I64 addrlen) {
CSocket* sock = sockfd(CSocket*);
if (sockfd > 0) return sock->accept(sock, addr, addrlen);
else return -1;
}
I64 close(I64 sockfd) {
CSocket* sock = sockfd(CSocket*);
if (sockfd > 0) return sock->close(sock);
else return -1;
}
I64 bind(I64 sockfd, sockaddr* addr, I64 addrlen) {
CSocket* sock = sockfd(CSocket*);
if (sockfd > 0) return sock->bind(sock, addr, addrlen);
else return -1;
}
I64 connect(I64 sockfd, sockaddr* addr, I64 addrlen) {
CSocket* sock = sockfd(CSocket*);
if (sockfd > 0) return sock->connect(sock, addr, addrlen);
else return -1;
}
I64 listen(I64 sockfd, I64 backlog) {
CSocket* sock = sockfd(CSocket*);
if (sockfd > 0) return sock->listen(sock, backlog);
else return -1;
}
I64 recv(I64 sockfd, U8* buf, I64 len, I64 flags) {
CSocket* sock = sockfd(CSocket*);
if (sockfd > 0) return sock->recvfrom(sock, buf, len, flags, NULL, 0);
else return -1;
}
I64 recvfrom(I64 sockfd, U8* buf, I64 len, I64 flags, sockaddr* src_addr, I64 addrlen) {
CSocket* sock = sockfd(CSocket*);
if (sockfd > 0) return sock->recvfrom(sock, buf, len, flags, src_addr, addrlen);
else return -1;
}
I64 send(I64 sockfd, U8* buf, I64 len, I64 flags) {
CSocket* sock = sockfd(CSocket*);
if (sockfd > 0) return sock->sendto(sock, buf, len, flags, NULL, 0);
else return -1;
}
I64 sendto(I64 sockfd, U8* buf, I64 len, I64 flags, sockaddr* dest_addr, I64 addrlen) {
CSocket* sock = sockfd(CSocket*);
if (sockfd > 0) return sock->sendto(sock, buf, len, flags, dest_addr, addrlen);
else return -1;
}
I64 setsockopt(I64 sockfd, I64 level, I64 optname, U8* optval, I64 optlen) {
CSocket* sock = sockfd(CSocket*);
if (sockfd > 0) return sock->setsockopt(sock, level, optname, optval, optlen);
else return -1;
}
I64 getaddrinfo(U8* node, U8* service, addrinfo* hints, addrinfo** res) {
if (socket_addr_resolver) return socket_addr_resolver->getaddrinfo(node, service, hints, res);
else return -1;
}
U0 freeaddrinfo(addrinfo* res) {
while (res) {
addrinfo* next = res->ai_next;
Free(res->ai_addr);
Free(res->ai_canonname);
Free(res);
res = next;
}
}
U0 AddrInfoCopy(addrinfo* ai_out, addrinfo* ai_in) {
MemCpy(ai_out, ai_in, sizeof(addrinfo));
if (ai_in->ai_addr) {
ai_out->ai_addr = MAlloc(ai_in->ai_addrlen);
MemCpy(ai_out->ai_addr, ai_in->ai_addr, ai_in->ai_addrlen);
}
if (ai_in->ai_canonname) {
ai_out->ai_canonname = StrNew(ai_in->ai_canonname);
}
}
U8* gai_strerror(I64 errcode) {
no_warn errcode;
return "Unspecified error";
}
// Inspired by https://docs.python.org/3.7/library/socket.html#socket.create_connection
I64 create_connection(U8* hostname, U16 port) {
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = 0;
addrinfo* res;
I64 error = getaddrinfo(hostname, NULL, NULL, &res);
if (error < 0) {
"$FG,4$getaddrinfo: error %d\n$FG$", error;
}
else {
addrinfo* curr = res;
while (curr) {
if (curr->ai_family == AF_INET && (curr->ai_socktype == 0 || curr->ai_socktype == SOCK_STREAM)) {
addr.sin_addr.s_addr = (curr->ai_addr(sockaddr_in*))->sin_addr.s_addr;
freeaddrinfo(res);
I64 sockfd = socket(AF_INET, SOCK_STREAM);
if (sockfd < 0)
return sockfd;
error = connect(sockfd, &addr, sizeof(addr));
if (error < 0) {
close(sockfd);
return error;
}
return sockfd;
}
curr = curr->ai_next;
}
"$FG,4$create_connection: no suitable address\n$FG$";
}
freeaddrinfo(res);
return -1;
}
U0 RegisterSocketClass(U16 domain, U16 type, CSocket* (*socket)(U16 domain, U16 type)) {
CSocketClass* cls = MAlloc(sizeof(CSocketClass));
cls->next = socket_classes;
cls->domain = domain;
cls->type = type;
cls->socket = socket;
socket_classes = cls;
}