Laika/lib/src/net/lsocket.c

373 lines
10 KiB
C

#include "net/lsocket.h"
#include "core/lerror.h"
#include "core/lmem.h"
#include "core/lsodium.h"
#include "net/lpacket.h"
#include "net/lpolllist.h"
static int _LNSetup = 0;
void laikaS_init(void)
{
if (_LNSetup++ > 0)
return; /* WSA is already setup! */
#ifdef _WIN32
WSADATA wsaData;
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
LAIKA_ERROR("WSAStartup failed!\n");
#endif
}
void laikaS_cleanUp(void)
{
if (--_LNSetup > 0)
return; /* WSA still needs to be up, a socket is still running */
#ifdef _WIN32
WSACleanup();
#endif
}
/* ======================================[[ Socket API ]]======================================= */
void laikaS_initSocket(struct sLaika_socket *sock, pollEvent onPollIn, pollEvent onPollOut,
pollFailEvent onPollFail, void *uData)
{
sock->sock = INVALID_SOCKET;
sock->onPollFail = onPollFail;
sock->onPollIn = onPollIn;
sock->onPollOut = onPollOut;
sock->uData = uData;
laikaM_initVector(sock->inBuf, 8);
laikaM_initVector(sock->outBuf, 8);
sock->flipEndian = false;
sock->setPollOut = false;
laikaS_init();
}
void laikaS_cleanSocket(struct sLaika_socket *sock)
{
/* free in & out arrays */
laikaM_free(sock->inBuf);
laikaM_free(sock->outBuf);
/* kill socket & cleanup WSA */
laikaS_kill(sock);
laikaS_cleanUp();
}
void laikaS_kill(struct sLaika_socket *sock)
{
if (!laikaS_isAlive(sock)) /* sanity check */
return;
#ifdef _WIN32
shutdown(sock->sock, SD_BOTH);
closesocket(sock->sock);
#else
shutdown(sock->sock, SHUT_RDWR);
close(sock->sock);
#endif
sock->sock = INVALID_SOCKET;
}
void laikaS_connect(struct sLaika_socket *sock, char *ip, char *port)
{
struct addrinfo res, *result, *curr;
if (!SOCKETINVALID(sock->sock))
LAIKA_ERROR("socket already setup!\n");
/* zero out our address info and setup the type */
memset(&res, 0, sizeof(struct addrinfo));
res.ai_family = AF_UNSPEC;
res.ai_socktype = SOCK_STREAM;
/* grab the address info */
if (getaddrinfo(ip, port, &res, &result) != 0)
LAIKA_ERROR("getaddrinfo() failed!\n");
/* getaddrinfo returns a list of possible addresses, step through them and try them until we
* find a valid address */
for (curr = result; curr != NULL; curr = curr->ai_next) {
sock->sock = socket(curr->ai_family, curr->ai_socktype, curr->ai_protocol);
/* if it failed, try the next sock */
if (SOCKETINVALID(sock->sock))
continue;
/* if it's not an invalid socket, break and exit the loop, we found a working addr! */
if (!SOCKETINVALID(connect(sock->sock, curr->ai_addr, curr->ai_addrlen)))
break;
laikaS_kill(sock);
}
freeaddrinfo(result);
/* if we reached the end of the linked list, we failed looking up the addr */
if (curr == NULL)
LAIKA_ERROR("couldn't connect a valid address handle to socket!\n");
}
void laikaS_bind(struct sLaika_socket *sock, uint16_t port)
{
socklen_t addressSize;
struct sockaddr_in6 address;
int opt = 1;
if (!SOCKETINVALID(sock->sock))
LAIKA_ERROR("socket already setup!\n");
/* open our socket */
sock->sock = socket(AF_INET6, SOCK_STREAM, 0);
if (SOCKETINVALID(sock->sock))
LAIKA_ERROR("socket() failed!\n");
/* allow reuse of local address */
#ifdef _WIN32
if (setsockopt(sock->sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(int)) != 0)
#else
if (setsockopt(sock->sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)) != 0)
#endif
LAIKA_ERROR("setsockopt() failed!\n");
address.sin6_family = AF_INET6;
address.sin6_addr = in6addr_any;
address.sin6_port = htons(port);
addressSize = sizeof(address);
/* bind to the port */
if (SOCKETERROR(bind(sock->sock, (struct sockaddr *)&address, addressSize)))
LAIKA_ERROR("bind() failed!\n");
if (SOCKETERROR(listen(sock->sock, SOMAXCONN)))
LAIKA_ERROR("listen() failed!\n");
}
void laikaS_acceptFrom(struct sLaika_socket *sock, struct sLaika_socket *from, char *ip)
{
struct sockaddr_in6 address;
socklen_t addressSize = sizeof(address);
sock->sock = accept(from->sock, (struct sockaddr *)&address, &addressSize);
if (SOCKETINVALID(sock->sock))
LAIKA_ERROR("accept() failed!\n");
/* read ip */
if (ip) {
if (inet_ntop(AF_INET6, &address.sin6_addr, ip, LAIKA_IPSTR_LEN) == NULL)
LAIKA_ERROR("inet_ntop() failed!\n");
}
}
bool laikaS_setNonBlock(struct sLaika_socket *sock)
{
#ifdef _WIN32
unsigned long mode = 1;
if (ioctlsocket(sock->sock, FIONBIO, &mode) != 0) {
#else
if (fcntl(sock->sock, F_SETFL, (fcntl(sock->sock, F_GETFL, 0) | O_NONBLOCK)) != 0) {
#endif
LAIKA_WARN("fcntl failed on new connection\n");
laikaS_kill(sock);
return false;
}
return true;
}
/* =====================================[[ Socket stream ]]===================================== */
void laikaS_consumeRead(struct sLaika_socket *sock, size_t sz)
{
laikaM_rmvVector(sock->inBuf, 0, sz);
}
void laikaS_zeroWrite(struct sLaika_socket *sock, size_t sz)
{
laikaM_growVector(uint8_t, sock->outBuf, sz);
/* set NULL bytes */
memset(&sock->outBuf[laikaM_countVector(sock->outBuf)], 0, sz);
laikaM_countVector(sock->outBuf) += sz;
}
void laikaS_read(struct sLaika_socket *sock, void *buf, size_t sz)
{
memcpy(buf, sock->inBuf, sz);
laikaM_rmvVector(sock->inBuf, 0, sz);
}
void laikaS_write(struct sLaika_socket *sock, void *buf, size_t sz)
{
/* make sure we have enough space to copy the buffer */
laikaM_growVector(uint8_t, sock->outBuf, sz);
/* copy the buffer, then increment outCount */
memcpy(&sock->outBuf[laikaM_countVector(sock->outBuf)], buf, sz);
laikaM_countVector(sock->outBuf) += sz;
}
void laikaS_writeKeyEncrypt(struct sLaika_socket *sock, void *buf, size_t sz, uint8_t *pub)
{
/* make sure we have enough space to encrypt the buffer */
laikaM_growVector(uint8_t, sock->outBuf, LAIKAENC_SIZE(sz));
/* encrypt the buffer into outBuf */
if (crypto_box_seal(&sock->outBuf[laikaM_countVector(sock->outBuf)], buf, sz, pub) != 0)
LAIKA_ERROR("Failed to encrypt!\n");
laikaM_countVector(sock->outBuf) += LAIKAENC_SIZE(sz);
}
void laikaS_readKeyDecrypt(struct sLaika_socket *sock, void *buf, size_t sz, uint8_t *pub,
uint8_t *priv)
{
/* decrypt into buf */
if (crypto_box_seal_open(buf, sock->inBuf, LAIKAENC_SIZE(sz), pub, priv) != 0)
LAIKA_ERROR("Failed to decrypt!\n");
laikaM_rmvVector(sock->inBuf, 0, LAIKAENC_SIZE(sz));
}
void laikaS_writeByte(struct sLaika_socket *sock, uint8_t data)
{
laikaM_growVector(uint8_t, sock->outBuf, 1);
sock->outBuf[laikaM_countVector(sock->outBuf)++] = data;
}
uint8_t laikaS_readByte(struct sLaika_socket *sock)
{
uint8_t tmp = *sock->inBuf;
/* consume 1 byte */
laikaM_rmvVector(sock->inBuf, 0, 1);
return tmp;
}
void laikaS_writeu16(struct sLaika_socket *sock, uint16_t i)
{
uint16_t tmp = i; /* copy int to buffer (which we can reverse if need-be) */
if (sock->flipEndian)
laikaM_reverse((uint8_t *)&tmp, sizeof(tmp));
laikaS_write(sock, (void *)&tmp, sizeof(tmp));
}
uint16_t laikaS_readu16(struct sLaika_socket *sock)
{
uint16_t tmp;
laikaS_read(sock, (void *)&tmp, sizeof(tmp));
if (sock->flipEndian)
laikaM_reverse((uint8_t *)&tmp, sizeof(tmp));
return tmp;
}
void laikaS_writeu32(struct sLaika_socket *sock, uint32_t i)
{
uint32_t tmp = i; /* copy int to buffer (which we can reverse if need-be) */
if (sock->flipEndian)
laikaM_reverse((uint8_t *)&tmp, sizeof(tmp));
laikaS_write(sock, (void *)&tmp, sizeof(tmp));
}
uint32_t laikaS_readu32(struct sLaika_socket *sock)
{
uint32_t tmp;
laikaS_read(sock, (void *)&tmp, sizeof(tmp));
if (sock->flipEndian)
laikaM_reverse((uint8_t *)&tmp, sizeof(tmp));
return tmp;
}
/* ===================================[[ Socket send/recv ]]==================================== */
RAWSOCKCODE laikaS_rawSend(struct sLaika_socket *sock, size_t sz, int *processed)
{
RAWSOCKCODE errCode = RAWSOCK_OK;
int sent, i, sentBytes = 0;
/* write bytes to the socket until an error occurs or we finish sending */
do {
sent = send(sock->sock, (buffer_t *)(&sock->outBuf[sentBytes]), sz - sentBytes,
LN_MSG_NOSIGNAL);
/* check for error result */
if (sent == 0) { /* connection closed gracefully */
errCode = RAWSOCK_CLOSED;
goto _rawWriteExit;
} else if (SOCKETERROR(sent)) { /* socket error? */
if (LN_ERRNO != LN_EWOULD
#ifndef _WIN32
/* posix also has some platforms which define EAGAIN as a different value than
EWOULD, might as well support it. */
&& LN_ERRNO != EAGAIN
#endif
) { /* socket error! */
errCode = RAWSOCK_ERROR;
goto _rawWriteExit;
}
/*
it was a result of EWOULD or EAGAIN, kernel socket send buffer is full,
tell the caller we need to set our poll event POLLOUT
*/
errCode = RAWSOCK_POLL;
goto _rawWriteExit;
}
} while ((sentBytes += sent) < sz);
_rawWriteExit:
/* trim sent data from outBuf */
laikaM_rmvVector(sock->outBuf, 0, sentBytes);
*processed = sentBytes;
return errCode;
}
RAWSOCKCODE laikaS_rawRecv(struct sLaika_socket *sock, size_t sz, int *processed)
{
RAWSOCKCODE errCode = RAWSOCK_OK;
int i, rcvd, start = laikaM_countVector(sock->inBuf);
/* sanity check */
if (sz == 0)
return RAWSOCK_OK;
/* make sure we have enough space to recv */
laikaM_growVector(uint8_t, sock->inBuf, sz);
rcvd = recv(sock->sock, (buffer_t *)&sock->inBuf[laikaM_countVector(sock->inBuf)], sz,
LN_MSG_NOSIGNAL);
if (rcvd == 0) {
errCode = RAWSOCK_CLOSED;
} else if (SOCKETERROR(rcvd) &&
LN_ERRNO != LN_EWOULD
#ifndef _WIN32
/* if it's a posix system, also make sure its not a EAGAIN result (which is a
recoverable error, there's just nothing to read lol) */
&& LN_ERRNO != EAGAIN
#endif
) {
/* if the socket closed or an error occurred, return the error result */
errCode = RAWSOCK_ERROR;
} else if (rcvd > 0) {
/* recv() worked, add rcvd to inCount */
laikaM_countVector(sock->inBuf) += rcvd;
}
*processed = rcvd;
return errCode;
}