aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tests/server/sws.c463
1 files changed, 286 insertions, 177 deletions
diff --git a/tests/server/sws.c b/tests/server/sws.c
index 1ec63f2b0..5f6346e8c 100644
--- a/tests/server/sws.c
+++ b/tests/server/sws.c
@@ -1218,7 +1218,8 @@ static curl_socket_t connect_to(const char *ipaddr, unsigned short port)
#ifdef TCP_NODELAY
/* Disable the Nagle algorithm */
- if(setsockopt(serverfd, level, TCP_NODELAY, (void *)&flag, sizeof(flag)) < 0)
+ if(setsockopt(serverfd, level, TCP_NODELAY,
+ (void *)&flag, sizeof(flag)) < 0)
logmsg("====> TCP_NODELAY for server conection failed");
#endif
@@ -1284,24 +1285,40 @@ static curl_socket_t connect_to(const char *ipaddr, unsigned short port)
#define data_or_ctrl(x) ((x)?"DATA":"CTRL")
-static void http_connect(curl_socket_t infd,
+#define CTRL 0
+#define DATA 1
+
+static void http_connect(curl_socket_t *infdp,
curl_socket_t rootfd,
struct httprequest *req,
const char *ipaddr)
{
- curl_socket_t serverfd[2];
- curl_socket_t clientfd[2];
- curl_socket_t datafd = CURL_SOCKET_BAD;
+ curl_socket_t serverfd[2] = {CURL_SOCKET_BAD, CURL_SOCKET_BAD};
+ curl_socket_t clientfd[2] = {CURL_SOCKET_BAD, CURL_SOCKET_BAD};
ssize_t toc[2] = {0, 0}; /* number of bytes to client */
ssize_t tos[2] = {0, 0}; /* number of bytes to server */
char readclient[2][256];
char readserver[2][256];
- bool poll_client[2] = { TRUE, TRUE };
- bool poll_server[2] = { TRUE, TRUE };
- int control=0;
+ bool poll_client_rd[2] = { TRUE, TRUE };
+ bool poll_server_rd[2] = { TRUE, TRUE };
+ bool poll_client_wr[2] = { TRUE, TRUE };
+ bool poll_server_wr[2] = { TRUE, TRUE };
+#ifdef TCP_NODELAY
+ curl_socklen_t flag = 1;
+ int level = IPPROTO_TCP;
+#endif
+ bool primary = FALSE;
+ bool secondary = FALSE;
+ int max_tunnel_idx; /* CTRL or DATA */
+#if 0
int quarters;
+#endif
int i;
+ /* primary tunnel client endpoint already connected */
+ clientfd[CTRL] = *infdp;
+
+#if 0
/* sleep here to make sure the client gets the CONNECT response
first and separate from the data that might follow here */
quarters = 4;
@@ -1309,220 +1326,310 @@ static void http_connect(curl_socket_t infd,
quarters--;
wait_ms(250);
}
+#endif
if(got_exit_signal)
- return;
+ goto http_connect_cleanup;
- clientfd[0] = infd;
- clientfd[1] = CURL_SOCKET_BAD;
+ serverfd[CTRL] = connect_to(ipaddr, req->connect_port);
+ if(serverfd[CTRL] == CURL_SOCKET_BAD)
+ goto http_connect_cleanup;
- serverfd[0] = connect_to(ipaddr, req->connect_port);
- if(CURL_SOCKET_BAD == serverfd[0])
- return;
- serverfd[1] = CURL_SOCKET_BAD; /* nothing there (yet) */
+ /* Primary tunnel socket endpoints are now connected. Tunnel data back and
+ forth over the primary tunnel until client or server breaks the primary
+ tunnel, simultaneously allowing establishment, operation and teardown of
+ a secondary tunnel that may be used for passive FTP data connection. */
+
+ max_tunnel_idx = CTRL;
+ primary = TRUE;
+
+ while(!got_exit_signal) {
- /* connected, now tunnel */
- while(1) {
fd_set input;
fd_set output;
- struct timeval timeout = {1,0};
+ struct timeval timeout = {0, 250000L}; /* 250 ms */
ssize_t rc;
curl_socket_t maxfd = (curl_socket_t)-1;
- int used;
FD_ZERO(&input);
FD_ZERO(&output);
- if(CURL_SOCKET_BAD != rootfd) {
- FD_SET(rootfd, &input); /* monitor this for new connections */
+ if((clientfd[DATA] == CURL_SOCKET_BAD) &&
+ (serverfd[DATA] == CURL_SOCKET_BAD)) {
+ /* when secondary tunnel is not established the listener socket
+ is monitored to allow client to establish the secondary tunnel */
+ FD_SET(rootfd, &input);
maxfd = rootfd;
}
- /* set sockets to wait for */
- for(i=0; i<=control; i++) {
- curl_socket_t mostfd = clientfd[i] > serverfd[i] ?
- clientfd[i] : serverfd[i];
- used = 0;
- if(mostfd > maxfd)
- maxfd = mostfd;
-
- if(poll_client[i]) {
- FD_SET(clientfd[i], &input);
- used |= 1 << (i*4);
- }
-
- if(poll_server[i]) {
- FD_SET(serverfd[i], &input);
- used |= 2 << (i*4);
- }
-
- if(toc[i]) { /* if there is data to client, wait until we can write */
- FD_SET(clientfd[i], &output);
- used |= 4 << (i*4);
+ /* set tunnel sockets to wait for */
+ for(i = 0; i <= max_tunnel_idx; i++) {
+ /* client side socket monitoring */
+ if(clientfd[i] != CURL_SOCKET_BAD) {
+ if(poll_client_rd[i]) {
+ /* unless told not to do so, monitor readability */
+ FD_SET(clientfd[i], &input);
+ if(clientfd[i] > maxfd)
+ maxfd = clientfd[i];
+ }
+ if(poll_client_wr[i] && toc[i]) {
+ /* unless told not to do so, monitor writeability
+ if there is data ready to be sent to client */
+ FD_SET(clientfd[i], &output);
+ if(clientfd[i] > maxfd)
+ maxfd = clientfd[i];
+ }
}
- if(tos[i]) { /* if there is data to server, wait until we can write */
- FD_SET(serverfd[i], &output);
- used |= 8 << (i*4);
+ /* server side socket monitoring */
+ if(serverfd[i] != CURL_SOCKET_BAD) {
+ if(poll_server_rd[i]) {
+ /* unless told not to do so, monitor readability */
+ FD_SET(serverfd[i], &input);
+ if(serverfd[i] > maxfd)
+ maxfd = serverfd[i];
+ }
+ if(poll_server_wr[i] && tos[i]) {
+ /* unless told not to do so, monitor writeability
+ if there is data ready to be sent to server */
+ FD_SET(serverfd[i], &output);
+ if(serverfd[i] > maxfd)
+ maxfd = serverfd[i];
+ }
}
}
+ if(got_exit_signal)
+ break;
rc = select((int)maxfd + 1, &input, &output, NULL, &timeout);
if(rc > 0) {
/* socket action */
- size_t len;
- int precontrol;
-
- if((CURL_SOCKET_BAD != rootfd) &&
- FD_ISSET(rootfd, &input)) {
- /* a new connection! */
- struct httprequest req2;
- datafd = accept(rootfd, NULL, NULL);
- if(CURL_SOCKET_BAD == datafd)
- return;
-
- logmsg("====> Client connect DATA");
- req2.pipelining = FALSE;
- if(get_request(datafd, &req2))
- /* non-zero means error, break out of loop */
- break;
- send_doc(datafd, &req2);
+ if(got_exit_signal)
+ break;
- if(DOCNUMBER_CONNECT != req2.testno) {
- /* eeek, not a CONNECT */
- sclose(datafd);
- break;
+ /* ---------------------------------------------------------- */
+
+ /* passive mode FTP may establish a secondary tunnel */
+ if((clientfd[DATA] == CURL_SOCKET_BAD) &&
+ (serverfd[DATA] == CURL_SOCKET_BAD) && FD_ISSET(rootfd, &input)) {
+ /* a new connection on listener socket (most likely from client) */
+ curl_socket_t datafd = accept(rootfd, NULL, NULL);
+ if(datafd != CURL_SOCKET_BAD) {
+ struct httprequest req2;
+ int err;
+ logmsg("====> Client connect DATA");
+#ifdef TCP_NODELAY
+ /* Disable the Nagle algorithm */
+ if(setsockopt(datafd, level, TCP_NODELAY,
+ (void *)&flag, sizeof(flag)) < 0)
+ logmsg("====> TCP_NODELAY for client conection failed");
+#endif
+ req2.pipelining = FALSE;
+ err = get_request(datafd, &req2);
+ if(!err) {
+ err = send_doc(datafd, &req2);
+ if(!err && (req2.testno == DOCNUMBER_CONNECT)) {
+ /* connect to the server */
+ serverfd[DATA] = connect_to(ipaddr, req2.connect_port);
+ if(serverfd[DATA] != CURL_SOCKET_BAD) {
+ /* secondary tunnel established, now we have two connections */
+ poll_client_rd[DATA] = TRUE;
+ poll_client_wr[DATA] = TRUE;
+ poll_server_rd[DATA] = TRUE;
+ poll_server_wr[DATA] = TRUE;
+ max_tunnel_idx = DATA;
+ secondary = TRUE;
+ toc[DATA] = 0;
+ tos[DATA] = 0;
+ clientfd[DATA] = datafd;
+ datafd = CURL_SOCKET_BAD;
+ }
+ }
+ }
+ if(datafd != CURL_SOCKET_BAD) {
+ /* secondary tunnel not established */
+ shutdown(datafd, SHUT_RDWR);
+ sclose(datafd);
+ }
}
-
- /* deal with the new connection */
- rootfd = CURL_SOCKET_BAD; /* prevent new connections */
- clientfd[1] = datafd;
-
- /* connect to the server */
- serverfd[1] = connect_to(ipaddr, req2.connect_port);
- if(serverfd[1] == CURL_SOCKET_BAD) {
- /* BADNESS, bail out */
+ if(got_exit_signal)
break;
- }
- control = 1; /* now we have two connections to work with */
}
- /* store the value before the loop starts */
- precontrol = control;
-
- for(i=0; i<=control; i++) {
- len = sizeof(readclient[i])-tos[i];
- if(len && FD_ISSET(clientfd[i], &input)) {
- /* read from client */
- rc = sread(clientfd[i], &readclient[i][tos[i]], len);
- if(rc <= 0) {
- logmsg("[%s] got %d at %s:%d, STOP READING client", data_or_ctrl(i),
- rc, __FILE__, __LINE__);
- poll_client[i] = FALSE;
- }
- else {
- logmsg("[%s] READ %d bytes from client", data_or_ctrl(i), rc);
- logmsg("[%s] READ \"%s\"", data_or_ctrl(i),
- data_to_hex(&readclient[i][tos[i]], rc));
- tos[i] += rc;
+ /* ---------------------------------------------------------- */
+
+ /* react to tunnel endpoint readable/writeable notifications */
+ for(i = 0; i <= max_tunnel_idx; i++) {
+ size_t len;
+ if(clientfd[i] != CURL_SOCKET_BAD) {
+ len = sizeof(readclient[i]) - tos[i];
+ if(len && FD_ISSET(clientfd[i], &input)) {
+ /* read from client */
+ rc = sread(clientfd[i], &readclient[i][tos[i]], len);
+ if(rc <= 0) {
+ logmsg("[%s] got %zd, STOP READING client", data_or_ctrl(i), rc);
+ shutdown(clientfd[i], SHUT_RD);
+ poll_client_rd[i] = FALSE;
+ }
+ else {
+ logmsg("[%s] READ %zd bytes from client", data_or_ctrl(i), rc);
+ logmsg("[%s] READ \"%s\"", data_or_ctrl(i),
+ data_to_hex(&readclient[i][tos[i]], rc));
+ tos[i] += rc;
+ }
}
}
-
- len = sizeof(readserver[i])-toc[i];
- if(len && FD_ISSET(serverfd[i], &input)) {
- /* read from server */
- rc = sread(serverfd[i], &readserver[i][toc[i]], len);
- if(rc <= 0) {
- logmsg("[%s] got %d at %s:%d, STOP READING server", data_or_ctrl(i),
- rc, __FILE__, __LINE__);
- poll_server[i] = FALSE;
- }
- else {
- logmsg("[%s] READ %d bytes from server", data_or_ctrl(i), rc);
- logmsg("[%s] READ \"%s\"", data_or_ctrl(i),
- data_to_hex(&readserver[i][toc[i]], rc));
- toc[i] += rc;
+ if(serverfd[i] != CURL_SOCKET_BAD) {
+ len = sizeof(readserver[i])-toc[i];
+ if(len && FD_ISSET(serverfd[i], &input)) {
+ /* read from server */
+ rc = sread(serverfd[i], &readserver[i][toc[i]], len);
+ if(rc <= 0) {
+ logmsg("[%s] got %zd, STOP READING server", data_or_ctrl(i), rc);
+ shutdown(serverfd[i], SHUT_RD);
+ poll_server_rd[i] = FALSE;
+ }
+ else {
+ logmsg("[%s] READ %zd bytes from server", data_or_ctrl(i), rc);
+ logmsg("[%s] READ \"%s\"", data_or_ctrl(i),
+ data_to_hex(&readserver[i][toc[i]], rc));
+ toc[i] += rc;
+ }
}
}
- if(toc[i] && FD_ISSET(clientfd[i], &output)) {
- /* write to client */
- rc = swrite(clientfd[i], readserver[i], toc[i]);
- if(rc <= 0) {
- logmsg("[%s] got %d at %s:%d", data_or_ctrl(i),
- rc, __FILE__, __LINE__);
- control--;
- break;
+ if(clientfd[i] != CURL_SOCKET_BAD) {
+ if(toc[i] && FD_ISSET(clientfd[i], &output)) {
+ /* write to client */
+ rc = swrite(clientfd[i], readserver[i], toc[i]);
+ if(rc <= 0) {
+ logmsg("[%s] got %zd, STOP WRITING client", data_or_ctrl(i), rc);
+ shutdown(clientfd[i], SHUT_WR);
+ poll_client_wr[i] = FALSE;
+ }
+ else {
+ logmsg("[%s] SENT %zd bytes to client", data_or_ctrl(i), rc);
+ logmsg("[%s] SENT \"%s\"", data_or_ctrl(i),
+ data_to_hex(readserver[i], rc));
+ if(toc[i] - rc)
+ memmove(&readserver[i][0], &readserver[i][rc], toc[i]-rc);
+ toc[i] -= rc;
+ }
}
- logmsg("[%s] SENT %d bytes to client", data_or_ctrl(i), rc);
- logmsg("[%s] SENT \"%s\"", data_or_ctrl(i),
- data_to_hex(readserver[i], rc));
- if(toc[i] - rc)
- memmove(&readserver[i][0], &readserver[i][rc], toc[i]-rc);
- toc[i] -= rc;
}
- if(tos[i] && FD_ISSET(serverfd[i], &output)) {
- /* write to server */
- rc = swrite(serverfd[i], readclient[i], tos[i]);
- if(rc <= 0) {
- logmsg("[%s] got %d at %s:%d", data_or_ctrl(i),
- rc, __FILE__, __LINE__);
- control--;
- break;
+ if(serverfd[i] != CURL_SOCKET_BAD) {
+ if(tos[i] && FD_ISSET(serverfd[i], &output)) {
+ /* write to server */
+ rc = swrite(serverfd[i], readclient[i], tos[i]);
+ if(rc <= 0) {
+ logmsg("[%s] got %zd, STOP WRITING server", data_or_ctrl(i), rc);
+ shutdown(serverfd[i], SHUT_WR);
+ poll_server_wr[i] = FALSE;
+ }
+ else {
+ logmsg("[%s] SENT %zd bytes to server", data_or_ctrl(i), rc);
+ logmsg("[%s] SENT \"%s\"", data_or_ctrl(i),
+ data_to_hex(readclient[i], rc));
+ if(tos[i] - rc)
+ memmove(&readclient[i][0], &readclient[i][rc], tos[i]-rc);
+ tos[i] -= rc;
+ }
}
- logmsg("[%s] SENT %d bytes to server", data_or_ctrl(i), rc);
- logmsg("[%s] SENT \"%s\"", data_or_ctrl(i),
- data_to_hex(readclient[i], rc));
- if(tos - rc)
- memmove(&readclient[i][0], &readclient[i][rc], tos[i]-rc);
- tos[i] -= rc;
}
+ }
+ if(got_exit_signal)
+ break;
- if(!toc[i] && !poll_server[i]) {
- /* nothing to send to the client is left, and server polling is
- switched off, bail out */
- logmsg("[%s] ENDING1", data_or_ctrl(i));
- control--;
- }
- if(!tos[i] && !poll_client[i]) {
- /* nothing to send to the server is left, and client polling is
- switched off, bail out */
- logmsg("[%s] ENDING2", data_or_ctrl(i));
- control--;
+ /* ---------------------------------------------------------- */
+
+ /* endpoint read/write disabling, endpoint closing and tunnel teardown */
+ for(i = 0; i <= max_tunnel_idx; i++) {
+ int loop;
+ for(loop = 2; loop; loop--) {
+ /* loop twice to satisfy condition interdependencies without
+ having to await select timeout or another socket event */
+ if(clientfd[i] != CURL_SOCKET_BAD) {
+ if(poll_client_rd[i] && !poll_server_wr[i]) {
+ logmsg("[%s] DISABLED READING client", data_or_ctrl(i));
+ shutdown(clientfd[i], SHUT_RD);
+ poll_client_rd[i] = FALSE;
+ }
+ if(poll_client_wr[i] && !poll_server_rd[i] && !toc[i]) {
+ logmsg("[%s] DISABLED WRITING client", data_or_ctrl(i));
+ shutdown(clientfd[i], SHUT_WR);
+ poll_client_wr[i] = FALSE;
+ }
+ if(!poll_client_wr[i] && !poll_client_rd[i]) {
+ logmsg("[%s] CLOSING client socket", data_or_ctrl(i));
+ sclose(clientfd[i]);
+ clientfd[i] = CURL_SOCKET_BAD;
+ if(serverfd[i] == CURL_SOCKET_BAD) {
+ logmsg("[%s] ENDING", data_or_ctrl(i));
+ if(i == DATA)
+ secondary = FALSE;
+ else
+ primary = FALSE;
+ }
+ }
+ }
+ if(serverfd[i] != CURL_SOCKET_BAD) {
+ if(poll_server_rd[i] && !poll_client_wr[i]) {
+ logmsg("[%s] DISABLED READING server", data_or_ctrl(i));
+ shutdown(serverfd[i], SHUT_RD);
+ poll_server_rd[i] = FALSE;
+ }
+ if(poll_server_wr[i] && !poll_client_rd[i] && !tos[i]) {
+ logmsg("[%s] DISABLED WRITING server", data_or_ctrl(i));
+ shutdown(serverfd[i], SHUT_WR);
+ poll_server_wr[i] = FALSE;
+ }
+ if(!poll_server_wr[i] && !poll_server_rd[i]) {
+ logmsg("[%s] CLOSING server socket", data_or_ctrl(i));
+ sclose(serverfd[i]);
+ serverfd[i] = CURL_SOCKET_BAD;
+ if(clientfd[i] == CURL_SOCKET_BAD) {
+ logmsg("[%s] ENDING", data_or_ctrl(i));
+ if(i == DATA)
+ secondary = FALSE;
+ else
+ primary = FALSE;
+ }
+ }
+ }
}
}
- if(precontrol > control) {
- /* if the value was decremented we close the "lost" sockets */
- if(serverfd[precontrol] != CURL_SOCKET_BAD)
- shutdown(serverfd[precontrol], SHUT_RDWR);
- if(clientfd[precontrol] != CURL_SOCKET_BAD)
- shutdown(clientfd[precontrol], SHUT_RDWR);
-
- quarters = 4;
- while((quarters > 0) && !got_exit_signal) {
- quarters--;
- wait_ms(250);
- }
- if(serverfd[precontrol] != CURL_SOCKET_BAD)
- sclose(serverfd[precontrol]);
- if(clientfd[precontrol] != CURL_SOCKET_BAD)
- sclose(clientfd[precontrol]);
+ /* ---------------------------------------------------------- */
- }
+ max_tunnel_idx = secondary ? DATA : CTRL;
- if(control < 0)
+ if(!primary)
+ /* exit loop upon primary tunnel teardown */
break;
- }
+
+ } /* (rc > 0) */
+
}
-#if 0
- /* close all sockets we created */
- for(i=0; i<2; i++) {
- if(serverfd[i] != CURL_SOCKET_BAD)
+
+http_connect_cleanup:
+
+ for(i = DATA; i >= CTRL; i--) {
+ if(serverfd[i] != CURL_SOCKET_BAD) {
+ logmsg("[%s] CLOSING server socket (cleanup)", data_or_ctrl(i));
+ shutdown(serverfd[i], SHUT_RDWR);
sclose(serverfd[i]);
- if(clientfd[i] != CURL_SOCKET_BAD)
+ }
+ if(clientfd[i] != CURL_SOCKET_BAD) {
+ logmsg("[%s] CLOSING client socket (cleanup)", data_or_ctrl(i));
+ shutdown(clientfd[i], SHUT_RDWR);
sclose(clientfd[i]);
+ }
+ if((serverfd[i] != CURL_SOCKET_BAD) ||
+ (clientfd[i] != CURL_SOCKET_BAD)) {
+ logmsg("[%s] ABORTING", data_or_ctrl(i));
+ }
}
-#endif
+
+ *infdp = CURL_SOCKET_BAD;
}
int main(int argc, char *argv[])
@@ -1810,7 +1917,7 @@ int main(int argc, char *argv[])
if(DOCNUMBER_CONNECT == req.testno) {
/* a CONNECT request, setup and talk the tunnel */
- http_connect(msgsock, sock, &req, hostport);
+ http_connect(&msgsock, sock, &req, hostport);
break;
}
@@ -1845,8 +1952,10 @@ int main(int argc, char *argv[])
a single byte of server-reply. */
wait_ms(50);
- sclose(msgsock);
- msgsock = CURL_SOCKET_BAD;
+ if(msgsock != CURL_SOCKET_BAD) {
+ sclose(msgsock);
+ msgsock = CURL_SOCKET_BAD;
+ }
if(serverlogslocked) {
serverlogslocked = 0;