mirror of
https://git.checksum.fail/alec/TOL.git
synced 2026-05-26 21:50:04 +00:00
328 lines
6.9 KiB
HolyC
328 lines
6.9 KiB
HolyC
#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 INET_ADDRSTRLEN 16
|
|
|
|
#define NS_INADDRSZ 4
|
|
|
|
#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_pton(I64 af, U8 *src, U8 *dst) {
|
|
I64 saw_digit, octets, ch;
|
|
U8 tmp[NS_INADDRSZ], *tp;
|
|
|
|
if (af != AF_INET) {
|
|
return -1;
|
|
}
|
|
|
|
saw_digit = 0;
|
|
octets = 0;
|
|
*(tp = tmp) = 0;
|
|
while (*src) {
|
|
ch = *src++;
|
|
if (ch >= '0' && ch <= '9') {
|
|
U64 new = *tp * 10 + (ch - '0');
|
|
if (saw_digit && *tp == 0)
|
|
return 0;
|
|
if (new > 255)
|
|
return 0;
|
|
*tp = new;
|
|
if (!saw_digit) {
|
|
if (++octets > 4)
|
|
return 0;
|
|
saw_digit = 1;
|
|
}
|
|
} else if (ch == '.' && saw_digit) {
|
|
if (octets == 4)
|
|
return 0;
|
|
*++tp = 0;
|
|
saw_digit = 0;
|
|
} else
|
|
return 0;
|
|
}
|
|
if (octets < 4)
|
|
return 0;
|
|
MemCpy(dst, tmp, NS_INADDRSZ);
|
|
return 1;
|
|
}
|
|
|
|
U8 *inet_ntop(I64 af, U8 *src, U8 *dst, I64 size) {
|
|
if (af == AF_INET && size >= INET_ADDRSTRLEN) {
|
|
StrPrint(dst, "%d.%d.%d.%d", src[0], src[1], src[2], src[3]);
|
|
return dst;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|