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 af30c430 authored by Deucе's avatar Deucе 👌🏾

Initial poll() work

Still needs updates in services_thread(), CGI stuff in websrvr.c,
and sbbs_t::external()
parent 9048b3d2
......@@ -643,15 +643,12 @@ bool sbbs_t::ftp_get(csi_t* csi, SOCKET ctrl_sock, char* src, char* dest, bool d
char rsp[512];
char buf[4097];
int rd;
int result;
BOOL data_avail;
ulong total=0;
SOCKET data_sock;
union xp_sockaddr addr;
socklen_t addr_len;
FILE* fp=NULL;
struct timeval tv;
fd_set socket_set;
if((data_sock=ftp_data_sock(csi, ctrl_sock, &addr.in))==INVALID_SOCKET)
return(false);
......@@ -684,16 +681,7 @@ bool sbbs_t::ftp_get(csi_t* csi, SOCKET ctrl_sock, char* src, char* dest, bool d
}
if(!(csi->ftp_mode&CS_FTP_PASV)) { /* Normal (Active) FTP */
/* Setup for select() */
tv.tv_sec=TIMEOUT_SOCK_LISTEN;
tv.tv_usec=0;
FD_ZERO(&socket_set);
FD_SET(data_sock,&socket_set);
result=select(data_sock+1,&socket_set,NULL,NULL,&tv);
if(result<1) {
if(!socket_readable(data_sock, TIMEOUT_SOCK_LISTEN * 1000)) {
csi->socket_error=ERROR_VALUE;
closesocket(data_sock);
return(false);
......@@ -770,15 +758,12 @@ bool sbbs_t::ftp_put(csi_t* csi, SOCKET ctrl_sock, char* src, char* dest)
char path[MAX_PATH+1];
char buf[4097];
int rd;
int result;
ulong total=0;
SOCKET data_sock;
union xp_sockaddr addr;
socklen_t addr_len;
FILE* fp=NULL;
bool error=false;
struct timeval tv;
fd_set socket_set;
SAFECOPY(path,src);
......@@ -823,16 +808,7 @@ bool sbbs_t::ftp_put(csi_t* csi, SOCKET ctrl_sock, char* src, char* dest)
}
if(!(csi->ftp_mode&CS_FTP_PASV)) { /* Normal (Active) FTP */
/* Setup for select() */
tv.tv_sec=TIMEOUT_SOCK_LISTEN;
tv.tv_usec=0;
FD_ZERO(&socket_set);
FD_SET(data_sock,&socket_set);
result=select(data_sock+1,&socket_set,NULL,NULL,&tv);
if(result<1) {
if(!socket_readable(data_sock, TIMEOUT_SOCK_LISTEN * 1000)) {
csi->socket_error=ERROR_VALUE;
closesocket(data_sock);
fclose(fp);
......
......@@ -310,8 +310,6 @@ static int sockprintf(SOCKET sock, CRYPT_SESSION sess, char *fmt, ...)
int result;
va_list argptr;
char sbuf[1024];
fd_set socket_set;
struct timeval tv;
char *estr;
va_start(argptr,fmt);
......@@ -330,20 +328,9 @@ static int sockprintf(SOCKET sock, CRYPT_SESSION sess, char *fmt, ...)
return(0);
}
/* Check socket for writability (using select) */
tv.tv_sec=300;
tv.tv_usec=0;
FD_ZERO(&socket_set);
FD_SET(sock,&socket_set);
if((result=select(sock+1,NULL,&socket_set,NULL,&tv))<1) {
if(result==0)
lprintf(LOG_WARNING,"%04d !TIMEOUT selecting socket for send"
,sock);
else
lprintf(LOG_WARNING,"%04d !ERROR %d selecting socket for send"
,sock, ERROR_VALUE);
/* Check socket for writability */
if(!socket_writable(sock, 300000)) {
lprintf(LOG_WARNING,"%04d !WARNING socket not ready for write" ,sock);
return(0);
}
......@@ -458,8 +445,6 @@ void recverror(SOCKET socket, int rd, int line)
static int sock_recvbyte(SOCKET sock, CRYPT_SESSION sess, char *buf, time_t *lastactive)
{
int len=0;
fd_set socket_set;
struct timeval tv;
int ret;
int i;
char *estr;
......@@ -476,7 +461,7 @@ static int sock_recvbyte(SOCKET sock, CRYPT_SESSION sess, char *buf, time_t *las
GCES(ret, sock, sess, estr, "setting read timeout");
while (1) {
ret = cryptPopData(sess, buf, 1, &len);
/* Successive reads will be with the full timeout after a select() */
/* Successive reads will be with the full timeout after a socket_readable() */
cryptSetAttribute(sess, CRYPT_OPTION_NET_READTIMEOUT, startup->max_inactivity);
switch(ret) {
case CRYPT_OK:
......@@ -506,50 +491,26 @@ static int sock_recvbyte(SOCKET sock, CRYPT_SESSION sess, char *buf, time_t *las
return(0);
}
tv.tv_sec=startup->max_inactivity;
tv.tv_usec=0;
FD_ZERO(&socket_set);
FD_SET(sock,&socket_set);
i=select(sock+1,&socket_set,NULL,NULL,&tv);
if(i<1) {
if(i==0) {
if((time(NULL)-(*lastactive))>startup->max_inactivity) {
lprintf(LOG_WARNING,"%04d Disconnecting due to to inactivity",sock);
sockprintf(sock,sess,"421 Disconnecting due to inactivity (%u seconds)."
,startup->max_inactivity);
return(0);
}
continue;
if (!socket_readable(sock, startup->max_inactivity * 1000)) {
if((time(NULL)-(*lastactive))>startup->max_inactivity) {
lprintf(LOG_WARNING,"%04d Disconnecting due to to inactivity",sock);
sockprintf(sock,sess,"421 Disconnecting due to inactivity (%u seconds)."
,startup->max_inactivity);
return(0);
}
recverror(sock,i,__LINE__);
return(i);
}
}
}
else {
while (1) {
tv.tv_sec=startup->max_inactivity;
tv.tv_usec=0;
FD_ZERO(&socket_set);
FD_SET(sock,&socket_set);
i=select(sock+1,&socket_set,NULL,NULL,&tv);
if(i<1) {
if(i==0) {
if((time(NULL)-(*lastactive))>startup->max_inactivity) {
lprintf(LOG_WARNING,"%04d Disconnecting due to to inactivity",sock);
sockprintf(sock,sess,"421 Disconnecting due to inactivity (%u seconds)."
,startup->max_inactivity);
return(0);
}
continue;
if (!socket_readable(sock, startup->max_inactivity * 1000)) {
if((time(NULL)-(*lastactive))>startup->max_inactivity) {
lprintf(LOG_WARNING,"%04d Disconnecting due to to inactivity",sock);
sockprintf(sock,sess,"421 Disconnecting due to inactivity (%u seconds)."
,startup->max_inactivity);
return(0);
}
return(i);
continue;
}
#ifdef SOCKET_DEBUG_RECV_CHAR
socket_debug[sock]|=SOCKET_DEBUG_RECV_CHAR;
......@@ -661,8 +622,6 @@ static void send_thread(void* arg)
user_t uploader;
union xp_sockaddr addr;
socklen_t addr_len;
fd_set socket_set;
struct timeval tv;
char *estr;
xfer=*(xfer_t*)arg;
......@@ -742,22 +701,8 @@ static void send_thread(void* arg)
break;
}
/* Check socket for writability (using select) */
tv.tv_sec=1;
tv.tv_usec=0;
FD_ZERO(&socket_set);
FD_SET(*xfer.data_sock,&socket_set);
i=select((*xfer.data_sock)+1,NULL,&socket_set,NULL,&tv);
if(i==SOCKET_ERROR) {
lprintf(LOG_WARNING,"%04d <%s> !DATA ERROR %d selecting socket %d for send"
,xfer.ctrl_sock, xfer.user->alias, ERROR_VALUE, *xfer.data_sock);
sockprintf(xfer.ctrl_sock,xfer.ctrl_sess,"426 Transfer error.");
error=TRUE;
break;
}
if(i<1)
/* Check socket for writability */
if (!socket_writable(*xfer.data_sock, 1000))
continue;
(void)fseek(fp,xfer.filepos+total,SEEK_SET);
......@@ -954,8 +899,6 @@ static void receive_thread(void* arg)
time_t now;
time_t start;
time_t last_report;
fd_set socket_set;
struct timeval tv;
CRYPT_SESSION sess = -1;
char *estr;
......@@ -1029,22 +972,8 @@ static void receive_thread(void* arg)
break;
}
/* Check socket for readability (using select) */
tv.tv_sec=1;
tv.tv_usec=0;
FD_ZERO(&socket_set);
FD_SET(*xfer.data_sock,&socket_set);
i=select((*xfer.data_sock)+1,&socket_set,NULL,NULL,&tv);
if(i==SOCKET_ERROR) {
lprintf(LOG_WARNING,"%04d <%s> !DATA ERROR %d selecting socket %d for receive"
,xfer.ctrl_sock, xfer.user->alias, ERROR_VALUE, *xfer.data_sock);
sockprintf(xfer.ctrl_sock,sess,"426 Transfer error.");
error=TRUE;
break;
}
if(i<1)
/* Check socket for readability */
if (!socket_readable(*xfer.data_sock, 1000))
continue;
#if defined(SOCKET_DEBUG_RECV_BUF)
......@@ -1333,8 +1262,6 @@ static void filexfer(union xp_sockaddr* addr, SOCKET ctrl_sock, CRYPT_SESSION ct
union xp_sockaddr server_addr;
BOOL reuseaddr;
xfer_t* xfer;
struct timeval tv;
fd_set socket_set;
char host_ip[INET6_ADDRSTRLEN];
if((*inprogress)==TRUE) {
......@@ -1434,23 +1361,9 @@ static void filexfer(union xp_sockaddr* addr, SOCKET ctrl_sock, CRYPT_SESSION ct
,ctrl_sock, user->alias,pasv_sock,host_ip,inet_addrport(addr));
}
/* Setup for select() */
tv.tv_sec=TIMEOUT_SOCKET_LISTEN;
tv.tv_usec=0;
FD_ZERO(&socket_set);
FD_SET(pasv_sock,&socket_set);
#if defined(SOCKET_DEBUG_SELECT)
socket_debug[ctrl_sock]|=SOCKET_DEBUG_SELECT;
#endif
result=select(pasv_sock+1,&socket_set,NULL,NULL,&tv);
#if defined(SOCKET_DEBUG_SELECT)
socket_debug[ctrl_sock]&=~SOCKET_DEBUG_SELECT;
#endif
if(result<1) {
lprintf(LOG_WARNING,"%04d <%s> PASV !DATA select returned %d (error: %d)"
,ctrl_sock, user->alias,result,ERROR_VALUE);
if (!socket_readable(pasv_sock, TIMEOUT_SOCKET_LISTEN * 1000)) {
lprintf(LOG_WARNING,"%04d <%s> PASV !WARNING socket not readable"
,ctrl_sock, user->alias);
sockprintf(ctrl_sock,ctrl_sess,"425 Error %d selecting socket for connection",ERROR_VALUE);
if(tmpfile && !(startup->options&FTP_OPT_KEEP_TEMP_FILES))
(void)ftp_remove(ctrl_sock, __LINE__, filename, user->alias);
......
......@@ -42,14 +42,11 @@ BOOL identify(union xp_sockaddr *client_addr, u_short local_port, char* buf
,size_t maxlen, int timeout)
{
char req[128];
int i;
int result;
int rd;
ulong val;
SOCKET sock=INVALID_SOCKET;
union xp_sockaddr addr;
struct timeval tv;
fd_set socket_set;
BOOL success=FALSE;
if(client_addr->addr.sa_family != AF_INET && client_addr->addr.sa_family != AF_INET6)
......@@ -73,12 +70,7 @@ BOOL identify(union xp_sockaddr *client_addr, u_short local_port, char* buf
if(result==SOCKET_ERROR
&& (ERROR_VALUE==EWOULDBLOCK || ERROR_VALUE==EINPROGRESS)) {
tv.tv_sec=timeout;
tv.tv_usec=0;
FD_ZERO(&socket_set);
FD_SET(sock,&socket_set);
if(select(sock+1,NULL,&socket_set,NULL,&tv)==1)
if (socket_writable(sock, timeout * 1000))
result=0; /* success */
}
if(result!=0) {
......@@ -89,14 +81,7 @@ BOOL identify(union xp_sockaddr *client_addr, u_short local_port, char* buf
val=0;
ioctlsocket(sock,FIONBIO,&val);
tv.tv_sec=10;
tv.tv_usec=0;
FD_ZERO(&socket_set);
FD_SET(sock,&socket_set);
i=select(sock+1,NULL,&socket_set,NULL,&tv);
if(i<1) {
if(!socket_writable(sock, 10000)) {
sprintf(buf,"ERROR %d selecting socket for send",ERROR_VALUE);
break;
}
......@@ -107,14 +92,7 @@ BOOL identify(union xp_sockaddr *client_addr, u_short local_port, char* buf
break;
}
tv.tv_sec=10;
tv.tv_usec=0;
FD_ZERO(&socket_set);
FD_SET(sock,&socket_set);
i=select(sock+1,&socket_set,NULL,NULL,&tv);
if(i<1) {
if(!socket_readable(sock, 10000)) {
sprintf(buf,"ERROR %d detecting response",ERROR_VALUE);
break;
}
......
......@@ -287,10 +287,6 @@ js_raw_pollin(JSContext *cx, uintN argc, jsval *arglist)
private_t* p;
jsrefcount rc;
int32 timeout = -1;
#ifdef __unix__
fd_set rd;
struct timeval tv = {0, 0};
#endif
if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_file_class))==NULL) {
return(JS_FALSE);
......@@ -307,13 +303,11 @@ js_raw_pollin(JSContext *cx, uintN argc, jsval *arglist)
JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(FALSE));
rc=JS_SUSPENDREQUEST(cx);
#ifdef __unix__
if (timeout >= 0) {
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout%1000)*1000;
}
FD_ZERO(&rd);
FD_SET(fileno(p->fp), &rd);
if (select(fileno(p->fp)+1, &rd, NULL, NULL, timeout < 0 ? NULL : &tv) == 1)
/*
* TODO: macOS poll() page has the ominous statement that "The poll() system call currently does not support devices."
* But, since we don't support OS X in Synchronet that's likely OK?
*/
if (socket_readable(fileno(p->fp), timeout))
JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(TRUE));
#else
while(timeout) {
......
......@@ -3688,12 +3688,21 @@ js_socket_select(JSContext *cx, uintN argc, jsval *arglist)
JSObject* robj;
JSObject* rarray;
BOOL poll_for_write=FALSE;
#ifdef _WIN32
fd_set socket_set[3];
fd_set* sets[3] = {NULL, NULL, NULL};
uintN argn;
SOCKET sock;
SOCKET maxsock=0;
struct timeval tv = {0, 0};
SOCKET sock;
#else
struct pollfd *fds;
int poll_timeout = 0;
nfds_t nfds;
short events;
int scount;
int k;
#endif
uintN argn;
jsuint i;
jsuint j;
jsuint limit[3];
......@@ -3710,8 +3719,13 @@ js_socket_select(JSContext *cx, uintN argc, jsval *arglist)
poll_for_write=JSVAL_TO_BOOLEAN(argv[argn]);
else if(JSVAL_IS_OBJECT(argv[argn]))
inarray[inarray_cnt++] = JSVAL_TO_OBJECT(argv[argn]);
#ifdef _WIN32
else if(JSVAL_IS_NUMBER(argv[argn]))
js_timeval(cx,argv[argn],&tv);
#else
else if(JSVAL_IS_NUMBER(argv[argn]))
poll_timeout = js_polltimeout(cx, argv[argn]);
#endif
}
if(inarray_cnt == 0)
......@@ -3732,6 +3746,7 @@ js_socket_select(JSContext *cx, uintN argc, jsval *arglist)
/* Return array */
if((robj = JS_NewArrayObject(cx, 0, NULL))==NULL)
return(JS_FALSE);
#ifdef _WIN32
FD_ZERO(&socket_set[0]);
if(poll_for_write)
sets[1]=&socket_set[0];
......@@ -3766,6 +3781,56 @@ js_socket_select(JSContext *cx, uintN argc, jsval *arglist)
JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(robj));
}
#else
// First, count the number of sockets...
nfds = 0;
for (i = 0; i < limit[0]; i++) {
if(!JS_GetElement(cx, inarray[0], i, &val))
break;
nfds += js_socket_numsocks(cx, val);
}
if (nfds == 0)
return JS_TRUE;
fds = calloc(nfds, sizeof(*fds));
if (fds == NULL) {
JS_ReportError(cx, "Error allocating %d elements of %lu bytes at %s:%d"
, nfds, sizeof(*fds), getfname(__FILE__), __LINE__);
return JS_FALSE;
}
nfds = 0;
for (i = 0; i < limit[0]; i++) {
if(!JS_GetElement(cx, inarray[0], i, &val))
break;
nfds += js_socket_add(cx, val, &fds[nfds], poll_for_write ? POLLOUT : POLLIN);
}
rc = JS_SUSPENDREQUEST(cx);
if (poll(fds, nfds, poll_timeout) >= 0) {
nfds = 0;
for (i = 0; i < limit[0]; i++) {
if(!JS_GetElement(cx, inarray[0], i, &val))
break;
scount = js_socket_numsocks(cx, val);
for (k = 0; k < scount; k++) {
if (fds[nfds + k].revents & (poll_for_write ? POLLOUT : POLLIN | POLLHUP)) {
val=INT_TO_JSVAL(i);
JS_RESUMEREQUEST(cx, rc);
if(!JS_SetElement(cx, robj, len++, &val)) {
rc=JS_SUSPENDREQUEST(cx);
break;
}
rc=JS_SUSPENDREQUEST(cx);
break;
}
}
nfds += scount;
}
JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(robj));
}
free(fds);
#endif
JS_RESUMEREQUEST(cx, rc);
return(JS_TRUE);
......@@ -3774,6 +3839,7 @@ js_socket_select(JSContext *cx, uintN argc, jsval *arglist)
/* Return object */
if((robj = JS_NewObject(cx, NULL, NULL, NULL))==NULL)
return(JS_FALSE);
#ifdef _WIN32
for (j = 0; j < inarray_cnt; j++) {
if (limit[j] > 0) {
FD_ZERO(&socket_set[j]);
......@@ -3823,6 +3889,122 @@ js_socket_select(JSContext *cx, uintN argc, jsval *arglist)
}
JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(robj));
}
#else
/*
* So, we need to collapse all the FDs into a list with flags set...
* readfd corresponds to POLLIN
* writefd corresponds to POLLOUT
* and exceptfd corresponds to POLLPRI
*
* We don't want duplicates, and we don't want to set things that
* weren't requested.
*
* Brute-force method is to add them all to an array, sort the array,
* then remove duplicates after ORing the events together.
*
* NOTE that some of the socket objects may actually be duplicates
* themselves, and all of them should set with the appropriate status.
*
* Alternatively, we can just add them all in-order and pass a larger
* array to poll(). This will certainly be more efficient when
* generating the returned arrays.
*/
nfds = 0;
for (j = 0; j < inarray_cnt; j++) {
if (limit[j] > 0) {
for (i = 0; i < limit[j]; i++) {
if(!JS_GetElement(cx, inarray[0], i, &val))
break;
nfds += js_socket_numsocks(cx, val);
}
}
}
if (nfds == 0)
return JS_TRUE;
fds = calloc(nfds, sizeof(*fds));
if (fds == NULL) {
JS_ReportError(cx, "Error allocating %d elements of %lu bytes at %s:%d"
, nfds, sizeof(*fds), getfname(__FILE__), __LINE__);
return JS_FALSE;
}
nfds = 0;
for (j = 0; j < inarray_cnt; j++) {
if (limit[j] > 0) {
switch (j) {
case 0:
events = POLLIN;
break;
case 1:
events = POLLOUT;
break;
case 2:
events = POLLPRI;
break;
}
for (i = 0; i < limit[j]; i++) {
if(!JS_GetElement(cx, inarray[j], i, &val))
break;
nfds += js_socket_add(cx, val, &fds[nfds], poll_for_write ? POLLOUT : POLLIN);
}
}
}
rc = JS_SUSPENDREQUEST(cx);
if (poll(fds, nfds, poll_timeout) >= 0) {
nfds = 0;
for (j = 0; j < inarray_cnt; j++) {
if (limit[j] > 0) {
switch (j) {
case 0:
events = POLLIN | POLLHUP;
break;
case 1:
events = POLLOUT;
break;
case 2:
events = POLLPRI;
break;
}
len = 0;
JS_RESUMEREQUEST(cx, rc);
if((rarray = JS_NewArrayObject(cx, 0, NULL))==NULL) {
free(fds);
return(JS_FALSE);
}
val = OBJECT_TO_JSVAL(rarray);
if (!JS_SetProperty(cx, robj, props[j], &val)) {
free(fds);
return JS_FALSE;
}
rc=JS_SUSPENDREQUEST(cx);
for(i=0;i<limit[j];i++) {
JS_RESUMEREQUEST(cx, rc);
if(!JS_GetElement(cx, inarray[j], i, &val)) {
rc=JS_SUSPENDREQUEST(cx);
break;
}
rc=JS_SUSPENDREQUEST(cx);
scount = js_socket_numsocks(cx, val);
for (k = 0; k < scount; k++) {
if(fds[nfds + k].revents & (events | POLLERR | POLLNVAL)) {
val=INT_TO_JSVAL(i);
JS_RESUMEREQUEST(cx, rc);
if(!JS_SetElement(cx, rarray, len++, &val)) {
rc=JS_SUSPENDREQUEST(cx);
break;
}
rc=JS_SUSPENDREQUEST(cx);
}
}
nfds += scount;
}
}
}
JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(robj));
}
free(fds);
#endif
JS_RESUMEREQUEST(cx, rc);
return(JS_TRUE);
......
......@@ -196,8 +196,6 @@ static ptrdiff_t js_socket_recv(js_socket_private_t *p, void *buf, size_t len, i
{
ptrdiff_t total=0;
int copied,ret;
fd_set socket_set;
struct timeval tv = {0, 0};
char *estr;
time_t now = time(NULL);
int status;
......@@ -227,10 +225,8 @@ static ptrdiff_t js_socket_recv(js_socket_private_t *p, void *buf, size_t len, i
if (p->sock == INVALID_SOCKET)
ret = -1;
else {
FD_ZERO(&socket_set);
FD_SET(p->sock,&socket_set);
tv.tv_sec = timeout;
if((ret = select(p->sock+1,&socket_set,NULL,NULL,&tv))==1)
ret = 0;