Skip to content
Snippets Groups Projects
Commit 7d9e7b0c authored by Deucе's avatar Deucе :ok_hand_tone4:
Browse files

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 31c717f9
No related branches found
No related tags found
No related merge requests found
......@@ -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
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment