Synchronet now requires the libarchive development package (e.g. libarchive-dev on Debian-based Linux distros, libarchive.org for more info) to build successfully.

Commit 99f25324 authored by Deucе's avatar Deucе 👌🏾

Add three new socket status functions:

socket_readable() returns TRUE if recv() will not block if called
on the specified socket.

socket_writable() returns TRUE if send() will not block if called
on the specified socket.

socket_recvdone() returns TRUE if all data has been recv()ed an
the socket is closed.

"will not block" includes "will return an error".

This matches the three main use-cases for select() of a single socket
in Synchronet.

Use them in the SyncTERM RLogin stuff so they can be tested easily.
parent 1dc1a6b9
Pipeline #1580 passed with stage
in 9 minutes and 54 seconds
......@@ -17,7 +17,6 @@ SOCKET rlogin_sock=INVALID_SOCKET;
#endif
void rlogin_input_thread(void *args)
{
fd_set rds;
int rd;
int buffered;
size_t buffer;
......@@ -25,24 +24,7 @@ void rlogin_input_thread(void *args)
SetThreadName("RLogin Input");
conn_api.input_thread_running=1;
while(rlogin_sock != INVALID_SOCKET && !conn_api.terminate) {
FD_ZERO(&rds);
FD_SET(rlogin_sock, &rds);
#ifdef __linux__
{
struct timeval tv;
tv.tv_sec=0;
tv.tv_usec=500000;
rd=select(rlogin_sock+1, &rds, NULL, NULL, &tv);
}
#else
rd=select(rlogin_sock+1, &rds, NULL, NULL, NULL);
#endif
if(rd==-1) {
if(errno==EBADF)
break;
rd=0;
}
if(rd==1) {
if (socket_readable(rlogin_sock, -1)) {
rd=recv(rlogin_sock, conn_api.rd_buf, conn_api.rd_buf_size, 0);
if(rd <= 0)
break;
......@@ -79,24 +61,7 @@ void rlogin_output_thread(void *args)
pthread_mutex_unlock(&(conn_outbuf.mutex));
sent=0;
while(sent < wr) {
FD_ZERO(&wds);
FD_SET(rlogin_sock, &wds);
#ifdef __linux__
{
struct timeval tv;
tv.tv_sec=0;
tv.tv_usec=500000;
ret=select(rlogin_sock+1, NULL, &wds, NULL, &tv);
}
#else
ret=select(rlogin_sock+1, NULL, &wds, NULL, NULL);
#endif
if(ret==-1) {
if(errno==EBADF)
break;
ret=0;
}
if(ret==1) {
if (socket_writable(rlogin_sock, -1)) {
ret=sendsocket(rlogin_sock, conn_api.wr_buf+sent, wr-sent);
if(ret==-1)
break;
......@@ -173,22 +138,16 @@ int rlogin_connect(struct bbslist *bbs)
if(bbs->conn_type == CONN_TYPE_MBBS_GHOST) {
char sbuf[80];
char rbuf[10];
struct timeval tv;
int idx, ret;
fd_set rds;
FD_ZERO(&rds);
FD_SET(rlogin_sock, &rds);
tv.tv_sec=1;
tv.tv_usec=0;
/* Check to make sure GHost is actually listening */
sendsocket(rlogin_sock, "\r\nMBBS: PING\r\n", 14);
idx = 0;
while ((ret = select(rlogin_sock+1, &rds, NULL, NULL, &tv))==1) {
recv(rlogin_sock, rbuf+idx, 1, 0);
while (socket_readable(rlogin_sock, 1000)) {
ret = recv(rlogin_sock, rbuf+idx, 1, 0);
if (ret == -1)
break;
rbuf[++idx] = 0;
/* It says ERROR, but this is a good response to PING. */
......@@ -216,8 +175,10 @@ int rlogin_connect(struct bbslist *bbs)
sendsocket(rlogin_sock, sbuf, strlen(sbuf));
idx = 0;
while ((ret = select(rlogin_sock+1, &rds, NULL, NULL, &tv))==1) {
recv(rlogin_sock, rbuf+idx, 1, 0);
while (socket_readable(rlogin_sock, 1000)) {
ret = recv(rlogin_sock, rbuf+idx, 1, 0);
if (ret == -1)
break;
rbuf[++idx] = 0;
/* GHost says it's launching the program, so pass terminal to user. */
......
......@@ -47,28 +47,15 @@ void cryptlib_error_message(int status, const char * msg)
void ssh_input_thread(void *args)
{
fd_set rds;
int status;
int rd;
size_t buffered;
size_t buffer;
struct timeval tv;
SetThreadName("SSH Input");
conn_api.input_thread_running=1;
while(ssh_active && !conn_api.terminate) {
FD_ZERO(&rds);
FD_SET(ssh_sock, &rds);
tv.tv_sec = 0;
tv.tv_usec = 100;
rd=select(ssh_sock+1, &rds, NULL, NULL, &tv);
if(rd==-1) {
if(errno==EBADF)
break;
rd=0;
}
if(rd == 0)
if (!socket_readable(ssh_sock, 10))
continue;
pthread_mutex_lock(&ssh_mutex);
......
......@@ -329,6 +329,138 @@ BOOL socket_check(SOCKET sock, BOOL* rd_p, BOOL* wr_p, DWORD timeout)
return(FALSE);
}
/*
* Return TRUE if recv() will not block on socket
* Will block for timeout ms or forever if timeout is negative
*
* This means it will return true if recv() will return an error
* as well as if the socket is closed (and recv() will return 0)
*/
BOOL socket_readable(SOCKET sock, int timeout)
{
#ifdef _WIN32
fd_set rd_set;
struct timeval tv = {0};
struct timeval *tvp = &tv;
FD_ZERO(&rd_set);
FD_SET(sock, &rd_set);
if (timeout < 0)
tvp = NULL;
else {
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
}
switch (select(sock+1, &rd_set, NULL, NULL, tvp)) {
case 0: // Nothing to read
return FALSE;
case 1:
return TRUE;
}
// Errors and unexpected cases
return TRUE;
#else
struct pollfd pfd = {0};
pfd.fd = sock;
pfd.events = POLLIN;
if (poll(&pfd, 1, timeout) == 1)
return TRUE;
return FALSE;
#endif
}
/*
* Return TRUE if send() will not block on socket
* Will block for timeout ms or forever if timeout is negative
*
* This means it will return true if send() will return an error
* as well as if the socket is closed (and send() will return 0)
*/
BOOL socket_writable(SOCKET sock, int timeout)
{
#ifdef _WIN32
fd_set wr_set;
struct timeval tv = {0};
struct timeval *tvp = &tv;
FD_ZERO(&wr_set);
FD_SET(sock, &wr_set);
if (timeout < 0)
tvp = NULL;
else {
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
}
switch (select(sock+1, NULL, &wr_set, NULL, tvp)) {
case 0: // Nothing to read
return FALSE;
case 1:
return TRUE;
}
// Errors and unexpected cases
return TRUE;
#else
struct pollfd pfd = {0};
pfd.fd = sock;
pfd.events = POLLOUT;
if (poll(&pfd, 1, timeout) == 1)
return TRUE;
return FALSE;
#endif
}
/*
* Return TRUE if recv() will not block and will return zero
* or an error. This is *not* a test if a socket is
* disconnected, but rather that it is disconnected *AND* all
* data has been recv()ed.
*/
BOOL socket_recvdone(SOCKET sock, int timeout)
{
#ifdef _WIN32
fd_set rd_set;
struct timeval tv = {0};
struct timeval *tvp = &tv;
char ch;
int rd;
FD_ZERO(&rd_set);
FD_SET(sock, &rd_set);
if (timeout < 0)
tvp = NULL;
else {
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
}
switch (select(sock+1, &rd_set, NULL, NULL, tvp)) {
case -1: // Error, call this disconnected
return TRUE;
case 0: // Nothing to read
return FALSE;
}
rd = recv(sock,&ch,1,MSG_PEEK);
if (rd == 1 || (rd==SOCKET_ERROR && ERROR_VALUE==EMSGSIZE))
return FALSE;
return TRUE;
#else
struct pollfd pfd = {0};
pfd.fd = sock;
pfd.events = POLLIN;
if (poll(&pfd, 1, timeout) == 1) {
if (pfd.revents & POLLIN)
return FALSE;
return TRUE;
}
return FALSE;
#endif
}
int retry_bind(SOCKET s, const struct sockaddr *addr, socklen_t addrlen
,uint retries, uint wait_secs
,const char* prot
......
......@@ -232,6 +232,32 @@ DLLEXPORT int xp_inet_pton(int af, const char *src, void *dst);
#define inet_pton xp_inet_pton
#endif
/*
* Return TRUE if recv() will not block on socket
* Will block for timeout ms or forever if timeout is negative
*
* This means it will return true if recv() will return an error
* as well as if the socket is closed (and recv() will return 0)
*/
DLLEXPORT BOOL socket_readable(SOCKET sock, int timeout);
/*
* Return TRUE if send() will not block on socket
* Will block for timeout ms or forever if timeout is negative
*
* This means it will return true if send() will return an error
* as well as if the socket is closed (and send() will return 0)
*/
DLLEXPORT BOOL socket_writable(SOCKET sock, int timeout);
/*
* Return TRUE if recv() will not block and will return zero
* or an error. This is *not* a test if a socket is
* disconnected, but rather that it is disconnected *AND* all
* data has been recv()ed.
*/
DLLEXPORT BOOL socket_recvdone(SOCKET sock, int timeout);
#ifdef __cplusplus
}
#endif
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment