All checks were successful
CI / build-and-analyze (push) Successful in 1m12s
470 lines
12 KiB
C
470 lines
12 KiB
C
#include "Internal.h"
|
|
#include <arpa/inet.h>
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <assert.h>
|
|
|
|
int CgeNetInit(void) {
|
|
return 1;
|
|
}
|
|
|
|
void CgeNetCleanup(void) {
|
|
}
|
|
|
|
static int errnoToErrorCode(int value) {
|
|
switch (value) {
|
|
case 0:
|
|
return CGE_NET_EOK;
|
|
|
|
case EHOSTUNREACH:
|
|
return CGE_NET_EHOSTUNREACH;
|
|
|
|
case ENETUNREACH:
|
|
return CGE_NET_ENETUNREACH;
|
|
|
|
case EINVAL:
|
|
return CGE_NET_EINVAL;
|
|
|
|
case ENOMEM:
|
|
return CGE_NET_EOOM;
|
|
|
|
case ETIMEDOUT:
|
|
return CGE_NET_ETIMEDOUT;
|
|
|
|
case EWOULDBLOCK:
|
|
return CGE_NET_EWOULDBLOCK;
|
|
|
|
case EADDRINUSE:
|
|
return CGE_NET_EADDRINUSE;
|
|
|
|
case EADDRNOTAVAIL:
|
|
return CGE_NET_EADDRNOTAVAIL;
|
|
|
|
case ECONNREFUSED:
|
|
return CGE_NET_ECONNREFUSED;
|
|
|
|
case ECONNRESET:
|
|
return CGE_NET_ECONNRESET;
|
|
|
|
case ECONNABORTED:
|
|
return CGE_NET_ECONNABORTED;
|
|
|
|
default:
|
|
return CGE_NET_EUNKNOWN;
|
|
}
|
|
}
|
|
|
|
static int socketInit(CgeNetSocket *s, int domain, int type) {
|
|
int protocol;
|
|
|
|
assert(s != NULL);
|
|
|
|
s->domain = domain;
|
|
s->type = type;
|
|
s->pollObject = NULL;
|
|
s->pollIndex = 0;
|
|
|
|
switch (domain) {
|
|
case CGE_NET_IPV4: domain = AF_INET; break;
|
|
case CGE_NET_IPV6: domain = AF_INET6; break;
|
|
default: return CGE_NET_EINVAL;
|
|
}
|
|
|
|
switch (type) {
|
|
case CGE_NET_TCP: type = SOCK_STREAM; protocol = IPPROTO_TCP; break;
|
|
case CGE_NET_UDP: type = SOCK_DGRAM; protocol = IPPROTO_UDP; break;
|
|
default: return CGE_NET_EINVAL;
|
|
}
|
|
|
|
s->handle = socket(domain, type, protocol);
|
|
if (s->handle == -1)
|
|
return errnoToErrorCode(errno);
|
|
fcntl(s->handle, F_SETFD, FD_CLOEXEC);
|
|
|
|
return CGE_NET_EOK;
|
|
}
|
|
|
|
CgeNetSocket *CgeNetOpen(int domain, int type, int *result) {
|
|
CgeNetSocket *socket;
|
|
int code = CGE_NET_EOOM;
|
|
|
|
if ((socket = malloc(sizeof(*socket)))) {
|
|
if ((code = socketInit(socket, domain, type))) {
|
|
free(socket);
|
|
socket = NULL;
|
|
}
|
|
}
|
|
if (result)
|
|
*result = code;
|
|
|
|
return socket;
|
|
}
|
|
|
|
void CgeNetClose(CgeNetSocket *socket) {
|
|
if (!socket)
|
|
return;
|
|
|
|
if (socket->pollObject)
|
|
CgeNetPollRemove(socket->pollObject, socket);
|
|
|
|
assert(socket->handle != -1);
|
|
close(socket->handle);
|
|
free(socket);
|
|
}
|
|
|
|
int CgeNetErrorCode(CgeNetSocket *socket) {
|
|
assert(socket != NULL);
|
|
return socket->lastError;
|
|
}
|
|
|
|
int CgeNetFetchError(CgeNetSocket *socket) {
|
|
int result;
|
|
socklen_t len = sizeof(result);
|
|
|
|
assert(socket != NULL);
|
|
assert(socket->handle != -1);
|
|
|
|
if (getsockopt(socket->handle, SOL_SOCKET, SO_ERROR, &result, &len) < 0) {
|
|
socket->lastError = errnoToErrorCode(errno);
|
|
return 0;
|
|
}
|
|
|
|
socket->lastError = errnoToErrorCode(result);
|
|
return result != 0;
|
|
}
|
|
|
|
int CgeNetSetBlocking(CgeNetSocket *socket, int value) {
|
|
int flags;
|
|
|
|
assert(socket != NULL);
|
|
assert(socket->handle != -1);
|
|
|
|
if ((flags = fcntl(socket->handle, F_GETFL, 0)) == -1)
|
|
goto error;
|
|
flags = value ? (flags | O_NONBLOCK) : (flags & ~O_NONBLOCK);
|
|
if (fcntl(socket->handle, F_SETFL, flags) == -1)
|
|
goto error;
|
|
return 1;
|
|
|
|
error:
|
|
socket->lastError = errnoToErrorCode(errno);
|
|
return 0;
|
|
}
|
|
|
|
int CgeNetBind(CgeNetSocket *socket, const CgeNetAddr *addr) {
|
|
struct sockaddr_in sa4;
|
|
struct sockaddr_in6 sa6;
|
|
const struct sockaddr *sa;
|
|
socklen_t salen;
|
|
int result;
|
|
|
|
assert(socket != NULL);
|
|
assert(socket->handle != -1);
|
|
assert(addr != NULL);
|
|
assert(addr->type == CGE_NET_IPV4 || addr->type == CGE_NET_IPV6);
|
|
|
|
if (addr->type == CGE_NET_IPV4) {
|
|
memset(&sa4, 0, sizeof(sa4));
|
|
sa4.sin_family = AF_INET;
|
|
sa4.sin_port = htons(addr->ipv4.port);
|
|
memcpy(&sa4.sin_addr, addr->ipv4.addr, 4);
|
|
sa = (struct sockaddr*)&sa4;
|
|
salen = sizeof(sa4);
|
|
} else {
|
|
memset(&sa6, 0, sizeof(sa6));
|
|
sa6.sin6_family = AF_INET6;
|
|
sa6.sin6_port = htons(addr->ipv6.port);
|
|
memcpy(&sa6.sin6_addr, addr->ipv6.addr, 16);
|
|
sa = (struct sockaddr*)&sa6;
|
|
salen = sizeof(sa6);
|
|
}
|
|
|
|
result = bind(socket->handle, sa, salen);
|
|
if (result == -1)
|
|
socket->lastError = errnoToErrorCode(errno);
|
|
|
|
return result != -1;
|
|
}
|
|
|
|
int CgeNetListen(CgeNetSocket *socket, unsigned int backlog) {
|
|
int result;
|
|
|
|
assert(socket != NULL);
|
|
assert(socket->handle != -1);
|
|
|
|
result = listen(socket->handle, backlog);
|
|
if (result < 0)
|
|
socket->lastError = errnoToErrorCode(errno);
|
|
return result == 0;
|
|
}
|
|
|
|
CgeNetSocket *CgeNetAccept(CgeNetSocket *socket, CgeNetAddr *addr) {
|
|
int fd;
|
|
CgeNetSocket *client;
|
|
struct sockaddr_storage ss;
|
|
socklen_t slen = sizeof(ss);
|
|
|
|
assert(socket != NULL);
|
|
assert(socket->handle != -1);
|
|
|
|
do {
|
|
fd = accept(socket->handle, (struct sockaddr*)&ss, &slen);
|
|
} while (fd == -1 && errno == EINTR);
|
|
|
|
if (fd == -1) {
|
|
socket->lastError = errnoToErrorCode(errno);
|
|
return NULL;
|
|
}
|
|
|
|
if (!(client = malloc(sizeof(*client)))) {
|
|
close(fd);
|
|
socket->lastError = CGE_NET_EOOM;
|
|
return NULL;
|
|
}
|
|
|
|
client->handle = fd;
|
|
client->domain = socket->domain;
|
|
client->type = socket->type;
|
|
client->lastError = CGE_NET_EOK;
|
|
fcntl(fd, F_SETFD, FD_CLOEXEC);
|
|
|
|
if (addr) {
|
|
if (ss.ss_family == AF_INET) {
|
|
struct sockaddr_in *sa4 = (struct sockaddr_in*)&ss;
|
|
addr->ipv4.type = CGE_NET_IPV4;
|
|
memcpy(addr->ipv4.addr, &sa4->sin_addr, 4);
|
|
addr->ipv4.port = ntohs(sa4->sin_port);
|
|
} else {
|
|
struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)&ss;
|
|
addr->ipv6.type = CGE_NET_IPV6;
|
|
memcpy(addr->ipv6.addr, &sa6->sin6_addr, 16);
|
|
addr->ipv6.port = ntohs(sa6->sin6_port);
|
|
}
|
|
}
|
|
|
|
return client;
|
|
}
|
|
|
|
int CgeNetConnect(CgeNetSocket *socket, const CgeNetAddr *addr) {
|
|
struct sockaddr_in sa4;
|
|
struct sockaddr_in6 sa6;
|
|
const struct sockaddr *sa;
|
|
socklen_t salen;
|
|
int result;
|
|
int pollResult;
|
|
|
|
assert(socket != NULL);
|
|
assert(socket->handle != -1);
|
|
assert(addr != NULL);
|
|
assert(addr->type == CGE_NET_IPV4 || addr->type == CGE_NET_IPV6);
|
|
|
|
if (addr->type == CGE_NET_IPV4) {
|
|
memset(&sa4, 0, sizeof(sa4));
|
|
sa4.sin_family = AF_INET;
|
|
sa4.sin_port = htons(addr->ipv4.port);
|
|
memcpy(&sa4.sin_addr, addr->ipv4.addr, 4);
|
|
sa = (struct sockaddr*)&sa4;
|
|
salen = sizeof(sa4);
|
|
} else {
|
|
memset(&sa6, 0, sizeof(sa6));
|
|
sa6.sin6_family = AF_INET6;
|
|
sa6.sin6_port = htons(addr->ipv6.port);
|
|
memcpy(&sa6.sin6_addr, addr->ipv6.addr, 16);
|
|
sa = (struct sockaddr*)&sa6;
|
|
salen = sizeof(sa6);
|
|
}
|
|
|
|
result = connect(socket->handle, sa, salen);
|
|
if (result < 0 && errno == EINTR) {
|
|
socklen_t len = sizeof(result);
|
|
|
|
result = 0;
|
|
do {
|
|
struct pollfd pfd;
|
|
pfd.fd = socket->handle;
|
|
pfd.events = POLLOUT;
|
|
pollResult = poll(&pfd, 1, -1);
|
|
} while (pollResult < 0 && errno == EINTR);
|
|
|
|
if (pollResult < 0) {
|
|
socket->lastError = errnoToErrorCode(errno);
|
|
return 0;
|
|
}
|
|
|
|
if (getsockopt(socket->handle, SOL_SOCKET, SO_ERROR, &result, &len) < 0) {
|
|
socket->lastError = errnoToErrorCode(errno);
|
|
return 0;
|
|
}
|
|
errno = result;
|
|
result = result != 0 ? -1 : 0;
|
|
}
|
|
|
|
if (result == -1)
|
|
socket->lastError = errnoToErrorCode(errno);
|
|
return result != -1;
|
|
}
|
|
|
|
int CgeNetWrite(CgeNetSocket *socket, const void *buffer, size_t size,
|
|
size_t *actual) {
|
|
ssize_t result;
|
|
|
|
assert(socket != NULL);
|
|
assert(socket->handle != -1);
|
|
assert(buffer != NULL);
|
|
|
|
do {
|
|
result = send(socket->handle, buffer, size, 0);
|
|
} while (result < 0 && errno == EINTR);
|
|
|
|
if (actual)
|
|
*actual = result;
|
|
socket->lastError = result >= 0 ? CGE_NET_EOK : errnoToErrorCode(errno);
|
|
return result >= 0;
|
|
}
|
|
|
|
int CgeNetRead(CgeNetSocket *socket, void *buffer, size_t size,
|
|
size_t *actual) {
|
|
ssize_t result;
|
|
|
|
assert(socket != NULL);
|
|
assert(socket->handle != -1);
|
|
assert(buffer != NULL);
|
|
|
|
do {
|
|
result = recv(socket->handle, buffer, size, 0);
|
|
} while (result < 0 && errno == EINTR);
|
|
|
|
if (size > 0 && result == 0 && socket->type == CGE_NET_TCP) {
|
|
socket->lastError = CGE_NET_EDISCONNECT;
|
|
return 0;
|
|
}
|
|
|
|
if (actual)
|
|
*actual = result;
|
|
socket->lastError = result >= 0 ? CGE_NET_EOK : errnoToErrorCode(errno);
|
|
return result >= 0;
|
|
}
|
|
|
|
int CgeNetPeek(CgeNetSocket *socket, void *buffer, size_t size, size_t *actual) {
|
|
ssize_t result;
|
|
|
|
assert(socket != NULL);
|
|
assert(socket->handle != -1);
|
|
assert(buffer != NULL);
|
|
|
|
do {
|
|
result = recv(socket->handle, buffer, size, MSG_PEEK);
|
|
} while (result < 0 && errno == EINTR);
|
|
|
|
if (size > 0 && result == 0 && socket->type == CGE_NET_TCP) {
|
|
socket->lastError = CGE_NET_EDISCONNECT;
|
|
return 0;
|
|
}
|
|
|
|
if (actual)
|
|
*actual = result;
|
|
socket->lastError = result >= 0 ? CGE_NET_EOK : errnoToErrorCode(errno);
|
|
return result >= 0;
|
|
}
|
|
|
|
int CgeNetWriteTo(CgeNetSocket *socket, const CgeNetAddr *addr,
|
|
const void *buffer, size_t size, size_t *actual) {
|
|
struct sockaddr_in sa4;
|
|
struct sockaddr_in6 sa6;
|
|
const struct sockaddr *sa;
|
|
socklen_t salen;
|
|
ssize_t result;
|
|
|
|
assert(socket != NULL);
|
|
assert(socket->handle != -1);
|
|
assert(addr != NULL);
|
|
assert(addr->type == CGE_NET_IPV4 || addr->type == CGE_NET_IPV6);
|
|
assert(buffer != NULL);
|
|
|
|
if (socket->type != CGE_NET_UDP) {
|
|
socket->lastError = CGE_NET_EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if (addr->type == CGE_NET_IPV4) {
|
|
memset(&sa4, 0, sizeof(sa4));
|
|
sa4.sin_family = AF_INET;
|
|
sa4.sin_port = htons(addr->ipv4.port);
|
|
memcpy(&sa4.sin_addr, addr->ipv4.addr, 4);
|
|
sa = (struct sockaddr*)&sa4;
|
|
salen = sizeof(sa4);
|
|
} else {
|
|
memset(&sa6, 0, sizeof(sa6));
|
|
sa6.sin6_family = AF_INET6;
|
|
sa6.sin6_port = htons(addr->ipv6.port);
|
|
memcpy(&sa6.sin6_addr, addr->ipv6.addr, 16);
|
|
sa = (struct sockaddr*)&sa6;
|
|
salen = sizeof(sa6);
|
|
}
|
|
|
|
do {
|
|
result = sendto(socket->handle, buffer, size, 0, sa, salen);
|
|
} while (result < 0 && errno == EINTR);
|
|
|
|
if (actual)
|
|
*actual = result;
|
|
socket->lastError = result >= 0 ? CGE_NET_EOK : errnoToErrorCode(errno);
|
|
return result >= 0;
|
|
}
|
|
|
|
int CgeNetReadFrom(CgeNetSocket *socket, CgeNetAddr* addr, void *buffer,
|
|
size_t size, size_t *actual) {
|
|
struct sockaddr_storage ss;
|
|
socklen_t slen = sizeof(ss);
|
|
ssize_t result;
|
|
|
|
assert(socket != NULL);
|
|
assert(socket->handle != -1);
|
|
assert(addr != NULL);
|
|
assert(addr->type == CGE_NET_IPV4 || addr->type == CGE_NET_IPV6);
|
|
assert(buffer != NULL);
|
|
|
|
if (socket->type != CGE_NET_UDP) {
|
|
socket->lastError = CGE_NET_EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
do {
|
|
result = recvfrom(socket->handle, buffer, size, 0,
|
|
(struct sockaddr*)&ss, &slen);
|
|
} while (result < 0 && errno == EINTR);
|
|
|
|
if (result < 0) {
|
|
socket->lastError = errnoToErrorCode(errno);
|
|
return 0;
|
|
}
|
|
|
|
if (ss.ss_family == AF_INET) {
|
|
struct sockaddr_in *sa4 = (struct sockaddr_in*)&ss;
|
|
addr->ipv4.type = CGE_NET_IPV4;
|
|
memcpy(addr->ipv4.addr, &sa4->sin_addr, 4);
|
|
addr->ipv4.port = ntohs(sa4->sin_port);
|
|
} else {
|
|
struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)&ss;
|
|
addr->ipv6.type = CGE_NET_IPV6;
|
|
memcpy(addr->ipv6.addr, &sa6->sin6_addr, 16);
|
|
addr->ipv6.port = ntohs(sa6->sin6_port);
|
|
}
|
|
|
|
if (actual)
|
|
*actual = result;
|
|
return 1;
|
|
}
|
|
|
|
void *CgeNetContext(CgeNetSocket *socket) {
|
|
assert(socket != NULL);
|
|
return socket->context;
|
|
}
|
|
|
|
void CgeNetSetContext(CgeNetSocket *socket, void *data) {
|
|
assert(socket != NULL);
|
|
socket->context = data;
|
|
}
|