#include "../bsocket.h"
#include "../osbs.inl"
#include <sewer/cassert.h>
#include <sewer/ptr.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <netdb.h>
#include <fcntl.h>
#define SOCKET_ID int
#define SOCKET_NULL -1
#define SOCKET_FAIL -1
#define RECV_SHUTDOWN SHUT_RD
#define ALL_SHUTDOWN SHUT_RDWR
#define SIZE_T size_t
#define SSIZE_T ssize_t
static const char_t *i_WELL_KNOW_URL = "www.google.com";
void _osbs_start_sockets(void)
{
}
void _osbs_finish_sockets(void)
{
}
static serror_t i_socket_error(void)
{
int sock_error = errno;
switch (sock_error)
{
case ETIMEDOUT:
return ekSTIMEOUT;
}
if (bsocket_url_ip(i_WELL_KNOW_URL, NULL) == 0)
return ekSNONET;
else
return ekSNOHOST;
}
Socket *bsocket_connect(const uint32_t ip, const uint16_t port, const uint32_t timeout_ms, serror_t *error)
{
SOCKET_ID skID = SOCKET_NULL;
int ok_connect = 0;
skID = socket(PF_INET, SOCK_STREAM, 0);
if (skID == SOCKET_FAIL)
{
if (error != NULL)
*error = i_socket_error();
return NULL;
}
if (skID != SOCKET_FAIL)
{
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = htonl(ip);
if (timeout_ms == 0)
{
ok_connect = connect(skID, cast(&server, struct sockaddr), sizeof(server));
if (ok_connect == SOCKET_FAIL && error != NULL)
*error = i_socket_error();
}
else
{
long arg = fcntl(skID, F_GETFL, NULL);
if (arg < 0)
{
ok_connect = SOCKET_FAIL;
if (error != NULL)
*error = ekSUNDEF;
}
if (ok_connect != SOCKET_FAIL)
{
arg |= O_NONBLOCK;
if (fcntl(skID, F_SETFL, arg) < 0)
{
ok_connect = SOCKET_FAIL;
if (error != NULL)
*error = ekSUNDEF;
}
}
if (ok_connect != SOCKET_FAIL)
{
if (connect(skID, cast(&server, struct sockaddr), sizeof(server)) == SOCKET_FAIL)
{
if (errno == EINPROGRESS)
{
fd_set setW;
struct timeval time_out = {0, 0};
int ret = 0;
FD_ZERO(&setW);
FD_SET(skID, &setW);
time_out.tv_sec = timeout_ms / 1000;
time_out.tv_usec = (timeout_ms % 1000) * 1000;
ret = select(skID + 1, NULL, &setW, NULL, &time_out);
if (ret > 0)
{
if (FD_ISSET(skID, &setW))
{
int err = 0;
socklen_t errlen = sizeof(err);
getsockopt(skID, SOL_SOCKET, SO_ERROR, cast(&err, void), &errlen);
if (err != 0)
{
errno = err;
ok_connect = SOCKET_FAIL;
if (error != NULL)
*error = i_socket_error();
}
}
}
else
{
ok_connect = SOCKET_FAIL;
if (ret == 0)
{
errno = ETIMEDOUT;
if (error != NULL)
*error = i_socket_error();
}
}
}
else
{
ok_connect = SOCKET_FAIL;
}
}
}
}
}
if (ok_connect != SOCKET_FAIL && timeout_ms > 0)
{
long arg = fcntl(skID, F_GETFL, NULL);
if (arg < 0)
{
ok_connect = SOCKET_FAIL;
if (error != NULL)
*error = ekSUNDEF;
}
if (ok_connect != SOCKET_FAIL)
{
arg &= (~O_NONBLOCK);
if (fcntl(skID, F_SETFL, arg) < 0)
{
ok_connect = SOCKET_FAIL;
if (error != NULL)
*error = ekSUNDEF;
}
}
}
if (ok_connect != SOCKET_FAIL)
{
ptr_assign(error, ekSOK);
_osbs_socket_alloc();
return cast((intptr_t)skID, Socket);
}
else
{
if (skID != SOCKET_FAIL)
close(skID);
return NULL;
}
}
Socket *bsocket_server(const uint16_t port, const uint32_t max_connect, serror_t *error)
{
struct sockaddr_in server;
SOCKET_ID skID;
int ok;
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = INADDR_ANY;
skID = socket(PF_INET, SOCK_STREAM, 0);
if (skID == -1)
{
if (error != NULL)
*error = i_socket_error();
return NULL;
}
{
int reuseaddr = 1;
int sok = SOCKET_FAIL;
sok = setsockopt(skID, SOL_SOCKET, SO_REUSEADDR, cast_const(&reuseaddr, char), sizeof(reuseaddr));
cassert_unref(sok == 0, sok);
}
ok = bind(skID, cast(&server, struct sockaddr), sizeof(server));
if (ok == SOCKET_FAIL)
{
close(skID);
if (error != NULL)
*error = i_socket_error();
return NULL;
}
ok = listen(skID, (int)max_connect);
if (ok == SOCKET_FAIL)
{
close(skID);
if (error != NULL)
i_socket_error();
return NULL;
}
_osbs_socket_alloc();
return cast((intptr_t)skID, Socket);
}
Socket *bsocket_accept(Socket *lsocket, const uint32_t timeout_ms, serror_t *error)
{
SOCKET_ID lsockid = 0;
fd_set set;
int select_id;
SOCKET_ID cliID;
struct sockaddr_in clData;
socklen_t sizeSt;
cassert_no_null(lsocket);
lsockid = (SOCKET_ID)(intptr_t)lsocket;
FD_ZERO(&set);
FD_SET(lsockid, &set);
if (timeout_ms > 0)
{
struct timeval timeout;
timeout.tv_sec = timeout_ms / 1000;
timeout.tv_usec = (timeout_ms % 1000) * 1000;
select_id = select((int)lsockid + 1, &set, NULL, NULL, &timeout);
}
else
{
select_id = select((int)lsockid + 1, &set, NULL, NULL, NULL);
}
if (select_id == SOCKET_FAIL)
{
ptr_assign(error, ekSUNDEF);
return NULL;
}
if (select_id == 0)
{
ptr_assign(error, ekSTIMEOUT);
return NULL;
}
sizeSt = sizeof(clData);
cliID = accept((SOCKET_ID)(intptr_t)lsocket, cast(&clData, struct sockaddr), &sizeSt);
if (cliID == SOCKET_FAIL)
{
if (error != NULL)
i_socket_error();
return NULL;
}
_osbs_socket_alloc();
ptr_assign(error, ekSOK);
return cast((intptr_t)cliID, Socket);
}
void bsocket_close(Socket **lsocket)
{
int ok;
cassert_no_null(lsocket);
cassert_no_null(*lsocket);
shutdown((SOCKET_ID)(intptr_t)(*lsocket), ALL_SHUTDOWN);
ok = close((SOCKET_ID)(intptr_t)(*lsocket));
cassert_unref(ok == 0, ok);
_osbs_socket_dealloc();
*lsocket = NULL;
}
void bsocket_local_ip(Socket *lsocket, uint32_t *ip, uint16_t *port)
{
struct sockaddr_in laddress;
socklen_t addr_size = sizeof(laddress);
cassert_no_null(lsocket);
cassert(ip != NULL || port != NULL);
if (getsockname((SOCKET_ID)(intptr_t)lsocket, cast(&laddress, struct sockaddr), &addr_size) != -1)
{
ptr_assign(ip, ntohl(laddress.sin_addr.s_addr));
ptr_assign(port, ntohs(laddress.sin_port));
}
else
{
ptr_assign(ip, 0);
ptr_assign(port, 0);
}
}
void bsocket_remote_ip(Socket *lsocket, uint32_t *ip, uint16_t *port)
{
struct sockaddr_in laddress;
socklen_t addr_size = sizeof(laddress);
cassert_no_null(lsocket);
cassert(ip != NULL || port != NULL);
if (getpeername((SOCKET_ID)(intptr_t)lsocket, cast(&laddress, struct sockaddr), &addr_size) != -1)
{
ptr_assign(ip, ntohl(laddress.sin_addr.s_addr));
ptr_assign(port, ntohs(laddress.sin_port));
}
else
{
ptr_assign(ip, 0);
ptr_assign(port, 0);
}
}
void bsocket_read_timeout(Socket *sock, const uint32_t timeout_ms)
{
struct timeval timeout;
int ret = 0;
timeout.tv_sec = timeout_ms / 1000;
timeout.tv_usec = (timeout_ms % 1000) * 1000;
ret = setsockopt((SOCKET_ID)(intptr_t)sock, SOL_SOCKET, SO_RCVTIMEO, cast_const(&timeout, char), sizeof(timeout));
cassert_unref(ret == 0, ret);
}
void bsocket_write_timeout(Socket *sock, const uint32_t timeout_ms)
{
struct timeval timeout;
int ret = 0;
timeout.tv_sec = timeout_ms / 1000;
timeout.tv_usec = (timeout_ms % 1000) * 1000;
ret = setsockopt((SOCKET_ID)(intptr_t)sock, SOL_SOCKET, SO_SNDTIMEO, cast_const(&timeout, char), sizeof(timeout));
cassert_unref(ret == 0, ret);
}
bool_t bsocket_read(Socket *lsocket, byte_t *data, const uint32_t size, uint32_t *rsize, serror_t *error)
{
SSIZE_T lrsize = 0;
serror_t lerror = ekSOK;
cassert_no_null(lsocket);
cassert_no_null(data);
for (;;)
{
SSIZE_T num_rbytes = 0;
cassert((int)size > lrsize);
num_rbytes = recv((SOCKET_ID)(intptr_t)lsocket, (char *)data, (SIZE_T)((long)size - (long)lrsize), 0);
if (num_rbytes > 0)
{
lrsize += num_rbytes;
if ((uint32_t)lrsize == size)
{
lerror = ekSOK;
break;
}
data += num_rbytes;
}
else if (num_rbytes == 0)
{
lerror = ekSOK;
break;
}
else if (num_rbytes == SOCKET_FAIL)
{
int sock_error = errno;
if (sock_error == ETIMEDOUT)
lerror = ekSTIMEOUT;
else
lerror = ekSSTREAM;
break;
}
else
{
lerror = ekSSTREAM;
break;
}
}
ptr_assign(rsize, (uint32_t)lrsize);
ptr_assign(error, lerror);
return (bool_t)(lerror == ekSOK);
}
bool_t bsocket_write(Socket *lsocket, const byte_t *data, const uint32_t size, uint32_t *wsize, serror_t *error)
{
SSIZE_T lwsize = 0;
bool_t ok = FALSE;
cassert_no_null(lsocket);
cassert_no_null(data);
for (;;)
{
SSIZE_T num_wbytes = 0;
cassert((int)size > lwsize);
num_wbytes = send((SOCKET_ID)(intptr_t)lsocket, cast_const(data, char), (SIZE_T)((long)size - (long)lwsize), 0);
if (num_wbytes > 0)
{
lwsize += num_wbytes;
if (lwsize == (int)size)
{
ok = TRUE;
break;
}
data += num_wbytes;
}
else if (num_wbytes == 0)
{
cassert(FALSE);
ok = TRUE;
break;
}
else if (num_wbytes == SOCKET_FAIL)
{
ok = FALSE;
break;
}
else
{
cassert(FALSE);
ok = FALSE;
break;
}
}
ptr_assign(wsize, (uint32_t)lwsize);
ptr_assign(error, ok == TRUE ? ekSOK : ekSSTREAM);
return ok;
}
uint32_t bsocket_url_ip(const char_t *url, serror_t *error)
{
struct hostent *host = NULL;
cassert_no_null(url);
#if defined(__WINDOWS__)
#pragma warning(disable : 4996)
host = gethostbyname(cast_const(url, char));
#pragma warning(default : 4996)
#else
host = gethostbyname(cast_const(url, char));
#endif
if (host != NULL)
{
struct in_addr **addr_list = dcast(host->h_addr_list, struct in_addr);
if (addr_list[0] != NULL)
{
ptr_assign(error, ekSOK);
return ntohl(addr_list[0]->s_addr);
}
}
if (error != NULL)
{
if (bsocket_url_ip(i_WELL_KNOW_URL, NULL) == 0)
*error = ekSNONET;
else
*error = ekSNOHOST;
}
return 0;
}