summaryrefslogtreecommitdiff
path: root/tests/server
diff options
context:
space:
mode:
authornikita <nikita@n0.is>2020-04-30 14:57:43 +0200
committernikita <nikita@n0.is>2020-04-30 14:57:43 +0200
commite78a29c1d7e4c0d9d5d2400f8935fe6c6db9852e (patch)
treefcebec99976dae264a75fccabd555af2ac77b4ad /tests/server
parent55ddc65827f8424ef1c6fe592da07608237cf2ae (diff)
parent53cdc2c963e33bc0cc1a51ad2df79396202e07f8 (diff)
downloadgnurl-e78a29c1d7e4c0d9d5d2400f8935fe6c6db9852e.tar.gz
gnurl-e78a29c1d7e4c0d9d5d2400f8935fe6c6db9852e.tar.bz2
gnurl-e78a29c1d7e4c0d9d5d2400f8935fe6c6db9852e.zip
Merge tag 'curl-7_70_0'
7.70.0
Diffstat (limited to 'tests/server')
-rw-r--r--tests/server/.gitignore1
-rw-r--r--tests/server/CMakeLists.txt21
-rw-r--r--tests/server/Makefile.inc31
-rwxr-xr-xtests/server/base64.pl21
-rw-r--r--tests/server/fake_ntlm.c17
-rw-r--r--tests/server/getpart.c2
-rw-r--r--tests/server/getpart.h2
-rw-r--r--tests/server/mqttd.c1018
-rw-r--r--tests/server/resolve.c4
-rw-r--r--tests/server/rtspd.c215
-rw-r--r--tests/server/server_setup.h2
-rw-r--r--tests/server/server_sockaddr.h2
-rw-r--r--tests/server/sockfilt.c604
-rw-r--r--tests/server/socksd.c143
-rw-r--r--tests/server/sws.c214
-rw-r--r--tests/server/testpart.c2
-rw-r--r--tests/server/tftp.h2
-rw-r--r--tests/server/tftpd.c250
-rw-r--r--tests/server/util.c322
-rw-r--r--tests/server/util.h21
20 files changed, 1953 insertions, 941 deletions
diff --git a/tests/server/.gitignore b/tests/server/.gitignore
index 94329f7da..0f84394c7 100644
--- a/tests/server/.gitignore
+++ b/tests/server/.gitignore
@@ -7,3 +7,4 @@ sws
tftpd
socksd
disabled
+mqttd \ No newline at end of file
diff --git a/tests/server/CMakeLists.txt b/tests/server/CMakeLists.txt
index 3a15284e4..71054a462 100644
--- a/tests/server/CMakeLists.txt
+++ b/tests/server/CMakeLists.txt
@@ -1,3 +1,24 @@
+#***************************************************************************
+# _ _ ____ _
+# Project ___| | | | _ \| |
+# / __| | | | |_) | |
+# | (__| |_| | _ <| |___
+# \___|\___/|_| \_\_____|
+#
+# Copyright (C) 2009 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at https://curl.haxx.se/docs/copyright.html.
+#
+# You may opt to use, copy, modify, merge, publish, distribute and/or sell
+# copies of the Software, and permit persons to whom the Software is
+# furnished to do so, under the terms of the COPYING file.
+#
+# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+# KIND, either express or implied.
+#
+###########################################################################
set(TARGET_LABEL_PREFIX "Test server ")
if(MSVC)
diff --git a/tests/server/Makefile.inc b/tests/server/Makefile.inc
index 6296af7cc..fb13d79cb 100644
--- a/tests/server/Makefile.inc
+++ b/tests/server/Makefile.inc
@@ -1,4 +1,27 @@
-noinst_PROGRAMS = getpart resolve rtspd sockfilt sws tftpd fake_ntlm socksd disabled
+#***************************************************************************
+# _ _ ____ _
+# Project ___| | | | _ \| |
+# / __| | | | |_) | |
+# | (__| |_| | _ <| |___
+# \___|\___/|_| \_\_____|
+#
+# Copyright (C) 2009 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at https://curl.haxx.se/docs/copyright.html.
+#
+# You may opt to use, copy, modify, merge, publish, distribute and/or sell
+# copies of the Software, and permit persons to whom the Software is
+# furnished to do so, under the terms of the COPYING file.
+#
+# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+# KIND, either express or implied.
+#
+###########################################################################
+
+noinst_PROGRAMS = getpart resolve rtspd sockfilt sws tftpd fake_ntlm \
+ socksd disabled mqttd
CURLX_SRCS = \
../../lib/mprintf.c \
@@ -56,6 +79,12 @@ socksd_SOURCES = $(CURLX_SRCS) $(CURLX_HDRS) $(USEFUL) $(UTIL) \
socksd_LDADD = @CURL_NETWORK_AND_TIME_LIBS@
socksd_CFLAGS = $(AM_CFLAGS)
+mqttd_SOURCES = $(CURLX_SRCS) $(CURLX_HDRS) $(USEFUL) $(UTIL) \
+ server_sockaddr.h mqttd.c \
+ ../../lib/inet_pton.c
+mqttd_LDADD = @CURL_NETWORK_AND_TIME_LIBS@
+mqttd_CFLAGS = $(AM_CFLAGS)
+
sws_SOURCES = $(CURLX_SRCS) $(CURLX_HDRS) $(USEFUL) $(UTIL) \
server_sockaddr.h \
sws.c \
diff --git a/tests/server/base64.pl b/tests/server/base64.pl
index 449c3103a..d00673ec9 100755
--- a/tests/server/base64.pl
+++ b/tests/server/base64.pl
@@ -1,4 +1,25 @@
#!/usr/bin/env perl
+#***************************************************************************
+# _ _ ____ _
+# Project ___| | | | _ \| |
+# / __| | | | |_) | |
+# | (__| |_| | _ <| |___
+# \___|\___/|_| \_\_____|
+#
+# Copyright (C) 2004 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at https://curl.haxx.se/docs/copyright.html.
+#
+# You may opt to use, copy, modify, merge, publish, distribute and/or sell
+# copies of the Software, and permit persons to whom the Software is
+# furnished to do so, under the terms of the COPYING file.
+#
+# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+# KIND, either express or implied.
+#
+###########################################################################
use MIME::Base64 qw(encode_base64);
diff --git a/tests/server/fake_ntlm.c b/tests/server/fake_ntlm.c
index c6e36b6f6..c9211f7ea 100644
--- a/tests/server/fake_ntlm.c
+++ b/tests/server/fake_ntlm.c
@@ -6,7 +6,7 @@
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2010, Mandy Wu, <mandy.wu@intel.com>
- * Copyright (C) 2011 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2011 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -112,7 +112,6 @@ int main(int argc, char *argv[])
char buf[1024];
char logfilename[256];
FILE *stream;
- char *filename;
int error;
char *type1_input = NULL, *type3_input = NULL;
char *type1_output = NULL, *type3_output = NULL;
@@ -186,12 +185,10 @@ int main(int argc, char *argv[])
path = env;
}
- filename = test2file(testnum);
- stream = fopen(filename, "rb");
+ stream = test2fopen(testnum);
if(!stream) {
error = errno;
logmsg("fopen() failed with error: %d %s", error, strerror(error));
- logmsg("Error opening file: %s", filename);
logmsg("Couldn't open test file %ld", testnum);
exit(1);
}
@@ -205,13 +202,11 @@ int main(int argc, char *argv[])
}
}
- stream = fopen(filename, "rb");
+ stream = test2fopen(testnum);
if(!stream) {
error = errno;
logmsg("fopen() failed with error: %d %s", error, strerror(error));
- logmsg("Error opening file: %s", filename);
logmsg("Couldn't open test file %ld", testnum);
- exit(1);
}
else {
size = 0;
@@ -225,11 +220,10 @@ int main(int argc, char *argv[])
while(fgets(buf, sizeof(buf), stdin)) {
if(strcmp(buf, type1_input) == 0) {
- stream = fopen(filename, "rb");
+ stream = test2fopen(testnum);
if(!stream) {
error = errno;
logmsg("fopen() failed with error: %d %s", error, strerror(error));
- logmsg("Error opening file: %s", filename);
logmsg("Couldn't open test file %ld", testnum);
exit(1);
}
@@ -247,11 +241,10 @@ int main(int argc, char *argv[])
fflush(stdout);
}
else if(strncmp(buf, type3_input, strlen(type3_input)) == 0) {
- stream = fopen(filename, "rb");
+ stream = test2fopen(testnum);
if(!stream) {
error = errno;
logmsg("fopen() failed with error: %d %s", error, strerror(error));
- logmsg("Error opening file: %s", filename);
logmsg("Couldn't open test file %ld", testnum);
exit(1);
}
diff --git a/tests/server/getpart.c b/tests/server/getpart.c
index 044705d06..664d81569 100644
--- a/tests/server/getpart.c
+++ b/tests/server/getpart.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/tests/server/getpart.h b/tests/server/getpart.h
index 277368519..e8ddd22aa 100644
--- a/tests/server/getpart.h
+++ b/tests/server/getpart.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/tests/server/mqttd.c b/tests/server/mqttd.c
new file mode 100644
index 000000000..6785b0014
--- /dev/null
+++ b/tests/server/mqttd.c
@@ -0,0 +1,1018 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+#include "server_setup.h"
+#include <stdlib.h>
+#include <string.h>
+#include "util.h"
+
+/* Function
+ *
+ * Accepts a TCP connection on a custom port (IPv4 or IPv6). Speaks MQTT.
+ *
+ * Read commands from FILE (set with --config). The commands control how to
+ * act and is reset to defaults each client TCP connect.
+ *
+ * Config file keywords:
+ *
+ * TODO
+ */
+
+/* based on sockfilt.c */
+
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_IN6_H
+#include <netinet/in6.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#define ENABLE_CURLX_PRINTF
+/* make the curlx header define all printf() functions to use the curlx_*
+ versions instead */
+#include "curlx.h" /* from the private lib dir */
+#include "getpart.h"
+#include "inet_pton.h"
+#include "util.h"
+#include "server_sockaddr.h"
+#include "warnless.h"
+
+/* include memdebug.h last */
+#include "memdebug.h"
+
+#ifdef USE_WINSOCK
+#undef EINTR
+#define EINTR 4 /* errno.h value */
+#undef EAGAIN
+#define EAGAIN 11 /* errno.h value */
+#undef ENOMEM
+#define ENOMEM 12 /* errno.h value */
+#undef EINVAL
+#define EINVAL 22 /* errno.h value */
+#endif
+
+#define DEFAULT_PORT 1883 /* MQTT default port */
+
+#ifndef DEFAULT_LOGFILE
+#define DEFAULT_LOGFILE "log/mqttd.log"
+#endif
+
+#ifndef DEFAULT_CONFIG
+#define DEFAULT_CONFIG "mqttd.config"
+#endif
+
+#define MQTT_MSG_CONNECT 0x10
+#define MQTT_MSG_CONNACK 0x20
+#define MQTT_MSG_PUBLISH 0x30
+#define MQTT_MSG_PUBACK 0x40
+#define MQTT_MSG_SUBSCRIBE 0x82
+#define MQTT_MSG_SUBACK 0x90
+#define MQTT_MSG_DISCONNECT 0xe0
+
+#define MQTT_CONNACK_LEN 4
+#define MQTT_SUBACK_LEN 5
+#define MQTT_CLIENTID_LEN 12 /* "curl0123abcd" */
+#define MQTT_HEADER_LEN 5 /* max 5 bytes */
+
+struct configurable {
+ unsigned char version; /* initial version byte in the request must match
+ this */
+ bool publish_before_suback;
+ bool short_publish;
+ unsigned char error_connack;
+ int testnum;
+};
+
+#define REQUEST_DUMP "log/server.input"
+#define CONFIG_VERSION 5
+
+static struct configurable config;
+
+const char *serverlogfile = DEFAULT_LOGFILE;
+static const char *configfile = DEFAULT_CONFIG;
+
+#ifdef ENABLE_IPV6
+static bool use_ipv6 = FALSE;
+#endif
+static const char *ipv_inuse = "IPv4";
+static unsigned short port = DEFAULT_PORT;
+
+static void resetdefaults(void)
+{
+ logmsg("Reset to defaults");
+ config.version = CONFIG_VERSION;
+ config.publish_before_suback = FALSE;
+ config.short_publish = FALSE;
+ config.error_connack = 0;
+ config.testnum = 0;
+}
+
+static unsigned char byteval(char *value)
+{
+ unsigned long num = strtoul(value, NULL, 10);
+ return num & 0xff;
+}
+
+static void getconfig(void)
+{
+ FILE *fp = fopen(configfile, FOPEN_READTEXT);
+ resetdefaults();
+ if(fp) {
+ char buffer[512];
+ logmsg("parse config file");
+ while(fgets(buffer, sizeof(buffer), fp)) {
+ char key[32];
+ char value[32];
+ if(2 == sscanf(buffer, "%31s %31s", key, value)) {
+ if(!strcmp(key, "version")) {
+ config.version = byteval(value);
+ logmsg("version [%d] set", config.version);
+ }
+ else if(!strcmp(key, "PUBLISH-before-SUBACK")) {
+ logmsg("PUBLISH-before-SUBACK set");
+ config.publish_before_suback = TRUE;
+ }
+ else if(!strcmp(key, "short-PUBLISH")) {
+ logmsg("short-PUBLISH set");
+ config.short_publish = TRUE;
+ }
+ else if(!strcmp(key, "error-CONNACK")) {
+ config.error_connack = byteval(value);
+ logmsg("error-CONNACK = %d", config.error_connack);
+ }
+ else if(!strcmp(key, "Testnum")) {
+ config.testnum = atoi(value);
+ logmsg("testnum = %d", config.testnum);
+ }
+ }
+ }
+ fclose(fp);
+ }
+ else {
+ logmsg("No config file '%s' to read", configfile);
+ }
+}
+
+static void loghex(unsigned char *buffer, ssize_t len)
+{
+ char data[12000];
+ ssize_t i;
+ unsigned char *ptr = buffer;
+ char *optr = data;
+ ssize_t width = 0;
+ int left = sizeof(data);
+
+ for(i = 0; i<len && (left >= 0); i++) {
+ msnprintf(optr, left, "%02x", ptr[i]);
+ width += 2;
+ optr += 2;
+ left -= 2;
+ }
+ if(width)
+ logmsg("'%s'", data);
+}
+
+typedef enum {
+ FROM_CLIENT,
+ FROM_SERVER
+} mqttdir;
+
+static void logprotocol(mqttdir dir,
+ const char *prefix, size_t remlen,
+ FILE *output,
+ unsigned char *buffer, ssize_t len)
+{
+ char data[12000] = "";
+ ssize_t i;
+ unsigned char *ptr = buffer;
+ char *optr = data;
+ ssize_t width = 0;
+ int left = sizeof(data);
+
+ for(i = 0; i<len && (left >= 0); i++) {
+ msnprintf(optr, left, "%02x", ptr[i]);
+ width += 2;
+ optr += 2;
+ left -= 2;
+ }
+ fprintf(output, "%s %s %zx %s\n",
+ dir == FROM_CLIENT? "client": "server",
+ prefix, remlen,
+ data);
+}
+
+
+/* return 0 on success */
+static int connack(FILE *dump, curl_socket_t fd)
+{
+ unsigned char packet[]={
+ MQTT_MSG_CONNACK, 0x02,
+ 0x00, 0x00
+ };
+ ssize_t rc;
+
+ packet[3] = config.error_connack;
+
+ rc = swrite(fd, (char *)packet, sizeof(packet));
+ if(rc > 0) {
+ logmsg("WROTE %d bytes [CONNACK]", rc);
+ loghex(packet, rc);
+ logprotocol(FROM_SERVER, "CONNACK", 2, dump, packet, sizeof(packet));
+ }
+ if(rc == sizeof(packet)) {
+ return 0;
+ }
+ return 1;
+}
+
+/* return 0 on success */
+static int suback(FILE *dump, curl_socket_t fd, unsigned short packetid)
+{
+ unsigned char packet[]={
+ MQTT_MSG_SUBACK, 0x03,
+ 0, 0, /* filled in below */
+ 0x00
+ };
+ ssize_t rc;
+ packet[2] = (unsigned char)(packetid >> 8);
+ packet[3] = (unsigned char)(packetid & 0xff);
+
+ rc = swrite(fd, (char *)packet, sizeof(packet));
+ if(rc == sizeof(packet)) {
+ logmsg("WROTE %d bytes [SUBACK]", rc);
+ loghex(packet, rc);
+ logprotocol(FROM_SERVER, "SUBACK", 3, dump, packet, rc);
+ return 0;
+ }
+ return 1;
+}
+
+#ifdef QOS
+/* return 0 on success */
+static int puback(FILE *dump, curl_socket_t fd, unsigned short packetid)
+{
+ unsigned char packet[]={
+ MQTT_MSG_PUBACK, 0x00,
+ 0, 0 /* filled in below */
+ };
+ ssize_t rc;
+ packet[2] = (unsigned char)(packetid >> 8);
+ packet[3] = (unsigned char)(packetid & 0xff);
+
+ rc = swrite(fd, (char *)packet, sizeof(packet));
+ if(rc == sizeof(packet)) {
+ logmsg("WROTE %d bytes [PUBACK]", rc);
+ loghex(packet, rc);
+ logprotocol(FROM_SERVER, dump, packet, rc);
+ return 0;
+ }
+ logmsg("Failed sending [PUBACK]");
+ return 1;
+}
+#endif
+
+/* return 0 on success */
+static int disconnect(FILE *dump, curl_socket_t fd)
+{
+ unsigned char packet[]={
+ MQTT_MSG_DISCONNECT, 0x00,
+ };
+ ssize_t rc = swrite(fd, (char *)packet, sizeof(packet));
+ if(rc == sizeof(packet)) {
+ logmsg("WROTE %d bytes [DISCONNECT]", rc);
+ loghex(packet, rc);
+ logprotocol(FROM_SERVER, "DISCONNECT", 0, dump, packet, rc);
+ return 0;
+ }
+ logmsg("Failed sending [DISCONNECT]");
+ return 1;
+}
+
+
+
+/*
+ do
+
+ encodedByte = X MOD 128
+
+ X = X DIV 128
+
+ // if there are more data to encode, set the top bit of this byte
+
+ if ( X > 0 )
+
+ encodedByte = encodedByte OR 128
+
+ endif
+
+ 'output' encodedByte
+
+ while ( X > 0 )
+
+*/
+
+/* return number of bytes used */
+static int encode_length(size_t packetlen, char *remlength) /* 4 bytes */
+{
+ int bytes = 0;
+ unsigned char encode;
+
+ do {
+ encode = packetlen % 0x80;
+ packetlen /= 0x80;
+ if(packetlen)
+ encode |= 0x80;
+
+ remlength[bytes++] = encode;
+
+ if(bytes > 3) {
+ logmsg("too large packet!");
+ return 0;
+ }
+ } while(packetlen);
+
+ return bytes;
+}
+
+
+static size_t decode_length(unsigned char *buf,
+ size_t buflen, size_t *lenbytes)
+{
+ size_t len = 0;
+ size_t mult = 1;
+ size_t i;
+ unsigned char encoded = 0x80;
+
+ for(i = 0; (i < buflen) && (encoded & 0x80); i++) {
+ encoded = buf[i];
+ len += (encoded & 0x7f) * mult;
+ mult *= 0x80;
+ }
+
+ if(lenbytes)
+ *lenbytes = i;
+
+ return len;
+}
+
+
+/* return 0 on success */
+static int publish(FILE *dump,
+ curl_socket_t fd, unsigned short packetid,
+ char *topic, char *payload, size_t payloadlen)
+{
+ size_t topiclen = strlen(topic);
+ unsigned char *packet;
+ size_t payloadindex;
+ ssize_t remaininglength = topiclen + 2 + payloadlen;
+ ssize_t packetlen;
+ ssize_t sendamount;
+ ssize_t rc;
+ char rembuffer[4];
+ int encodedlen;
+
+ encodedlen = encode_length(remaininglength, rembuffer);
+
+ /* one packet type byte (possibly two more for packetid) */
+ packetlen = remaininglength + encodedlen + 1;
+ packet = malloc(packetlen);
+ if(!packet)
+ return 1;
+
+ packet[0] = MQTT_MSG_PUBLISH; /* TODO: set QoS? */
+ memcpy(&packet[1], rembuffer, encodedlen);
+
+ (void)packetid;
+ /* packet_id if QoS is set */
+
+ packet[1 + encodedlen] = (unsigned char)(topiclen >> 8);
+ packet[2 + encodedlen] = (unsigned char)(topiclen & 0xff);
+ memcpy(&packet[3 + encodedlen], topic, topiclen);
+
+ payloadindex = 3 + topiclen + encodedlen;
+ memcpy(&packet[payloadindex], payload, payloadlen);
+
+ sendamount = packetlen;
+ if(config.short_publish)
+ sendamount -= 2;
+
+ rc = swrite(fd, (char *)packet, sendamount);
+ if(rc > 0) {
+ logmsg("WROTE %d bytes [PUBLISH]", rc);
+ loghex(packet, rc);
+ logprotocol(FROM_SERVER, "PUBLISH", remaininglength, dump, packet, rc);
+ }
+ if(rc == packetlen)
+ return 0;
+ return 1;
+}
+
+#define MAX_TOPIC_LENGTH 65535
+#define MAX_CLIENT_ID_LENGTH 32
+
+static char topic[MAX_TOPIC_LENGTH + 1];
+
+static int fixedheader(curl_socket_t fd,
+ unsigned char *bytep,
+ size_t *remaining_lengthp,
+ size_t *remaining_length_bytesp)
+{
+ /* get the fixed header */
+ unsigned char buffer[10];
+
+ /* get the first two bytes */
+ ssize_t rc = sread(fd, (char *)buffer, 2);
+ int i;
+ if(rc < 2) {
+ logmsg("READ %d bytes [SHORT!]", rc);
+ return 1; /* fail */
+ }
+ logmsg("READ %d bytes", rc);
+ loghex(buffer, rc);
+ *bytep = buffer[0];
+
+ /* if the length byte has the top bit set, get the next one too */
+ i = 1;
+ while(buffer[i] & 0x80) {
+ i++;
+ rc = sread(fd, (char *)&buffer[i], 1);
+ if(rc != 1) {
+ logmsg("Remaining Length broken");
+ return 1;
+ }
+ }
+ *remaining_lengthp = decode_length(&buffer[1], i, remaining_length_bytesp);
+ logmsg("Remaining Length: %ld [%d bytes]", (long) *remaining_lengthp,
+ *remaining_length_bytesp);
+ return 0;
+}
+
+static curl_socket_t mqttit(curl_socket_t fd)
+{
+ unsigned char buffer[10*1024];
+ ssize_t rc;
+ unsigned char byte;
+ unsigned short packet_id;
+ size_t payload_len;
+ unsigned int topic_len;
+ size_t remaining_length = 0;
+ size_t bytes = 0; /* remaining length field size in bytes */
+ char client_id[MAX_CLIENT_ID_LENGTH];
+ long testno;
+
+ static const char protocol[7] = {
+ 0x00, 0x04, /* protocol length */
+ 'M','Q','T','T', /* protocol name */
+ 0x04 /* protocol level */
+ };
+ FILE *dump = fopen(REQUEST_DUMP, "ab");
+ if(!dump)
+ goto end;
+
+ getconfig();
+
+ testno = config.testnum;
+
+ if(testno)
+ logmsg("Found test number %ld", testno);
+
+ do {
+ /* get the fixed header */
+ rc = fixedheader(fd, &byte, &remaining_length, &bytes);
+ if(rc)
+ break;
+ if(remaining_length) {
+ rc = sread(fd, (char *)buffer, remaining_length);
+ if(rc > 0) {
+ logmsg("READ %d bytes", rc);
+ loghex(buffer, rc);
+ }
+ }
+
+ if(byte == MQTT_MSG_CONNECT) {
+ logprotocol(FROM_CLIENT, "CONNECT", remaining_length,
+ dump, buffer, rc);
+
+ if(memcmp(protocol, buffer, sizeof(protocol))) {
+ logmsg("Protocol preamble mismatch");
+ goto end;
+ }
+ /* ignore the connect flag byte and two keepalive bytes */
+
+ payload_len = (buffer[10] << 8) | buffer[11];
+ if((ssize_t)payload_len != (rc - 12)) {
+ logmsg("Payload length mismatch, expected %x got %x",
+ rc - 12, payload_len);
+ goto end;
+ }
+ else if((payload_len + 1) > MAX_CLIENT_ID_LENGTH) {
+ logmsg("Too large client id");
+ goto end;
+ }
+ memcpy(client_id, &buffer[14], payload_len);
+ client_id[payload_len] = 0;
+
+ logmsg("MQTT client connect accepted: %s", client_id);
+
+ /* The first packet sent from the Server to the Client MUST be a
+ CONNACK Packet */
+
+ if(connack(dump, fd)) {
+ logmsg("failed sending CONNACK");
+ goto end;
+ }
+ }
+ else if(byte == MQTT_MSG_SUBSCRIBE) {
+ FILE *stream;
+ int error;
+ char *data;
+ size_t datalen;
+ logprotocol(FROM_CLIENT, "SUBSCRIBE", remaining_length,
+ dump, buffer, rc);
+ logmsg("Incoming SUBSCRIBE");
+
+ if(rc < 6) {
+ logmsg("Too small SUBSCRIBE");
+ goto end;
+ }
+
+ /* two bytes packet id */
+ packet_id = (unsigned short)((buffer[0] << 8) | buffer[1]);
+
+ /* two bytes topic length */
+ topic_len = (buffer[2] << 8) | buffer[3];
+ if(topic_len != (remaining_length - 5)) {
+ logmsg("Wrong topic length, got %d expected %d",
+ topic_len, remaining_length - 5);
+ goto end;
+ }
+ memcpy(topic, &buffer[4], topic_len);
+ topic[topic_len] = 0;
+
+ /* there's a QoS byte (two bits) after the topic */
+
+ logmsg("SUBSCRIBE to '%s' [%d]", topic, packet_id);
+ stream = test2fopen(testno);
+ error = getpart(&data, &datalen, "reply", "data", stream);
+ if(!error) {
+ if(!config.publish_before_suback) {
+ if(suback(dump, fd, packet_id)) {
+ logmsg("failed sending SUBACK");
+ goto end;
+ }
+ }
+ if(publish(dump, fd, packet_id, topic, data, datalen)) {
+ logmsg("PUBLISH failed");
+ goto end;
+ }
+ if(config.publish_before_suback) {
+ if(suback(dump, fd, packet_id)) {
+ logmsg("failed sending SUBACK");
+ goto end;
+ }
+ }
+ }
+ else {
+ char *def = (char *)"this is random payload yes yes it is";
+ publish(dump, fd, packet_id, topic, def, strlen(def));
+ }
+ disconnect(dump, fd);
+ }
+ else if((byte & 0xf0) == (MQTT_MSG_PUBLISH & 0xf0)) {
+ size_t topiclen;
+
+ logmsg("Incoming PUBLISH");
+ logprotocol(FROM_CLIENT, "PUBLISH", remaining_length,
+ dump, buffer, rc);
+
+ topiclen = (buffer[1 + bytes] << 8) | buffer[2 + bytes];
+ logmsg("Got %d bytes topic", topiclen);
+ /* TODO: verify topiclen */
+
+#ifdef QOS
+ /* TODO: handle packetid if there is one. Send puback if QoS > 0 */
+ puback(dump, fd, 0);
+#endif
+ /* expect a disconnect here */
+ /* get the request */
+ rc = sread(fd, (char *)&buffer[0], 2);
+
+ logmsg("READ %d bytes [DISCONNECT]", rc);
+ loghex(buffer, rc);
+ logprotocol(FROM_CLIENT, "DISCONNECT", 0, dump, buffer, rc);
+ goto end;
+ }
+ else {
+ /* not supported (yet) */
+ goto end;
+ }
+ } while(1);
+
+ end:
+ fclose(dump);
+ return CURL_SOCKET_BAD;
+}
+
+/*
+ sockfdp is a pointer to an established stream or CURL_SOCKET_BAD
+
+ if sockfd is CURL_SOCKET_BAD, listendfd is a listening socket we must
+ accept()
+*/
+static bool incoming(curl_socket_t listenfd)
+{
+ fd_set fds_read;
+ fd_set fds_write;
+ fd_set fds_err;
+ int clients = 0; /* connected clients */
+
+ if(got_exit_signal) {
+ logmsg("signalled to die, exiting...");
+ return FALSE;
+ }
+
+#ifdef HAVE_GETPPID
+ /* As a last resort, quit if socks5 process becomes orphan. */
+ if(getppid() <= 1) {
+ logmsg("process becomes orphan, exiting");
+ return FALSE;
+ }
+#endif
+
+ do {
+ ssize_t rc;
+ int error = 0;
+ curl_socket_t sockfd = listenfd;
+ int maxfd = (int)sockfd;
+
+ FD_ZERO(&fds_read);
+ FD_ZERO(&fds_write);
+ FD_ZERO(&fds_err);
+
+ /* there's always a socket to wait for */
+ FD_SET(sockfd, &fds_read);
+
+ do {
+ /* select() blocking behavior call on blocking descriptors please */
+ rc = select(maxfd + 1, &fds_read, &fds_write, &fds_err, NULL);
+ if(got_exit_signal) {
+ logmsg("signalled to die, exiting...");
+ return FALSE;
+ }
+ } while((rc == -1) && ((error = SOCKERRNO) == EINTR));
+
+ if(rc < 0) {
+ logmsg("select() failed with error: (%d) %s",
+ error, strerror(error));
+ return FALSE;
+ }
+
+ if(FD_ISSET(sockfd, &fds_read)) {
+ curl_socket_t newfd = accept(sockfd, NULL, NULL);
+ if(CURL_SOCKET_BAD == newfd) {
+ error = SOCKERRNO;
+ logmsg("accept(%d, NULL, NULL) failed with error: (%d) %s",
+ sockfd, error, strerror(error));
+ }
+ else {
+ logmsg("====> Client connect, fd %d. Read config from %s",
+ newfd, configfile);
+ set_advisor_read_lock(SERVERLOGS_LOCK);
+ (void)mqttit(newfd); /* until done */
+ clear_advisor_read_lock(SERVERLOGS_LOCK);
+
+ logmsg("====> Client disconnect");
+ sclose(newfd);
+ }
+ }
+ } while(clients);
+
+ return TRUE;
+}
+
+static curl_socket_t sockdaemon(curl_socket_t sock,
+ unsigned short *listenport)
+{
+ /* passive daemon style */
+ srvr_sockaddr_union_t listener;
+ int flag;
+ int rc;
+ int totdelay = 0;
+ int maxretr = 10;
+ int delay = 20;
+ int attempt = 0;
+ int error = 0;
+
+ do {
+ attempt++;
+ flag = 1;
+ rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (void *)&flag, sizeof(flag));
+ if(rc) {
+ error = SOCKERRNO;
+ logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s",
+ error, strerror(error));
+ if(maxretr) {
+ rc = wait_ms(delay);
+ if(rc) {
+ /* should not happen */
+ logmsg("wait_ms() failed with error: %d", rc);
+ sclose(sock);
+ return CURL_SOCKET_BAD;
+ }
+ if(got_exit_signal) {
+ logmsg("signalled to die, exiting...");
+ sclose(sock);
+ return CURL_SOCKET_BAD;
+ }
+ totdelay += delay;
+ delay *= 2; /* double the sleep for next attempt */
+ }
+ }
+ } while(rc && maxretr--);
+
+ if(rc) {
+ logmsg("setsockopt(SO_REUSEADDR) failed %d times in %d ms. Error: (%d) %s",
+ attempt, totdelay, error, strerror(error));
+ logmsg("Continuing anyway...");
+ }
+
+ /* When the specified listener port is zero, it is actually a
+ request to let the system choose a non-zero available port. */
+
+#ifdef ENABLE_IPV6
+ if(!use_ipv6) {
+#endif
+ memset(&listener.sa4, 0, sizeof(listener.sa4));
+ listener.sa4.sin_family = AF_INET;
+ listener.sa4.sin_addr.s_addr = INADDR_ANY;
+ listener.sa4.sin_port = htons(*listenport);
+ rc = bind(sock, &listener.sa, sizeof(listener.sa4));
+#ifdef ENABLE_IPV6
+ }
+ else {
+ memset(&listener.sa6, 0, sizeof(listener.sa6));
+ listener.sa6.sin6_family = AF_INET6;
+ listener.sa6.sin6_addr = in6addr_any;
+ listener.sa6.sin6_port = htons(*listenport);
+ rc = bind(sock, &listener.sa, sizeof(listener.sa6));
+ }
+#endif /* ENABLE_IPV6 */
+ if(rc) {
+ error = SOCKERRNO;
+ logmsg("Error binding socket on port %hu: (%d) %s",
+ *listenport, error, strerror(error));
+ sclose(sock);
+ return CURL_SOCKET_BAD;
+ }
+
+ if(!*listenport) {
+ /* The system was supposed to choose a port number, figure out which
+ port we actually got and update the listener port value with it. */
+ curl_socklen_t la_size;
+ srvr_sockaddr_union_t localaddr;
+#ifdef ENABLE_IPV6
+ if(!use_ipv6)
+#endif
+ la_size = sizeof(localaddr.sa4);
+#ifdef ENABLE_IPV6
+ else
+ la_size = sizeof(localaddr.sa6);
+#endif
+ memset(&localaddr.sa, 0, (size_t)la_size);
+ if(getsockname(sock, &localaddr.sa, &la_size) < 0) {
+ error = SOCKERRNO;
+ logmsg("getsockname() failed with error: (%d) %s",
+ error, strerror(error));
+ sclose(sock);
+ return CURL_SOCKET_BAD;
+ }
+ switch(localaddr.sa.sa_family) {
+ case AF_INET:
+ *listenport = ntohs(localaddr.sa4.sin_port);
+ break;
+#ifdef ENABLE_IPV6
+ case AF_INET6:
+ *listenport = ntohs(localaddr.sa6.sin6_port);
+ break;
+#endif
+ default:
+ break;
+ }
+ if(!*listenport) {
+ /* Real failure, listener port shall not be zero beyond this point. */
+ logmsg("Apparently getsockname() succeeded, with listener port zero.");
+ logmsg("A valid reason for this failure is a binary built without");
+ logmsg("proper network library linkage. This might not be the only");
+ logmsg("reason, but double check it before anything else.");
+ sclose(sock);
+ return CURL_SOCKET_BAD;
+ }
+ }
+
+ /* start accepting connections */
+ rc = listen(sock, 5);
+ if(0 != rc) {
+ error = SOCKERRNO;
+ logmsg("listen(%d, 5) failed with error: (%d) %s",
+ sock, error, strerror(error));
+ sclose(sock);
+ return CURL_SOCKET_BAD;
+ }
+
+ return sock;
+}
+
+
+int main(int argc, char *argv[])
+{
+ curl_socket_t sock = CURL_SOCKET_BAD;
+ curl_socket_t msgsock = CURL_SOCKET_BAD;
+ int wrotepidfile = 0;
+ int wroteportfile = 0;
+ const char *pidname = ".mqttd.pid";
+ const char *portname = ".mqttd.port";
+ bool juggle_again;
+ int error;
+ int arg = 1;
+
+ while(argc>arg) {
+ if(!strcmp("--version", argv[arg])) {
+ printf("mqttd IPv4%s\n",
+#ifdef ENABLE_IPV6
+ "/IPv6"
+#else
+ ""
+#endif
+ );
+ return 0;
+ }
+ else if(!strcmp("--pidfile", argv[arg])) {
+ arg++;
+ if(argc>arg)
+ pidname = argv[arg++];
+ }
+ else if(!strcmp("--portfile", argv[arg])) {
+ arg++;
+ if(argc>arg)
+ portname = argv[arg++];
+ }
+ else if(!strcmp("--config", argv[arg])) {
+ arg++;
+ if(argc>arg)
+ configfile = argv[arg++];
+ }
+ else if(!strcmp("--logfile", argv[arg])) {
+ arg++;
+ if(argc>arg)
+ serverlogfile = argv[arg++];
+ }
+ else if(!strcmp("--ipv6", argv[arg])) {
+#ifdef ENABLE_IPV6
+ ipv_inuse = "IPv6";
+ use_ipv6 = TRUE;
+#endif
+ arg++;
+ }
+ else if(!strcmp("--ipv4", argv[arg])) {
+ /* for completeness, we support this option as well */
+#ifdef ENABLE_IPV6
+ ipv_inuse = "IPv4";
+ use_ipv6 = FALSE;
+#endif
+ arg++;
+ }
+ else if(!strcmp("--port", argv[arg])) {
+ arg++;
+ if(argc>arg) {
+ char *endptr;
+ unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
+ if((endptr != argv[arg] + strlen(argv[arg])) ||
+ ((ulnum != 0UL) && ((ulnum < 1025UL) || (ulnum > 65535UL)))) {
+ fprintf(stderr, "mqttd: invalid --port argument (%s)\n",
+ argv[arg]);
+ return 0;
+ }
+ port = curlx_ultous(ulnum);
+ arg++;
+ }
+ }
+ else {
+ puts("Usage: mqttd [option]\n"
+ " --config [file]\n"
+ " --version\n"
+ " --logfile [file]\n"
+ " --pidfile [file]\n"
+ " --ipv4\n"
+ " --ipv6\n"
+ " --port [port]\n");
+ return 0;
+ }
+ }
+
+#ifdef WIN32
+ win32_init();
+ atexit(win32_cleanup);
+
+ setmode(fileno(stdin), O_BINARY);
+ setmode(fileno(stdout), O_BINARY);
+ setmode(fileno(stderr), O_BINARY);
+#endif
+
+ install_signal_handlers(FALSE);
+
+#ifdef ENABLE_IPV6
+ if(!use_ipv6)
+#endif
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+#ifdef ENABLE_IPV6
+ else
+ sock = socket(AF_INET6, SOCK_STREAM, 0);
+#endif
+
+ if(CURL_SOCKET_BAD == sock) {
+ error = SOCKERRNO;
+ logmsg("Error creating socket: (%d) %s",
+ error, strerror(error));
+ goto mqttd_cleanup;
+ }
+
+ {
+ /* passive daemon style */
+ sock = sockdaemon(sock, &port);
+ if(CURL_SOCKET_BAD == sock) {
+ goto mqttd_cleanup;
+ }
+ msgsock = CURL_SOCKET_BAD; /* no stream socket yet */
+ }
+
+ logmsg("Running %s version", ipv_inuse);
+ logmsg("Listening on port %hu", port);
+
+ wrotepidfile = write_pidfile(pidname);
+ if(!wrotepidfile) {
+ goto mqttd_cleanup;
+ }
+
+ wroteportfile = write_portfile(portname, (int)port);
+ if(!wroteportfile) {
+ goto mqttd_cleanup;
+ }
+
+ do {
+ juggle_again = incoming(sock);
+ } while(juggle_again);
+
+mqttd_cleanup:
+
+ if((msgsock != sock) && (msgsock != CURL_SOCKET_BAD))
+ sclose(msgsock);
+
+ if(sock != CURL_SOCKET_BAD)
+ sclose(sock);
+
+ if(wrotepidfile)
+ unlink(pidname);
+
+ restore_signal_handlers(FALSE);
+
+ if(got_exit_signal) {
+ logmsg("============> mqttd exits with signal (%d)", exit_signal);
+ /*
+ * To properly set the return status of the process we
+ * must raise the same signal SIGINT or SIGTERM that we
+ * caught and let the old handler take care of it.
+ */
+ raise(exit_signal);
+ }
+
+ logmsg("============> mqttd quits");
+ return 0;
+}
diff --git a/tests/server/resolve.c b/tests/server/resolve.c
index 993e03125..f72561879 100644
--- a/tests/server/resolve.c
+++ b/tests/server/resolve.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -127,7 +127,7 @@ int main(int argc, char *argv[])
memset(&hints, 0, sizeof(hints));
hints.ai_family = use_ipv6 ? PF_INET6 : PF_INET;
hints.ai_socktype = SOCK_STREAM;
- hints.ai_flags = AI_CANONNAME;
+ hints.ai_flags = 0;
/* Use parenthesis around functions to stop them from being replaced by
the macro in memdebug.h */
rc = (getaddrinfo)(host, "80", &hints, &ai);
diff --git a/tests/server/rtspd.c b/tests/server/rtspd.c
index 7563fd22a..6ee7787b1 100644
--- a/tests/server/rtspd.c
+++ b/tests/server/rtspd.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -201,138 +201,6 @@ static const char *doc404_RTSP = "RTSP/1.0 404 Not Found\r\n"
#define RTP_DATA_SIZE 12
static const char *RTP_DATA = "$_1234\n\0asdf";
-/* do-nothing macro replacement for systems which lack siginterrupt() */
-
-#ifndef HAVE_SIGINTERRUPT
-#define siginterrupt(x,y) do {} while(0)
-#endif
-
-/* vars used to keep around previous signal handlers */
-
-typedef RETSIGTYPE (*SIGHANDLER_T)(int);
-
-#ifdef SIGHUP
-static SIGHANDLER_T old_sighup_handler = SIG_ERR;
-#endif
-
-#ifdef SIGPIPE
-static SIGHANDLER_T old_sigpipe_handler = SIG_ERR;
-#endif
-
-#ifdef SIGALRM
-static SIGHANDLER_T old_sigalrm_handler = SIG_ERR;
-#endif
-
-#ifdef SIGINT
-static SIGHANDLER_T old_sigint_handler = SIG_ERR;
-#endif
-
-#ifdef SIGTERM
-static SIGHANDLER_T old_sigterm_handler = SIG_ERR;
-#endif
-
-#if defined(SIGBREAK) && defined(WIN32)
-static SIGHANDLER_T old_sigbreak_handler = SIG_ERR;
-#endif
-
-/* var which if set indicates that the program should finish execution */
-
-SIG_ATOMIC_T got_exit_signal = 0;
-
-/* if next is set indicates the first signal handled in exit_signal_handler */
-
-static volatile int exit_signal = 0;
-
-/* signal handler that will be triggered to indicate that the program
- should finish its execution in a controlled manner as soon as possible.
- The first time this is called it will set got_exit_signal to one and
- store in exit_signal the signal that triggered its execution. */
-
-static RETSIGTYPE exit_signal_handler(int signum)
-{
- int old_errno = errno;
- if(got_exit_signal == 0) {
- got_exit_signal = 1;
- exit_signal = signum;
- }
- (void)signal(signum, exit_signal_handler);
- errno = old_errno;
-}
-
-static void install_signal_handlers(void)
-{
-#ifdef SIGHUP
- /* ignore SIGHUP signal */
- old_sighup_handler = signal(SIGHUP, SIG_IGN);
- if(old_sighup_handler == SIG_ERR)
- logmsg("cannot install SIGHUP handler: %s", strerror(errno));
-#endif
-#ifdef SIGPIPE
- /* ignore SIGPIPE signal */
- old_sigpipe_handler = signal(SIGPIPE, SIG_IGN);
- if(old_sigpipe_handler == SIG_ERR)
- logmsg("cannot install SIGPIPE handler: %s", strerror(errno));
-#endif
-#ifdef SIGALRM
- /* ignore SIGALRM signal */
- old_sigalrm_handler = signal(SIGALRM, SIG_IGN);
- if(old_sigalrm_handler == SIG_ERR)
- logmsg("cannot install SIGALRM handler: %s", strerror(errno));
-#endif
-#ifdef SIGINT
- /* handle SIGINT signal with our exit_signal_handler */
- old_sigint_handler = signal(SIGINT, exit_signal_handler);
- if(old_sigint_handler == SIG_ERR)
- logmsg("cannot install SIGINT handler: %s", strerror(errno));
- else
- siginterrupt(SIGINT, 1);
-#endif
-#ifdef SIGTERM
- /* handle SIGTERM signal with our exit_signal_handler */
- old_sigterm_handler = signal(SIGTERM, exit_signal_handler);
- if(old_sigterm_handler == SIG_ERR)
- logmsg("cannot install SIGTERM handler: %s", strerror(errno));
- else
- siginterrupt(SIGTERM, 1);
-#endif
-#if defined(SIGBREAK) && defined(WIN32)
- /* handle SIGBREAK signal with our exit_signal_handler */
- old_sigbreak_handler = signal(SIGBREAK, exit_signal_handler);
- if(old_sigbreak_handler == SIG_ERR)
- logmsg("cannot install SIGBREAK handler: %s", strerror(errno));
- else
- siginterrupt(SIGBREAK, 1);
-#endif
-}
-
-static void restore_signal_handlers(void)
-{
-#ifdef SIGHUP
- if(SIG_ERR != old_sighup_handler)
- (void)signal(SIGHUP, old_sighup_handler);
-#endif
-#ifdef SIGPIPE
- if(SIG_ERR != old_sigpipe_handler)
- (void)signal(SIGPIPE, old_sigpipe_handler);
-#endif
-#ifdef SIGALRM
- if(SIG_ERR != old_sigalrm_handler)
- (void)signal(SIGALRM, old_sigalrm_handler);
-#endif
-#ifdef SIGINT
- if(SIG_ERR != old_sigint_handler)
- (void)signal(SIGINT, old_sigint_handler);
-#endif
-#ifdef SIGTERM
- if(SIG_ERR != old_sigterm_handler)
- (void)signal(SIGTERM, old_sigterm_handler);
-#endif
-#if defined(SIGBREAK) && defined(WIN32)
- if(SIG_ERR != old_sigbreak_handler)
- (void)signal(SIGBREAK, old_sigbreak_handler);
-#endif
-}
-
static int ProcessRequest(struct httprequest *req)
{
char *line = &req->reqbuf[req->checkindex];
@@ -379,8 +247,6 @@ static int ProcessRequest(struct httprequest *req)
/* get the number after it */
if(ptr) {
FILE *stream;
- char *filename;
-
if((strlen(doc) + strlen(request)) < 200)
msnprintf(logbuf, sizeof(logbuf), "Got request: %s %s %s/%d.%d",
request, doc, prot_str, prot_major, prot_minor);
@@ -420,13 +286,11 @@ static int ProcessRequest(struct httprequest *req)
req->testno, req->partno);
logmsg("%s", logbuf);
- filename = test2file(req->testno);
+ stream = test2fopen(req->testno);
- stream = fopen(filename, "rb");
if(!stream) {
int error = errno;
logmsg("fopen() failed with error: %d %s", error, strerror(error));
- logmsg("Error opening file: %s", filename);
logmsg("Couldn't open test file %ld", req->testno);
req->open = FALSE; /* closes connection */
return 1; /* done */
@@ -981,17 +845,13 @@ static int send_doc(curl_socket_t sock, struct httprequest *req)
count = strlen(buffer);
}
else {
- char *filename = test2file(req->testno);
+ FILE *stream = test2fopen(req->testno);
char partbuf[80]="data";
- FILE *stream;
if(0 != req->partno)
msnprintf(partbuf, sizeof(partbuf), "data%ld", req->partno);
-
- stream = fopen(filename, "rb");
if(!stream) {
error = errno;
logmsg("fopen() failed with error: %d %s", error, strerror(error));
- logmsg("Error opening file: %s", filename);
logmsg("Couldn't open test file");
return 0;
}
@@ -1011,11 +871,10 @@ static int send_doc(curl_socket_t sock, struct httprequest *req)
}
/* re-open the same file again */
- stream = fopen(filename, "rb");
+ stream = test2fopen(req->testno);
if(!stream) {
error = errno;
logmsg("fopen() failed with error: %d %s", error, strerror(error));
- logmsg("Error opening file: %s", filename);
logmsg("Couldn't open test file");
free(ptr);
return 0;
@@ -1190,6 +1049,7 @@ int main(int argc, char *argv[])
int flag;
unsigned short port = DEFAULT_PORT;
const char *pidname = ".rtsp.pid";
+ const char *portfile = NULL;
struct httprequest req;
int rc;
int error;
@@ -1216,6 +1076,11 @@ int main(int argc, char *argv[])
if(argc>arg)
pidname = argv[arg++];
}
+ else if(!strcmp("--portfile", argv[arg])) {
+ arg++;
+ if(argc>arg)
+ portfile = argv[arg++];
+ }
else if(!strcmp("--logfile", argv[arg])) {
arg++;
if(argc>arg)
@@ -1240,12 +1105,6 @@ int main(int argc, char *argv[])
if(argc>arg) {
char *endptr;
unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
- if((endptr != argv[arg] + strlen(argv[arg])) ||
- (ulnum < 1025UL) || (ulnum > 65535UL)) {
- fprintf(stderr, "rtspd: invalid --port argument (%s)\n",
- argv[arg]);
- return 0;
- }
port = curlx_ultous(ulnum);
arg++;
}
@@ -1262,6 +1121,7 @@ int main(int argc, char *argv[])
" --version\n"
" --logfile [file]\n"
" --pidfile [file]\n"
+ " --portfile [file]\n"
" --ipv4\n"
" --ipv6\n"
" --port [port]\n"
@@ -1275,7 +1135,7 @@ int main(int argc, char *argv[])
atexit(win32_cleanup);
#endif
- install_signal_handlers();
+ install_signal_handlers(false);
pid = (long)getpid();
@@ -1329,6 +1189,49 @@ int main(int argc, char *argv[])
goto server_cleanup;
}
+ if(!port) {
+ /* The system was supposed to choose a port number, figure out which
+ port we actually got and update the listener port value with it. */
+ curl_socklen_t la_size;
+ srvr_sockaddr_union_t localaddr;
+#ifdef ENABLE_IPV6
+ if(!use_ipv6)
+#endif
+ la_size = sizeof(localaddr.sa4);
+#ifdef ENABLE_IPV6
+ else
+ la_size = sizeof(localaddr.sa6);
+#endif
+ memset(&localaddr.sa, 0, (size_t)la_size);
+ if(getsockname(sock, &localaddr.sa, &la_size) < 0) {
+ error = SOCKERRNO;
+ logmsg("getsockname() failed with error: (%d) %s",
+ error, strerror(error));
+ sclose(sock);
+ goto server_cleanup;
+ }
+ switch(localaddr.sa.sa_family) {
+ case AF_INET:
+ port = ntohs(localaddr.sa4.sin_port);
+ break;
+#ifdef ENABLE_IPV6
+ case AF_INET6:
+ port = ntohs(localaddr.sa6.sin6_port);
+ break;
+#endif
+ default:
+ break;
+ }
+ if(!port) {
+ /* Real failure, listener port shall not be zero beyond this point. */
+ logmsg("Apparently getsockname() succeeded, with listener port zero.");
+ logmsg("A valid reason for this failure is a binary built without");
+ logmsg("proper network library linkage. This might not be the only");
+ logmsg("reason, but double check it before anything else.");
+ sclose(sock);
+ goto server_cleanup;
+ }
+ }
logmsg("Running %s version on port %d", ipv_inuse, (int)port);
/* start accepting connections */
@@ -1349,6 +1252,12 @@ int main(int argc, char *argv[])
if(!wrotepidfile)
goto server_cleanup;
+ if(portfile) {
+ wrotepidfile = write_portfile(portfile, port);
+ if(!wrotepidfile)
+ goto server_cleanup;
+ }
+
for(;;) {
msgsock = accept(sock, NULL, NULL);
@@ -1465,7 +1374,7 @@ server_cleanup:
clear_advisor_read_lock(SERVERLOGS_LOCK);
}
- restore_signal_handlers();
+ restore_signal_handlers(false);
if(got_exit_signal) {
logmsg("========> %s rtspd (port: %d pid: %ld) exits with signal (%d)",
diff --git a/tests/server/server_setup.h b/tests/server/server_setup.h
index 76c462369..1f2cef10c 100644
--- a/tests/server/server_setup.h
+++ b/tests/server/server_setup.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/tests/server/server_sockaddr.h b/tests/server/server_sockaddr.h
index bbcab8346..c3602e1cf 100644
--- a/tests/server/server_sockaddr.h
+++ b/tests/server/server_sockaddr.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/tests/server/sockfilt.c b/tests/server/sockfilt.c
index 569be6f6c..84c72f960 100644
--- a/tests/server/sockfilt.c
+++ b/tests/server/sockfilt.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -147,138 +147,6 @@ enum sockmode {
ACTIVE_DISCONNECT /* as a client, disconnected from server */
};
-/* do-nothing macro replacement for systems which lack siginterrupt() */
-
-#ifndef HAVE_SIGINTERRUPT
-#define siginterrupt(x,y) do {} while(0)
-#endif
-
-/* vars used to keep around previous signal handlers */
-
-typedef RETSIGTYPE (*SIGHANDLER_T)(int);
-
-#ifdef SIGHUP
-static SIGHANDLER_T old_sighup_handler = SIG_ERR;
-#endif
-
-#ifdef SIGPIPE
-static SIGHANDLER_T old_sigpipe_handler = SIG_ERR;
-#endif
-
-#ifdef SIGALRM
-static SIGHANDLER_T old_sigalrm_handler = SIG_ERR;
-#endif
-
-#ifdef SIGINT
-static SIGHANDLER_T old_sigint_handler = SIG_ERR;
-#endif
-
-#ifdef SIGTERM
-static SIGHANDLER_T old_sigterm_handler = SIG_ERR;
-#endif
-
-#if defined(SIGBREAK) && defined(WIN32)
-static SIGHANDLER_T old_sigbreak_handler = SIG_ERR;
-#endif
-
-/* var which if set indicates that the program should finish execution */
-
-SIG_ATOMIC_T got_exit_signal = 0;
-
-/* if next is set indicates the first signal handled in exit_signal_handler */
-
-static volatile int exit_signal = 0;
-
-/* signal handler that will be triggered to indicate that the program
- should finish its execution in a controlled manner as soon as possible.
- The first time this is called it will set got_exit_signal to one and
- store in exit_signal the signal that triggered its execution. */
-
-static RETSIGTYPE exit_signal_handler(int signum)
-{
- int old_errno = errno;
- if(got_exit_signal == 0) {
- got_exit_signal = 1;
- exit_signal = signum;
- }
- (void)signal(signum, exit_signal_handler);
- errno = old_errno;
-}
-
-static void install_signal_handlers(void)
-{
-#ifdef SIGHUP
- /* ignore SIGHUP signal */
- old_sighup_handler = signal(SIGHUP, SIG_IGN);
- if(old_sighup_handler == SIG_ERR)
- logmsg("cannot install SIGHUP handler: %s", strerror(errno));
-#endif
-#ifdef SIGPIPE
- /* ignore SIGPIPE signal */
- old_sigpipe_handler = signal(SIGPIPE, SIG_IGN);
- if(old_sigpipe_handler == SIG_ERR)
- logmsg("cannot install SIGPIPE handler: %s", strerror(errno));
-#endif
-#ifdef SIGALRM
- /* ignore SIGALRM signal */
- old_sigalrm_handler = signal(SIGALRM, SIG_IGN);
- if(old_sigalrm_handler == SIG_ERR)
- logmsg("cannot install SIGALRM handler: %s", strerror(errno));
-#endif
-#ifdef SIGINT
- /* handle SIGINT signal with our exit_signal_handler */
- old_sigint_handler = signal(SIGINT, exit_signal_handler);
- if(old_sigint_handler == SIG_ERR)
- logmsg("cannot install SIGINT handler: %s", strerror(errno));
- else
- siginterrupt(SIGINT, 1);
-#endif
-#ifdef SIGTERM
- /* handle SIGTERM signal with our exit_signal_handler */
- old_sigterm_handler = signal(SIGTERM, exit_signal_handler);
- if(old_sigterm_handler == SIG_ERR)
- logmsg("cannot install SIGTERM handler: %s", strerror(errno));
- else
- siginterrupt(SIGTERM, 1);
-#endif
-#if defined(SIGBREAK) && defined(WIN32)
- /* handle SIGBREAK signal with our exit_signal_handler */
- old_sigbreak_handler = signal(SIGBREAK, exit_signal_handler);
- if(old_sigbreak_handler == SIG_ERR)
- logmsg("cannot install SIGBREAK handler: %s", strerror(errno));
- else
- siginterrupt(SIGBREAK, 1);
-#endif
-}
-
-static void restore_signal_handlers(void)
-{
-#ifdef SIGHUP
- if(SIG_ERR != old_sighup_handler)
- (void)signal(SIGHUP, old_sighup_handler);
-#endif
-#ifdef SIGPIPE
- if(SIG_ERR != old_sigpipe_handler)
- (void)signal(SIGPIPE, old_sigpipe_handler);
-#endif
-#ifdef SIGALRM
- if(SIG_ERR != old_sigalrm_handler)
- (void)signal(SIGALRM, old_sigalrm_handler);
-#endif
-#ifdef SIGINT
- if(SIG_ERR != old_sigint_handler)
- (void)signal(SIGINT, old_sigint_handler);
-#endif
-#ifdef SIGTERM
- if(SIG_ERR != old_sigterm_handler)
- (void)signal(SIGTERM, old_sigterm_handler);
-#endif
-#if defined(SIGBREAK) && defined(WIN32)
- if(SIG_ERR != old_sigbreak_handler)
- (void)signal(SIGBREAK, old_sigbreak_handler);
-#endif
-}
-
#ifdef WIN32
/*
* read-wrapper to support reading from stdin on Windows.
@@ -532,22 +400,26 @@ static void lograw(unsigned char *buffer, ssize_t len)
*/
struct select_ws_wait_data {
HANDLE handle; /* actual handle to wait for during select */
- HANDLE event; /* internal event to abort waiting thread */
+ HANDLE signal; /* internal event to signal handle trigger */
+ HANDLE abort; /* internal event to abort waiting thread */
+ HANDLE mutex; /* mutex to prevent event race-condition */
};
static DWORD WINAPI select_ws_wait_thread(LPVOID lpParameter)
{
struct select_ws_wait_data *data;
- HANDLE handle, handles[2];
+ HANDLE mutex, signal, handle, handles[2];
INPUT_RECORD inputrecord;
LARGE_INTEGER size, pos;
- DWORD type, length;
+ DWORD type, length, ret;
/* retrieve handles from internal structure */
data = (struct select_ws_wait_data *) lpParameter;
if(data) {
handle = data->handle;
- handles[0] = data->event;
+ handles[0] = data->abort;
handles[1] = handle;
+ signal = data->signal;
+ mutex = data->mutex;
free(data);
}
else
@@ -567,29 +439,41 @@ static DWORD WINAPI select_ws_wait_thread(LPVOID lpParameter)
*/
while(WaitForMultipleObjectsEx(1, handles, FALSE, 0, FALSE)
== WAIT_TIMEOUT) {
- /* get total size of file */
- length = 0;
- size.QuadPart = 0;
- size.LowPart = GetFileSize(handle, &length);
- if((size.LowPart != INVALID_FILE_SIZE) ||
- (GetLastError() == NO_ERROR)) {
- size.HighPart = length;
- /* get the current position within the file */
- pos.QuadPart = 0;
- pos.LowPart = SetFilePointer(handle, 0, &pos.HighPart,
- FILE_CURRENT);
- if((pos.LowPart != INVALID_SET_FILE_POINTER) ||
+ ret = WaitForSingleObjectEx(mutex, 0, FALSE);
+ if(ret == WAIT_OBJECT_0) {
+ /* get total size of file */
+ length = 0;
+ size.QuadPart = 0;
+ size.LowPart = GetFileSize(handle, &length);
+ if((size.LowPart != INVALID_FILE_SIZE) ||
(GetLastError() == NO_ERROR)) {
- /* compare position with size, abort if not equal */
- if(size.QuadPart == pos.QuadPart) {
- /* sleep and continue waiting */
- SleepEx(0, FALSE);
- continue;
+ size.HighPart = length;
+ /* get the current position within the file */
+ pos.QuadPart = 0;
+ pos.LowPart = SetFilePointer(handle, 0, &pos.HighPart,
+ FILE_CURRENT);
+ if((pos.LowPart != INVALID_SET_FILE_POINTER) ||
+ (GetLastError() == NO_ERROR)) {
+ /* compare position with size, abort if not equal */
+ if(size.QuadPart == pos.QuadPart) {
+ /* sleep and continue waiting */
+ SleepEx(0, FALSE);
+ ReleaseMutex(mutex);
+ continue;
+ }
}
}
+ /* there is some data available, stop waiting */
+ logmsg("[select_ws_wait_thread] data available, DISK: %p", handle);
+ SetEvent(signal);
+ ReleaseMutex(mutex);
+ break;
+ }
+ else if(ret == WAIT_ABANDONED) {
+ /* we are not allowed to process this event, because select_ws
+ is post-processing the signalled events and we must exit. */
+ break;
}
- /* there is some data available, stop waiting */
- break;
}
break;
@@ -603,22 +487,33 @@ static DWORD WINAPI select_ws_wait_thread(LPVOID lpParameter)
*/
while(WaitForMultipleObjectsEx(2, handles, FALSE, INFINITE, FALSE)
== WAIT_OBJECT_0 + 1) {
- /* check if this is an actual console handle */
- length = 0;
- if(GetConsoleMode(handle, &length)) {
- /* retrieve an event from the console buffer */
- length = 0;
- if(PeekConsoleInput(handle, &inputrecord, 1, &length)) {
- /* check if the event is not an actual key-event */
- if(length == 1 && inputrecord.EventType != KEY_EVENT) {
- /* purge the non-key-event and continue waiting */
- ReadConsoleInput(handle, &inputrecord, 1, &length);
- continue;
+ ret = WaitForSingleObjectEx(mutex, 0, FALSE);
+ if(ret == WAIT_OBJECT_0) {
+ /* check if this is an actual console handle */
+ if(GetConsoleMode(handle, &ret)) {
+ /* retrieve an event from the console buffer */
+ length = 0;
+ if(PeekConsoleInput(handle, &inputrecord, 1, &length)) {
+ /* check if the event is not an actual key-event */
+ if(length == 1 && inputrecord.EventType != KEY_EVENT) {
+ /* purge the non-key-event and continue waiting */
+ ReadConsoleInput(handle, &inputrecord, 1, &length);
+ ReleaseMutex(mutex);
+ continue;
+ }
}
}
+ /* there is some data available, stop waiting */
+ logmsg("[select_ws_wait_thread] data available, CHAR: %p", handle);
+ SetEvent(signal);
+ ReleaseMutex(mutex);
+ break;
+ }
+ else if(ret == WAIT_ABANDONED) {
+ /* we are not allowed to process this event, because select_ws
+ is post-processing the signalled events and we must exit. */
+ break;
}
- /* there is some data available, stop waiting */
- break;
}
break;
@@ -632,36 +527,65 @@ static DWORD WINAPI select_ws_wait_thread(LPVOID lpParameter)
*/
while(WaitForMultipleObjectsEx(1, handles, FALSE, 0, FALSE)
== WAIT_TIMEOUT) {
- /* peek into the pipe and retrieve the amount of data available */
- length = 0;
- if(PeekNamedPipe(handle, NULL, 0, NULL, &length, NULL)) {
- /* if there is no data available, sleep and continue waiting */
- if(length == 0) {
- SleepEx(0, FALSE);
- continue;
+ ret = WaitForSingleObjectEx(mutex, 0, FALSE);
+ if(ret == WAIT_OBJECT_0) {
+ /* peek into the pipe and retrieve the amount of data available */
+ length = 0;
+ if(PeekNamedPipe(handle, NULL, 0, NULL, &length, NULL)) {
+ /* if there is no data available, sleep and continue waiting */
+ if(length == 0) {
+ SleepEx(0, FALSE);
+ ReleaseMutex(mutex);
+ continue;
+ }
+ else {
+ logmsg("[select_ws_wait_thread] PeekNamedPipe len: %d", length);
+ }
}
- }
- else {
- /* if the pipe has been closed, sleep and continue waiting */
- if(GetLastError() == ERROR_BROKEN_PIPE) {
- SleepEx(0, FALSE);
- continue;
+ else {
+ /* if the pipe has NOT been closed, sleep and continue waiting */
+ ret = GetLastError();
+ if(ret != ERROR_BROKEN_PIPE) {
+ logmsg("[select_ws_wait_thread] PeekNamedPipe error: %d", ret);
+ SleepEx(0, FALSE);
+ ReleaseMutex(mutex);
+ continue;
+ }
+ else {
+ logmsg("[select_ws_wait_thread] pipe closed, PIPE: %p", handle);
+ }
}
+ /* there is some data available, stop waiting */
+ logmsg("[select_ws_wait_thread] data available, PIPE: %p", handle);
+ SetEvent(signal);
+ ReleaseMutex(mutex);
+ break;
+ }
+ else if(ret == WAIT_ABANDONED) {
+ /* we are not allowed to process this event, because select_ws
+ is post-processing the signalled events and we must exit. */
+ break;
}
- /* there is some data available, stop waiting */
- break;
}
break;
default:
/* The handle has an unknown type, try to wait on it */
- WaitForMultipleObjectsEx(2, handles, FALSE, INFINITE, FALSE);
+ if(WaitForMultipleObjectsEx(2, handles, FALSE, INFINITE, FALSE)
+ == WAIT_OBJECT_0 + 1) {
+ if(WaitForSingleObjectEx(mutex, 0, FALSE) == WAIT_OBJECT_0) {
+ logmsg("[select_ws_wait_thread] data available, HANDLE: %p", handle);
+ SetEvent(signal);
+ ReleaseMutex(mutex);
+ }
+ }
break;
}
return 0;
}
-static HANDLE select_ws_wait(HANDLE handle, HANDLE event)
+static HANDLE select_ws_wait(HANDLE handle, HANDLE signal,
+ HANDLE abort, HANDLE mutex)
{
struct select_ws_wait_data *data;
HANDLE thread = NULL;
@@ -670,7 +594,9 @@ static HANDLE select_ws_wait(HANDLE handle, HANDLE event)
data = malloc(sizeof(struct select_ws_wait_data));
if(data) {
data->handle = handle;
- data->event = event;
+ data->signal = signal;
+ data->abort = abort;
+ data->mutex = mutex;
/* launch waiting thread */
thread = CreateThread(NULL, 0,
@@ -686,23 +612,24 @@ static HANDLE select_ws_wait(HANDLE handle, HANDLE event)
return thread;
}
struct select_ws_data {
- curl_socket_t fd; /* the original input handle (indexed by fds) */
- curl_socket_t wsasock; /* the internal socket handle (indexed by wsa) */
- WSAEVENT wsaevent; /* the internal WINSOCK2 event (indexed by wsa) */
- HANDLE thread; /* the internal threads handle (indexed by thd) */
+ int fd; /* provided file descriptor (indexed by nfd) */
+ long wsastate; /* internal pre-select state (indexed by nfd) */
+ curl_socket_t wsasock; /* internal socket handle (indexed by nws) */
+ WSAEVENT wsaevent; /* internal select event (indexed by nws) */
+ HANDLE signal; /* internal thread signal (indexed by nth) */
+ HANDLE thread; /* internal thread handle (indexed by nth) */
};
static int select_ws(int nfds, fd_set *readfds, fd_set *writefds,
- fd_set *exceptfds, struct timeval *timeout)
+ fd_set *exceptfds, struct timeval *tv)
{
- DWORD milliseconds, wait, idx;
- WSANETWORKEVENTS wsanetevents;
+ HANDLE abort, mutex, signal, handle, *handles;
+ DWORD timeout_ms, wait, nfd, nth, nws, i;
+ fd_set readsock, writesock, exceptsock;
struct select_ws_data *data;
- HANDLE handle, *handles;
+ WSANETWORKEVENTS wsaevents;
+ curl_socket_t wsasock;
+ int error, ret, fd;
WSAEVENT wsaevent;
- int error, fds;
- HANDLE waitevent = NULL;
- DWORD nfd = 0, thd = 0, wsa = 0;
- int ret = 0;
/* check if the input value is valid */
if(nfds < 0) {
@@ -710,15 +637,31 @@ static int select_ws(int nfds, fd_set *readfds, fd_set *writefds,
return -1;
}
+ /* convert struct timeval to milliseconds */
+ if(tv) {
+ timeout_ms = (tv->tv_sec*1000) + (DWORD)(((double)tv->tv_usec)/1000.0);
+ }
+ else {
+ timeout_ms = INFINITE;
+ }
+
/* check if we got descriptors, sleep in case we got none */
if(!nfds) {
- Sleep((timeout->tv_sec*1000)+(DWORD)(((double)timeout->tv_usec)/1000.0));
+ SleepEx(timeout_ms, FALSE);
return 0;
}
- /* create internal event to signal waiting threads */
- waitevent = CreateEvent(NULL, TRUE, FALSE, NULL);
- if(!waitevent) {
+ /* create internal event to abort waiting threads */
+ abort = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if(!abort) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /* create internal mutex to lock event handling in threads */
+ mutex = CreateMutex(NULL, FALSE, NULL);
+ if(!mutex) {
+ CloseHandle(abort);
errno = ENOMEM;
return -1;
}
@@ -726,7 +669,8 @@ static int select_ws(int nfds, fd_set *readfds, fd_set *writefds,
/* allocate internal array for the internal data */
data = calloc(nfds, sizeof(struct select_ws_data));
if(data == NULL) {
- CloseHandle(waitevent);
+ CloseHandle(abort);
+ CloseHandle(mutex);
errno = ENOMEM;
return -1;
}
@@ -734,61 +678,106 @@ static int select_ws(int nfds, fd_set *readfds, fd_set *writefds,
/* allocate internal array for the internal event handles */
handles = calloc(nfds, sizeof(HANDLE));
if(handles == NULL) {
- CloseHandle(waitevent);
+ CloseHandle(abort);
+ CloseHandle(mutex);
free(data);
errno = ENOMEM;
return -1;
}
/* loop over the handles in the input descriptor sets */
- for(fds = 0; fds < nfds; fds++) {
- long networkevents = 0;
+ nfd = 0; /* number of handled file descriptors */
+ nth = 0; /* number of interal waiting threads */
+ nws = 0; /* number of handled WINSOCK sockets */
+ for(fd = 0; fd < nfds; fd++) {
+ wsasock = curlx_sitosk(fd);
+ wsaevents.lNetworkEvents = 0;
handles[nfd] = 0;
- if(FD_ISSET(fds, readfds))
- networkevents |= FD_READ|FD_ACCEPT|FD_CLOSE;
+ FD_ZERO(&readsock);
+ FD_ZERO(&writesock);
+ FD_ZERO(&exceptsock);
- if(FD_ISSET(fds, writefds))
- networkevents |= FD_WRITE|FD_CONNECT;
+ if(FD_ISSET(wsasock, readfds)) {
+ FD_SET(wsasock, &readsock);
+ wsaevents.lNetworkEvents |= FD_READ|FD_ACCEPT|FD_CLOSE;
+ }
+
+ if(FD_ISSET(wsasock, writefds)) {
+ FD_SET(wsasock, &writesock);
+ wsaevents.lNetworkEvents |= FD_WRITE|FD_CONNECT;
+ }
- if(FD_ISSET(fds, exceptfds))
- networkevents |= FD_OOB|FD_CLOSE;
+ if(FD_ISSET(wsasock, exceptfds)) {
+ FD_SET(wsasock, &exceptsock);
+ wsaevents.lNetworkEvents |= FD_OOB;
+ }
/* only wait for events for which we actually care */
- if(networkevents) {
- data[nfd].fd = curlx_sitosk(fds);
- if(fds == fileno(stdin)) {
- handle = GetStdHandle(STD_INPUT_HANDLE);
- handle = select_ws_wait(handle, waitevent);
- handles[nfd] = handle;
- data[thd].thread = handle;
- thd++;
+ if(wsaevents.lNetworkEvents) {
+ data[nfd].fd = fd;
+ if(fd == fileno(stdin)) {
+ signal = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if(signal) {
+ handle = GetStdHandle(STD_INPUT_HANDLE);
+ handle = select_ws_wait(handle, signal, abort, mutex);
+ if(handle) {
+ handles[nfd] = signal;
+ data[nth].signal = signal;
+ data[nth].thread = handle;
+ nth++;
+ }
+ else {
+ CloseHandle(signal);
+ }
+ }
}
- else if(fds == fileno(stdout)) {
+ else if(fd == fileno(stdout)) {
handles[nfd] = GetStdHandle(STD_OUTPUT_HANDLE);
}
- else if(fds == fileno(stderr)) {
+ else if(fd == fileno(stderr)) {
handles[nfd] = GetStdHandle(STD_ERROR_HANDLE);
}
else {
wsaevent = WSACreateEvent();
if(wsaevent != WSA_INVALID_EVENT) {
- error = WSAEventSelect(fds, wsaevent, networkevents);
+ error = WSAEventSelect(wsasock, wsaevent, wsaevents.lNetworkEvents);
if(error != SOCKET_ERROR) {
- handle = (HANDLE) wsaevent;
- handles[nfd] = handle;
- data[wsa].wsasock = curlx_sitosk(fds);
- data[wsa].wsaevent = wsaevent;
- wsa++;
+ handles[nfd] = (HANDLE)wsaevent;
+ data[nws].wsasock = wsasock;
+ data[nws].wsaevent = wsaevent;
+ data[nfd].wsastate = 0;
+ tv->tv_sec = 0;
+ tv->tv_usec = 0;
+ /* check if the socket is already ready */
+ if(select(fd + 1, &readsock, &writesock, &exceptsock, tv) == 1) {
+ logmsg("[select_ws] socket %d is ready", fd);
+ WSASetEvent(wsaevent);
+ if(FD_ISSET(wsasock, &readsock))
+ data[nfd].wsastate |= FD_READ;
+ if(FD_ISSET(wsasock, &writesock))
+ data[nfd].wsastate |= FD_WRITE;
+ if(FD_ISSET(wsasock, &exceptsock))
+ data[nfd].wsastate |= FD_OOB;
+ }
+ nws++;
}
else {
- curl_socket_t socket = curlx_sitosk(fds);
WSACloseEvent(wsaevent);
- handle = (HANDLE) socket;
- handle = select_ws_wait(handle, waitevent);
- handles[nfd] = handle;
- data[thd].thread = handle;
- thd++;
+ signal = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if(signal) {
+ handle = (HANDLE)wsasock;
+ handle = select_ws_wait(handle, signal, abort, mutex);
+ if(handle) {
+ handles[nfd] = signal;
+ data[nth].signal = signal;
+ data[nth].thread = handle;
+ nth++;
+ }
+ else {
+ CloseHandle(signal);
+ }
+ }
}
}
}
@@ -796,104 +785,94 @@ static int select_ws(int nfds, fd_set *readfds, fd_set *writefds,
}
}
- /* convert struct timeval to milliseconds */
- if(timeout) {
- milliseconds = ((timeout->tv_sec * 1000) + (timeout->tv_usec / 1000));
- }
- else {
- milliseconds = INFINITE;
- }
-
/* wait for one of the internal handles to trigger */
- wait = WaitForMultipleObjectsEx(nfd, handles, FALSE, milliseconds, FALSE);
+ wait = WaitForMultipleObjectsEx(nfd, handles, FALSE, timeout_ms, FALSE);
- /* signal the event handle for the waiting threads */
- SetEvent(waitevent);
+ /* wait for internal mutex to lock event handling in threads */
+ WaitForSingleObjectEx(mutex, INFINITE, FALSE);
/* loop over the internal handles returned in the descriptors */
- for(idx = 0; idx < nfd; idx++) {
- curl_socket_t sock = data[idx].fd;
- handle = handles[idx];
- fds = curlx_sktosi(sock);
+ ret = 0; /* number of ready file descriptors */
+ for(i = 0; i < nfd; i++) {
+ fd = data[i].fd;
+ handle = handles[i];
+ wsasock = curlx_sitosk(fd);
/* check if the current internal handle was triggered */
- if(wait != WAIT_FAILED && (wait - WAIT_OBJECT_0) <= idx &&
+ if(wait != WAIT_FAILED && (wait - WAIT_OBJECT_0) <= i &&
WaitForSingleObjectEx(handle, 0, FALSE) == WAIT_OBJECT_0) {
/* first handle stdin, stdout and stderr */
- if(fds == fileno(stdin)) {
+ if(fd == fileno(stdin)) {
/* stdin is never ready for write or exceptional */
- FD_CLR(sock, writefds);
- FD_CLR(sock, exceptfds);
+ FD_CLR(wsasock, writefds);
+ FD_CLR(wsasock, exceptfds);
}
- else if(fds == fileno(stdout) || fds == fileno(stderr)) {
+ else if(fd == fileno(stdout) || fd == fileno(stderr)) {
/* stdout and stderr are never ready for read or exceptional */
- FD_CLR(sock, readfds);
- FD_CLR(sock, exceptfds);
+ FD_CLR(wsasock, readfds);
+ FD_CLR(wsasock, exceptfds);
}
else {
/* try to handle the event with the WINSOCK2 functions */
- wsanetevents.lNetworkEvents = 0;
- error = WSAEnumNetworkEvents(fds, handle, &wsanetevents);
+ wsaevents.lNetworkEvents = 0;
+ error = WSAEnumNetworkEvents(wsasock, handle, &wsaevents);
if(error != SOCKET_ERROR) {
+ /* merge result from pre-check using select */
+ wsaevents.lNetworkEvents |= data[i].wsastate;
+
/* remove from descriptor set if not ready for read/accept/close */
- if(!(wsanetevents.lNetworkEvents & (FD_READ|FD_ACCEPT|FD_CLOSE)))
- FD_CLR(sock, readfds);
+ if(!(wsaevents.lNetworkEvents & (FD_READ|FD_ACCEPT|FD_CLOSE)))
+ FD_CLR(wsasock, readfds);
/* remove from descriptor set if not ready for write/connect */
- if(!(wsanetevents.lNetworkEvents & (FD_WRITE|FD_CONNECT)))
- FD_CLR(sock, writefds);
-
- /* HACK:
- * use exceptfds together with readfds to signal
- * that the connection was closed by the client.
- *
- * Reason: FD_CLOSE is only signaled once, sometimes
- * at the same time as FD_READ with data being available.
- * This means that recv/sread is not reliable to detect
- * that the connection is closed.
- */
+ if(!(wsaevents.lNetworkEvents & (FD_WRITE|FD_CONNECT)))
+ FD_CLR(wsasock, writefds);
+
/* remove from descriptor set if not exceptional */
- if(!(wsanetevents.lNetworkEvents & (FD_OOB|FD_CLOSE)))
- FD_CLR(sock, exceptfds);
+ if(!(wsaevents.lNetworkEvents & (FD_OOB)))
+ FD_CLR(wsasock, exceptfds);
}
}
/* check if the event has not been filtered using specific tests */
- if(FD_ISSET(sock, readfds) || FD_ISSET(sock, writefds) ||
- FD_ISSET(sock, exceptfds)) {
+ if(FD_ISSET(wsasock, readfds) || FD_ISSET(wsasock, writefds) ||
+ FD_ISSET(wsasock, exceptfds)) {
ret++;
}
}
else {
/* remove from all descriptor sets since this handle did not trigger */
- FD_CLR(sock, readfds);
- FD_CLR(sock, writefds);
- FD_CLR(sock, exceptfds);
+ FD_CLR(wsasock, readfds);
+ FD_CLR(wsasock, writefds);
+ FD_CLR(wsasock, exceptfds);
}
}
- for(fds = 0; fds < nfds; fds++) {
- if(FD_ISSET(fds, readfds))
- logmsg("select_ws: %d is readable", fds);
-
- if(FD_ISSET(fds, writefds))
- logmsg("select_ws: %d is writable", fds);
+ /* signal the event handle for the other waiting threads */
+ SetEvent(abort);
- if(FD_ISSET(fds, exceptfds))
- logmsg("select_ws: %d is excepted", fds);
+ for(fd = 0; fd < nfds; fd++) {
+ if(FD_ISSET(fd, readfds))
+ logmsg("[select_ws] %d is readable", fd);
+ if(FD_ISSET(fd, writefds))
+ logmsg("[select_ws] %d is writable", fd);
+ if(FD_ISSET(fd, exceptfds))
+ logmsg("[select_ws] %d is exceptional", fd);
}
- for(idx = 0; idx < wsa; idx++) {
- WSAEventSelect(data[idx].wsasock, NULL, 0);
- WSACloseEvent(data[idx].wsaevent);
+ for(i = 0; i < nws; i++) {
+ WSAEventSelect(data[i].wsasock, NULL, 0);
+ WSACloseEvent(data[i].wsaevent);
}
- for(idx = 0; idx < thd; idx++) {
- WaitForSingleObject(data[idx].thread, INFINITE);
- CloseHandle(data[idx].thread);
+ for(i = 0; i < nth; i++) {
+ WaitForSingleObjectEx(data[i].thread, INFINITE, FALSE);
+ CloseHandle(data[i].thread);
+ CloseHandle(data[i].signal);
}
- CloseHandle(waitevent);
+ CloseHandle(abort);
+ CloseHandle(mutex);
free(handles);
free(data);
@@ -972,9 +951,6 @@ static bool juggle(curl_socket_t *sockfdp,
else {
/* there's always a socket to wait for */
FD_SET(sockfd, &fds_read);
-#ifdef USE_WINSOCK
- FD_SET(sockfd, &fds_err);
-#endif
maxfd = (int)sockfd;
}
break;
@@ -985,9 +961,6 @@ static bool juggle(curl_socket_t *sockfdp,
/* sockfd turns CURL_SOCKET_BAD when our connection has been closed */
if(CURL_SOCKET_BAD != sockfd) {
FD_SET(sockfd, &fds_read);
-#ifdef USE_WINSOCK
- FD_SET(sockfd, &fds_err);
-#endif
maxfd = (int)sockfd;
}
else {
@@ -1165,11 +1138,7 @@ static bool juggle(curl_socket_t *sockfdp,
lograw(buffer, nread_socket);
}
- if(nread_socket <= 0
-#ifdef USE_WINSOCK
- || FD_ISSET(sockfd, &fds_err)
-#endif
- ) {
+ if(nread_socket <= 0) {
logmsg("====> Client disconnect");
if(!write_stdout("DISC\n", 5))
return FALSE;
@@ -1335,6 +1304,7 @@ int main(int argc, char *argv[])
curl_socket_t msgsock = CURL_SOCKET_BAD;
int wrotepidfile = 0;
const char *pidname = ".sockfilt.pid";
+ const char *portfile = NULL; /* none by default */
bool juggle_again;
int rc;
int error;
@@ -1362,6 +1332,11 @@ int main(int argc, char *argv[])
if(argc>arg)
pidname = argv[arg++];
}
+ else if(!strcmp("--portfile", argv[arg])) {
+ arg++;
+ if(argc > arg)
+ portfile = argv[arg++];
+ }
else if(!strcmp("--logfile", argv[arg])) {
arg++;
if(argc>arg)
@@ -1391,12 +1366,6 @@ int main(int argc, char *argv[])
if(argc>arg) {
char *endptr;
unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
- if((endptr != argv[arg] + strlen(argv[arg])) ||
- ((ulnum != 0UL) && ((ulnum < 1025UL) || (ulnum > 65535UL)))) {
- fprintf(stderr, "sockfilt: invalid --port argument (%s)\n",
- argv[arg]);
- return 0;
- }
port = curlx_ultous(ulnum);
arg++;
}
@@ -1451,7 +1420,7 @@ int main(int argc, char *argv[])
setmode(fileno(stderr), O_BINARY);
#endif
- install_signal_handlers();
+ install_signal_handlers(false);
#ifdef ENABLE_IPV6
if(!use_ipv6)
@@ -1532,6 +1501,13 @@ int main(int argc, char *argv[])
write_stdout("FAIL\n", 5);
goto sockfilt_cleanup;
}
+ if(portfile) {
+ wrotepidfile = write_portfile(portfile, port);
+ if(!wrotepidfile) {
+ write_stdout("FAIL\n", 5);
+ goto sockfilt_cleanup;
+ }
+ }
do {
juggle_again = juggle(&msgsock, sock, &mode);
@@ -1548,7 +1524,7 @@ sockfilt_cleanup:
if(wrotepidfile)
unlink(pidname);
- restore_signal_handlers();
+ restore_signal_handlers(false);
if(got_exit_signal) {
logmsg("============> sockfilt exits with signal (%d)", exit_signal);
diff --git a/tests/server/socksd.c b/tests/server/socksd.c
index 5e32bc904..e6e9c12b0 100644
--- a/tests/server/socksd.c
+++ b/tests/server/socksd.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -230,123 +230,6 @@ static void getconfig(void)
}
}
-
-/* do-nothing macro replacement for systems which lack siginterrupt() */
-
-#ifndef HAVE_SIGINTERRUPT
-#define siginterrupt(x,y) do {} while(0)
-#endif
-
-/* vars used to keep around previous signal handlers */
-
-typedef RETSIGTYPE (*SIGHANDLER_T)(int);
-
-#ifdef SIGHUP
-static SIGHANDLER_T old_sighup_handler = SIG_ERR;
-#endif
-
-#ifdef SIGPIPE
-static SIGHANDLER_T old_sigpipe_handler = SIG_ERR;
-#endif
-
-#ifdef SIGALRM
-static SIGHANDLER_T old_sigalrm_handler = SIG_ERR;
-#endif
-
-#ifdef SIGINT
-static SIGHANDLER_T old_sigint_handler = SIG_ERR;
-#endif
-
-#if defined(SIGBREAK) && defined(WIN32)
-static SIGHANDLER_T old_sigbreak_handler = SIG_ERR;
-#endif
-
-/* var which if set indicates that the program should finish execution */
-
-SIG_ATOMIC_T got_exit_signal = 0;
-
-/* if next is set indicates the first signal handled in exit_signal_handler */
-
-static volatile int exit_signal = 0;
-
-/* signal handler that will be triggered to indicate that the program
- should finish its execution in a controlled manner as soon as possible.
- The first time this is called it will set got_exit_signal to one and
- store in exit_signal the signal that triggered its execution. */
-
-static RETSIGTYPE exit_signal_handler(int signum)
-{
- int old_errno = errno;
- if(got_exit_signal == 0) {
- got_exit_signal = 1;
- exit_signal = signum;
- }
- (void)signal(signum, exit_signal_handler);
- errno = old_errno;
-}
-
-static void install_signal_handlers(void)
-{
-#ifdef SIGHUP
- /* ignore SIGHUP signal */
- old_sighup_handler = signal(SIGHUP, SIG_IGN);
- if(old_sighup_handler == SIG_ERR)
- logmsg("cannot install SIGHUP handler: %s", strerror(errno));
-#endif
-#ifdef SIGPIPE
- /* ignore SIGPIPE signal */
- old_sigpipe_handler = signal(SIGPIPE, SIG_IGN);
- if(old_sigpipe_handler == SIG_ERR)
- logmsg("cannot install SIGPIPE handler: %s", strerror(errno));
-#endif
-#ifdef SIGALRM
- /* ignore SIGALRM signal */
- old_sigalrm_handler = signal(SIGALRM, SIG_IGN);
- if(old_sigalrm_handler == SIG_ERR)
- logmsg("cannot install SIGALRM handler: %s", strerror(errno));
-#endif
-#ifdef SIGINT
- /* handle SIGINT signal with our exit_signal_handler */
- old_sigint_handler = signal(SIGINT, exit_signal_handler);
- if(old_sigint_handler == SIG_ERR)
- logmsg("cannot install SIGINT handler: %s", strerror(errno));
- else
- siginterrupt(SIGINT, 1);
-#endif
-#if defined(SIGBREAK) && defined(WIN32)
- /* handle SIGBREAK signal with our exit_signal_handler */
- old_sigbreak_handler = signal(SIGBREAK, exit_signal_handler);
- if(old_sigbreak_handler == SIG_ERR)
- logmsg("cannot install SIGBREAK handler: %s", strerror(errno));
- else
- siginterrupt(SIGBREAK, 1);
-#endif
-}
-
-static void restore_signal_handlers(void)
-{
-#ifdef SIGHUP
- if(SIG_ERR != old_sighup_handler)
- (void)signal(SIGHUP, old_sighup_handler);
-#endif
-#ifdef SIGPIPE
- if(SIG_ERR != old_sigpipe_handler)
- (void)signal(SIGPIPE, old_sigpipe_handler);
-#endif
-#ifdef SIGALRM
- if(SIG_ERR != old_sigalrm_handler)
- (void)signal(SIGALRM, old_sigalrm_handler);
-#endif
-#ifdef SIGINT
- if(SIG_ERR != old_sigint_handler)
- (void)signal(SIGINT, old_sigint_handler);
-#endif
-#if defined(SIGBREAK) && defined(WIN32)
- if(SIG_ERR != old_sigbreak_handler)
- (void)signal(SIGBREAK, old_sigbreak_handler);
-#endif
-}
-
static void loghex(unsigned char *buffer, ssize_t len)
{
char data[1200];
@@ -1000,6 +883,7 @@ int main(int argc, char *argv[])
curl_socket_t msgsock = CURL_SOCKET_BAD;
int wrotepidfile = 0;
const char *pidname = ".socksd.pid";
+ const char *portfile = NULL;
bool juggle_again;
int error;
int arg = 1;
@@ -1020,6 +904,11 @@ int main(int argc, char *argv[])
if(argc>arg)
pidname = argv[arg++];
}
+ else if(!strcmp("--portfile", argv[arg])) {
+ arg++;
+ if(argc>arg)
+ portfile = argv[arg++];
+ }
else if(!strcmp("--config", argv[arg])) {
arg++;
if(argc>arg)
@@ -1060,12 +949,6 @@ int main(int argc, char *argv[])
if(argc>arg) {
char *endptr;
unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
- if((endptr != argv[arg] + strlen(argv[arg])) ||
- ((ulnum != 0UL) && ((ulnum < 1025UL) || (ulnum > 65535UL)))) {
- fprintf(stderr, "socksd: invalid --port argument (%s)\n",
- argv[arg]);
- return 0;
- }
port = curlx_ultous(ulnum);
arg++;
}
@@ -1078,6 +961,7 @@ int main(int argc, char *argv[])
" --version\n"
" --logfile [file]\n"
" --pidfile [file]\n"
+ " --portfile [file]\n"
" --ipv4\n"
" --ipv6\n"
" --bindonly\n"
@@ -1095,7 +979,7 @@ int main(int argc, char *argv[])
setmode(fileno(stderr), O_BINARY);
#endif
- install_signal_handlers();
+ install_signal_handlers(false);
#ifdef ENABLE_IPV6
if(!use_ipv6)
@@ -1130,6 +1014,13 @@ int main(int argc, char *argv[])
goto socks5_cleanup;
}
+ if(portfile) {
+ wrotepidfile = write_portfile(portfile, port);
+ if(!wrotepidfile) {
+ goto socks5_cleanup;
+ }
+ }
+
do {
juggle_again = incoming(sock);
} while(juggle_again);
@@ -1145,7 +1036,7 @@ socks5_cleanup:
if(wrotepidfile)
unlink(pidname);
- restore_signal_handlers();
+ restore_signal_handlers(false);
if(got_exit_signal) {
logmsg("============> socksd exits with signal (%d)", exit_signal);
diff --git a/tests/server/sws.c b/tests/server/sws.c
index b397ed19b..48ea26d5e 100644
--- a/tests/server/sws.c
+++ b/tests/server/sws.c
@@ -213,141 +213,9 @@ static const char *doc404 = "HTTP/1.1 404 Not Found\r\n"
"The requested URL was not found on this server.\n"
"<P><HR><ADDRESS>" SWSVERSION "</ADDRESS>\n" "</BODY></HTML>\n";
-/* do-nothing macro replacement for systems which lack siginterrupt() */
-
-#ifndef HAVE_SIGINTERRUPT
-#define siginterrupt(x,y) do {} while(0)
-#endif
-
-/* vars used to keep around previous signal handlers */
-
-typedef RETSIGTYPE (*SIGHANDLER_T)(int);
-
-#ifdef SIGHUP
-static SIGHANDLER_T old_sighup_handler = SIG_ERR;
-#endif
-
-#ifdef SIGPIPE
-static SIGHANDLER_T old_sigpipe_handler = SIG_ERR;
-#endif
-
-#ifdef SIGALRM
-static SIGHANDLER_T old_sigalrm_handler = SIG_ERR;
-#endif
-
-#ifdef SIGINT
-static SIGHANDLER_T old_sigint_handler = SIG_ERR;
-#endif
-
-#ifdef SIGTERM
-static SIGHANDLER_T old_sigterm_handler = SIG_ERR;
-#endif
-
-#if defined(SIGBREAK) && defined(WIN32)
-static SIGHANDLER_T old_sigbreak_handler = SIG_ERR;
-#endif
-
-/* var which if set indicates that the program should finish execution */
-
-SIG_ATOMIC_T got_exit_signal = 0;
-
-/* if next is set indicates the first signal handled in exit_signal_handler */
-
-static volatile int exit_signal = 0;
-
/* work around for handling trailing headers */
static int already_recv_zeroed_chunk = FALSE;
-/* signal handler that will be triggered to indicate that the program
- should finish its execution in a controlled manner as soon as possible.
- The first time this is called it will set got_exit_signal to one and
- store in exit_signal the signal that triggered its execution. */
-
-static RETSIGTYPE exit_signal_handler(int signum)
-{
- int old_errno = errno;
- if(got_exit_signal == 0) {
- got_exit_signal = 1;
- exit_signal = signum;
- }
- (void)signal(signum, exit_signal_handler);
- errno = old_errno;
-}
-
-static void install_signal_handlers(void)
-{
-#ifdef SIGHUP
- /* ignore SIGHUP signal */
- old_sighup_handler = signal(SIGHUP, SIG_IGN);
- if(old_sighup_handler == SIG_ERR)
- logmsg("cannot install SIGHUP handler: %s", strerror(errno));
-#endif
-#ifdef SIGPIPE
- /* ignore SIGPIPE signal */
- old_sigpipe_handler = signal(SIGPIPE, SIG_IGN);
- if(old_sigpipe_handler == SIG_ERR)
- logmsg("cannot install SIGPIPE handler: %s", strerror(errno));
-#endif
-#ifdef SIGALRM
- /* ignore SIGALRM signal */
- old_sigalrm_handler = signal(SIGALRM, SIG_IGN);
- if(old_sigalrm_handler == SIG_ERR)
- logmsg("cannot install SIGALRM handler: %s", strerror(errno));
-#endif
-#ifdef SIGINT
- /* handle SIGINT signal with our exit_signal_handler */
- old_sigint_handler = signal(SIGINT, exit_signal_handler);
- if(old_sigint_handler == SIG_ERR)
- logmsg("cannot install SIGINT handler: %s", strerror(errno));
- else
- siginterrupt(SIGINT, 1);
-#endif
-#ifdef SIGTERM
- /* handle SIGTERM signal with our exit_signal_handler */
- old_sigterm_handler = signal(SIGTERM, exit_signal_handler);
- if(old_sigterm_handler == SIG_ERR)
- logmsg("cannot install SIGTERM handler: %s", strerror(errno));
- else
- siginterrupt(SIGTERM, 1);
-#endif
-#if defined(SIGBREAK) && defined(WIN32)
- /* handle SIGBREAK signal with our exit_signal_handler */
- old_sigbreak_handler = signal(SIGBREAK, exit_signal_handler);
- if(old_sigbreak_handler == SIG_ERR)
- logmsg("cannot install SIGBREAK handler: %s", strerror(errno));
- else
- siginterrupt(SIGBREAK, 1);
-#endif
-}
-
-static void restore_signal_handlers(void)
-{
-#ifdef SIGHUP
- if(SIG_ERR != old_sighup_handler)
- (void)signal(SIGHUP, old_sighup_handler);
-#endif
-#ifdef SIGPIPE
- if(SIG_ERR != old_sigpipe_handler)
- (void)signal(SIGPIPE, old_sigpipe_handler);
-#endif
-#ifdef SIGALRM
- if(SIG_ERR != old_sigalrm_handler)
- (void)signal(SIGALRM, old_sigalrm_handler);
-#endif
-#ifdef SIGINT
- if(SIG_ERR != old_sigint_handler)
- (void)signal(SIGINT, old_sigint_handler);
-#endif
-#ifdef SIGTERM
- if(SIG_ERR != old_sigterm_handler)
- (void)signal(SIGTERM, old_sigterm_handler);
-#endif
-#if defined(SIGBREAK) && defined(WIN32)
- if(SIG_ERR != old_sigbreak_handler)
- (void)signal(SIGBREAK, old_sigbreak_handler);
-#endif
-}
-
/* returns true if the current socket is an IP one */
static bool socket_domain_is_ip(void)
{
@@ -367,18 +235,15 @@ static bool socket_domain_is_ip(void)
static int parse_servercmd(struct httprequest *req)
{
FILE *stream;
- char *filename;
int error;
- filename = test2file(req->testno);
+ stream = test2fopen(req->testno);
req->close = FALSE;
req->connmon = FALSE;
- stream = fopen(filename, "rb");
if(!stream) {
error = errno;
logmsg("fopen() failed with error: %d %s", error, strerror(error));
- logmsg(" [1] Error opening file: %s", filename);
logmsg(" Couldn't open test file %ld", req->testno);
req->open = FALSE; /* closes connection */
return 1; /* done */
@@ -1123,7 +988,6 @@ static int send_doc(curl_socket_t sock, struct httprequest *req)
}
else {
char partbuf[80];
- char *filename = test2file(req->testno);
/* select the <data> tag for "normal" requests and the <connect> one
for CONNECT requests (within the <reply> section) */
@@ -1136,11 +1000,10 @@ static int send_doc(curl_socket_t sock, struct httprequest *req)
logmsg("Send response test%ld section <%s>", req->testno, partbuf);
- stream = fopen(filename, "rb");
+ stream = test2fopen(req->testno);
if(!stream) {
error = errno;
logmsg("fopen() failed with error: %d %s", error, strerror(error));
- logmsg(" [3] Error opening file: %s", filename);
return 0;
}
else {
@@ -1159,11 +1022,10 @@ static int send_doc(curl_socket_t sock, struct httprequest *req)
}
/* re-open the same file again */
- stream = fopen(filename, "rb");
+ stream = test2fopen(req->testno);
if(!stream) {
error = errno;
logmsg("fopen() failed with error: %d %s", error, strerror(error));
- logmsg(" [4] Error opening file: %s", filename);
free(ptr);
return 0;
}
@@ -1981,6 +1843,7 @@ int main(int argc, char *argv[])
bool unlink_socket = false;
#endif
const char *pidname = ".http.pid";
+ const char *portname = ".http.port";
struct httprequest req;
int rc = 0;
int error;
@@ -2013,6 +1876,11 @@ int main(int argc, char *argv[])
if(argc>arg)
pidname = argv[arg++];
}
+ else if(!strcmp("--portfile", argv[arg])) {
+ arg++;
+ if(argc>arg)
+ portname = argv[arg++];
+ }
else if(!strcmp("--logfile", argv[arg])) {
arg++;
if(argc>arg)
@@ -2060,7 +1928,7 @@ int main(int argc, char *argv[])
char *endptr;
unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
if((endptr != argv[arg] + strlen(argv[arg])) ||
- (ulnum < 1025UL) || (ulnum > 65535UL)) {
+ (ulnum && ((ulnum < 1025UL) || (ulnum > 65535UL)))) {
fprintf(stderr, "sws: invalid --port argument (%s)\n",
argv[arg]);
return 0;
@@ -2093,6 +1961,7 @@ int main(int argc, char *argv[])
" --version\n"
" --logfile [file]\n"
" --pidfile [file]\n"
+ " --portfile [file]\n"
" --ipv4\n"
" --ipv6\n"
" --unix-socket [file]\n"
@@ -2104,14 +1973,12 @@ int main(int argc, char *argv[])
}
}
- msnprintf(port_str, sizeof(port_str), "port %hu", port);
-
#ifdef WIN32
win32_init();
atexit(win32_cleanup);
#endif
- install_signal_handlers();
+ install_signal_handlers(false);
pid = (long)getpid();
@@ -2212,11 +2079,58 @@ int main(int argc, char *argv[])
}
if(0 != rc) {
error = SOCKERRNO;
- logmsg("Error binding socket on %s: (%d) %s",
- location_str, error, strerror(error));
+ logmsg("Error binding socket: (%d) %s", error, strerror(error));
goto sws_cleanup;
}
+ if(!port) {
+ /* The system was supposed to choose a port number, figure out which
+ port we actually got and update the listener port value with it. */
+ curl_socklen_t la_size;
+ srvr_sockaddr_union_t localaddr;
+#ifdef ENABLE_IPV6
+ if(socket_domain != AF_INET6)
+#endif
+ la_size = sizeof(localaddr.sa4);
+#ifdef ENABLE_IPV6
+ else
+ la_size = sizeof(localaddr.sa6);
+#endif
+ memset(&localaddr.sa, 0, (size_t)la_size);
+ if(getsockname(sock, &localaddr.sa, &la_size) < 0) {
+ error = SOCKERRNO;
+ logmsg("getsockname() failed with error: (%d) %s",
+ error, strerror(error));
+ sclose(sock);
+ goto sws_cleanup;
+ }
+ switch(localaddr.sa.sa_family) {
+ case AF_INET:
+ port = ntohs(localaddr.sa4.sin_port);
+ break;
+#ifdef ENABLE_IPV6
+ case AF_INET6:
+ port = ntohs(localaddr.sa6.sin6_port);
+ break;
+#endif
+ default:
+ break;
+ }
+ if(!port) {
+ /* Real failure, listener port shall not be zero beyond this point. */
+ logmsg("Apparently getsockname() succeeded, with listener port zero.");
+ logmsg("A valid reason for this failure is a binary built without");
+ logmsg("proper network library linkage. This might not be the only");
+ logmsg("reason, but double check it before anything else.");
+ sclose(sock);
+ goto sws_cleanup;
+ }
+ }
+#ifdef USE_UNIX_SOCKETS
+ if(socket_domain != AF_UNIX)
+#endif
+ msnprintf(port_str, sizeof(port_str), "port %hu", port);
+
logmsg("Running %s %s version on %s",
use_gopher?"GOPHER":"HTTP", socket_type, location_str);
@@ -2243,6 +2157,10 @@ int main(int argc, char *argv[])
if(!wrotepidfile)
goto sws_cleanup;
+ wrotepidfile = write_portfile(portname, port);
+ if(!wrotepidfile)
+ goto sws_cleanup;
+
/* initialization of httprequest struct is done before get_request(), but
the pipelining struct field must be initialized previously to FALSE
every time a new connection arrives. */
@@ -2394,7 +2312,7 @@ sws_cleanup:
clear_advisor_read_lock(SERVERLOGS_LOCK);
}
- restore_signal_handlers();
+ restore_signal_handlers(false);
if(got_exit_signal) {
logmsg("========> %s sws (%s pid: %ld) exits with signal (%d)",
diff --git a/tests/server/testpart.c b/tests/server/testpart.c
index 7d31e5183..9c295e0f5 100644
--- a/tests/server/testpart.c
+++ b/tests/server/testpart.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/tests/server/tftp.h b/tests/server/tftp.h
index 3cdd6e6d0..62a0cc35e 100644
--- a/tests/server/tftp.h
+++ b/tests/server/tftp.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/tests/server/tftpd.c b/tests/server/tftpd.c
index 35419b0b9..0c0e9bedf 100644
--- a/tests/server/tftpd.c
+++ b/tests/server/tftpd.c
@@ -15,7 +15,8 @@
*/
/*
- * Copyright (c) 1983 Regents of the University of California.
+ * Copyright (C) 2005 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (c) 1983, Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -209,6 +210,7 @@ static const char *ipv_inuse = "IPv4";
const char *serverlogfile = DEFAULT_LOGFILE;
static const char *pidname = ".tftpd.pid";
+static const char *portfile = NULL;
static int serverlogslocked = 0;
static int wrotepidfile = 0;
@@ -220,44 +222,6 @@ static sigjmp_buf timeoutbuf;
static const unsigned int rexmtval = TIMEOUT;
#endif
-/* do-nothing macro replacement for systems which lack siginterrupt() */
-
-#ifndef HAVE_SIGINTERRUPT
-#define siginterrupt(x,y) do {} while(0)
-#endif
-
-/* vars used to keep around previous signal handlers */
-
-typedef RETSIGTYPE (*SIGHANDLER_T)(int);
-
-#ifdef SIGHUP
-static SIGHANDLER_T old_sighup_handler = SIG_ERR;
-#endif
-
-#ifdef SIGPIPE
-static SIGHANDLER_T old_sigpipe_handler = SIG_ERR;
-#endif
-
-#ifdef SIGINT
-static SIGHANDLER_T old_sigint_handler = SIG_ERR;
-#endif
-
-#ifdef SIGTERM
-static SIGHANDLER_T old_sigterm_handler = SIG_ERR;
-#endif
-
-#if defined(SIGBREAK) && defined(WIN32)
-static SIGHANDLER_T old_sigbreak_handler = SIG_ERR;
-#endif
-
-/* var which if set indicates that the program should finish execution */
-
-SIG_ATOMIC_T got_exit_signal = 0;
-
-/* if next is set indicates the first signal handled in exit_signal_handler */
-
-static volatile int exit_signal = 0;
-
/*****************************************************************************
* FUNCTION PROTOTYPES *
*****************************************************************************/
@@ -294,12 +258,6 @@ static void justtimeout(int signum);
#endif /* HAVE_ALARM && SIGALRM */
-static RETSIGTYPE exit_signal_handler(int signum);
-
-static void install_signal_handlers(void);
-
-static void restore_signal_handlers(void);
-
/*****************************************************************************
* FUNCTION IMPLEMENTATIONS *
*****************************************************************************/
@@ -347,86 +305,6 @@ static void justtimeout(int signum)
#endif /* HAVE_ALARM && SIGALRM */
-/* signal handler that will be triggered to indicate that the program
- should finish its execution in a controlled manner as soon as possible.
- The first time this is called it will set got_exit_signal to one and
- store in exit_signal the signal that triggered its execution. */
-
-static RETSIGTYPE exit_signal_handler(int signum)
-{
- int old_errno = errno;
- if(got_exit_signal == 0) {
- got_exit_signal = 1;
- exit_signal = signum;
- }
- (void)signal(signum, exit_signal_handler);
- errno = old_errno;
-}
-
-static void install_signal_handlers(void)
-{
-#ifdef SIGHUP
- /* ignore SIGHUP signal */
- old_sighup_handler = signal(SIGHUP, SIG_IGN);
- if(old_sighup_handler == SIG_ERR)
- logmsg("cannot install SIGHUP handler: %s", strerror(errno));
-#endif
-#ifdef SIGPIPE
- /* ignore SIGPIPE signal */
- old_sigpipe_handler = signal(SIGPIPE, SIG_IGN);
- if(old_sigpipe_handler == SIG_ERR)
- logmsg("cannot install SIGPIPE handler: %s", strerror(errno));
-#endif
-#ifdef SIGINT
- /* handle SIGINT signal with our exit_signal_handler */
- old_sigint_handler = signal(SIGINT, exit_signal_handler);
- if(old_sigint_handler == SIG_ERR)
- logmsg("cannot install SIGINT handler: %s", strerror(errno));
- else
- siginterrupt(SIGINT, 1);
-#endif
-#ifdef SIGTERM
- /* handle SIGTERM signal with our exit_signal_handler */
- old_sigterm_handler = signal(SIGTERM, exit_signal_handler);
- if(old_sigterm_handler == SIG_ERR)
- logmsg("cannot install SIGTERM handler: %s", strerror(errno));
- else
- siginterrupt(SIGTERM, 1);
-#endif
-#if defined(SIGBREAK) && defined(WIN32)
- /* handle SIGBREAK signal with our exit_signal_handler */
- old_sigbreak_handler = signal(SIGBREAK, exit_signal_handler);
- if(old_sigbreak_handler == SIG_ERR)
- logmsg("cannot install SIGBREAK handler: %s", strerror(errno));
- else
- siginterrupt(SIGBREAK, 1);
-#endif
-}
-
-static void restore_signal_handlers(void)
-{
-#ifdef SIGHUP
- if(SIG_ERR != old_sighup_handler)
- (void)signal(SIGHUP, old_sighup_handler);
-#endif
-#ifdef SIGPIPE
- if(SIG_ERR != old_sigpipe_handler)
- (void)signal(SIGPIPE, old_sigpipe_handler);
-#endif
-#ifdef SIGINT
- if(SIG_ERR != old_sigint_handler)
- (void)signal(SIGINT, old_sigint_handler);
-#endif
-#ifdef SIGTERM
- if(SIG_ERR != old_sigterm_handler)
- (void)signal(SIGTERM, old_sigterm_handler);
-#endif
-#if defined(SIGBREAK) && defined(WIN32)
- if(SIG_ERR != old_sigbreak_handler)
- (void)signal(SIGBREAK, old_sigbreak_handler);
-#endif
-}
-
/*
* init for either read-ahead or write-behind.
* zero for write-behind, one for read-head.
@@ -697,6 +575,11 @@ int main(int argc, char **argv)
if(argc>arg)
pidname = argv[arg++];
}
+ else if(!strcmp("--portfile", argv[arg])) {
+ arg++;
+ if(argc>arg)
+ portfile = argv[arg++];
+ }
else if(!strcmp("--logfile", argv[arg])) {
arg++;
if(argc>arg)
@@ -721,12 +604,6 @@ int main(int argc, char **argv)
if(argc>arg) {
char *endptr;
unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
- if((endptr != argv[arg] + strlen(argv[arg])) ||
- (ulnum < 1025UL) || (ulnum > 65535UL)) {
- fprintf(stderr, "tftpd: invalid --port argument (%s)\n",
- argv[arg]);
- return 0;
- }
port = curlx_ultous(ulnum);
arg++;
}
@@ -756,7 +633,7 @@ int main(int argc, char **argv)
atexit(win32_cleanup);
#endif
- install_signal_handlers();
+ install_signal_handlers(true);
pid = (long)getpid();
@@ -813,12 +690,64 @@ int main(int argc, char **argv)
goto tftpd_cleanup;
}
+ if(!port) {
+ /* The system was supposed to choose a port number, figure out which
+ port we actually got and update the listener port value with it. */
+ curl_socklen_t la_size;
+ srvr_sockaddr_union_t localaddr;
+#ifdef ENABLE_IPV6
+ if(!use_ipv6)
+#endif
+ la_size = sizeof(localaddr.sa4);
+#ifdef ENABLE_IPV6
+ else
+ la_size = sizeof(localaddr.sa6);
+#endif
+ memset(&localaddr.sa, 0, (size_t)la_size);
+ if(getsockname(sock, &localaddr.sa, &la_size) < 0) {
+ error = SOCKERRNO;
+ logmsg("getsockname() failed with error: (%d) %s",
+ error, strerror(error));
+ sclose(sock);
+ goto tftpd_cleanup;
+ }
+ switch(localaddr.sa.sa_family) {
+ case AF_INET:
+ port = ntohs(localaddr.sa4.sin_port);
+ break;
+#ifdef ENABLE_IPV6
+ case AF_INET6:
+ port = ntohs(localaddr.sa6.sin6_port);
+ break;
+#endif
+ default:
+ break;
+ }
+ if(!port) {
+ /* Real failure, listener port shall not be zero beyond this point. */
+ logmsg("Apparently getsockname() succeeded, with listener port zero.");
+ logmsg("A valid reason for this failure is a binary built without");
+ logmsg("proper network library linkage. This might not be the only");
+ logmsg("reason, but double check it before anything else.");
+ result = 2;
+ goto tftpd_cleanup;
+ }
+ }
+
wrotepidfile = write_pidfile(pidname);
if(!wrotepidfile) {
result = 1;
goto tftpd_cleanup;
}
+ if(portfile) {
+ wrotepidfile = write_portfile(portfile, port);
+ if(!wrotepidfile) {
+ result = 1;
+ goto tftpd_cleanup;
+ }
+ }
+
logmsg("Running %s version on port UDP/%d", ipv_inuse, (int)port);
for(;;) {
@@ -923,13 +852,15 @@ tftpd_cleanup:
if(wrotepidfile)
unlink(pidname);
+ if(portfile)
+ unlink(portfile);
if(serverlogslocked) {
serverlogslocked = 0;
clear_advisor_read_lock(SERVERLOGS_LOCK);
}
- restore_signal_handlers();
+ restore_signal_handlers(true);
if(got_exit_signal) {
logmsg("========> %s tftpd (port: %d pid: %ld) exits with signal (%d)",
@@ -1067,16 +998,12 @@ static int do_tftp(struct testcase *test, struct tftphdr *tp, ssize_t size)
static int parse_servercmd(struct testcase *req)
{
FILE *stream;
- char *filename;
int error;
- filename = test2file(req->testno);
-
- stream = fopen(filename, "rb");
+ stream = test2fopen(req->testno);
if(!stream) {
error = errno;
logmsg("fopen() failed with error: %d %s", error, strerror(error));
- logmsg(" [1] Error opening file: %s", filename);
logmsg(" Couldn't open test file %ld", req->testno);
return 1; /* done */
}
@@ -1159,7 +1086,7 @@ static int validate_access(struct testcase *test,
char partbuf[80]="data";
long partno;
long testno;
- char *file;
+ FILE *stream;
ptr++; /* skip the slash */
@@ -1184,40 +1111,33 @@ static int validate_access(struct testcase *test,
(void)parse_servercmd(test);
- file = test2file(testno);
+ stream = test2fopen(testno);
if(0 != partno)
msnprintf(partbuf, sizeof(partbuf), "data%ld", partno);
- if(file) {
- FILE *stream = fopen(file, "rb");
- if(!stream) {
- int error = errno;
- logmsg("fopen() failed with error: %d %s", error, strerror(error));
- logmsg("Error opening file: %s", file);
- logmsg("Couldn't open test file: %s", file);
+ if(!stream) {
+ int error = errno;
+ logmsg("fopen() failed with error: %d %s", error, strerror(error));
+ logmsg("Couldn't open test file for test : %d", testno);
+ return EACCESS;
+ }
+ else {
+ size_t count;
+ int error = getpart(&test->buffer, &count, "reply", partbuf, stream);
+ fclose(stream);
+ if(error) {
+ logmsg("getpart() failed with error: %d", error);
return EACCESS;
}
- else {
- size_t count;
- int error = getpart(&test->buffer, &count, "reply", partbuf, stream);
- fclose(stream);
- if(error) {
- logmsg("getpart() failed with error: %d", error);
- return EACCESS;
- }
- if(test->buffer) {
- test->rptr = test->buffer; /* set read pointer */
- test->bufsize = count; /* set total count */
- test->rcount = count; /* set data left to read */
- }
- else
- return EACCESS;
+ if(test->buffer) {
+ test->rptr = test->buffer; /* set read pointer */
+ test->bufsize = count; /* set total count */
+ test->rcount = count; /* set data left to read */
}
-
+ else
+ return EACCESS;
}
- else
- return EACCESS;
}
else {
logmsg("no slash found in path");
diff --git a/tests/server/util.c b/tests/server/util.c
index 263f0cece..f576b9c23 100644
--- a/tests/server/util.c
+++ b/tests/server/util.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -194,11 +194,21 @@ void win32_cleanup(void)
/* set by the main code to point to where the test dir is */
const char *path = ".";
-char *test2file(long testno)
+FILE *test2fopen(long testno)
{
- static char filename[256];
+ FILE *stream;
+ char filename[256];
+ /* first try the alternative, preprocessed, file */
+ msnprintf(filename, sizeof(filename), ALTTEST_DATA_PATH, path, testno);
+ stream = fopen(filename, "rb");
+ if(stream)
+ return stream;
+
+ /* then try the source version */
msnprintf(filename, sizeof(filename), TEST_DATA_PATH, path, testno);
- return filename;
+ stream = fopen(filename, "rb");
+
+ return stream;
}
/*
@@ -261,20 +271,43 @@ int wait_ms(int timeout_ms)
int write_pidfile(const char *filename)
{
FILE *pidfile;
- long pid;
+ curl_off_t pid;
- pid = (long)getpid();
+ pid = (curl_off_t)getpid();
pidfile = fopen(filename, "wb");
if(!pidfile) {
logmsg("Couldn't write pid file: %s %s", filename, strerror(errno));
return 0; /* fail */
}
- fprintf(pidfile, "%ld\n", pid);
+#if defined(WIN32) || defined(_WIN32)
+ /* store pid + 65536 to avoid conflict with Cygwin/msys PIDs, see also:
+ * - https://cygwin.com/git/?p=newlib-cygwin.git;a=commit; ↵
+ * h=b5e1003722cb14235c4f166be72c09acdffc62ea
+ * - https://cygwin.com/git/?p=newlib-cygwin.git;a=commit; ↵
+ * h=448cf5aa4b429d5a9cebf92a0da4ab4b5b6d23fe
+ */
+ pid += 65536;
+#endif
+ fprintf(pidfile, "%" CURL_FORMAT_CURL_OFF_T "\n", pid);
fclose(pidfile);
logmsg("Wrote pid %ld to %s", pid, filename);
return 1; /* success */
}
+/* store the used port number in a file */
+int write_portfile(const char *filename, int port)
+{
+ FILE *portfile = fopen(filename, "wb");
+ if(!portfile) {
+ logmsg("Couldn't write port file: %s %s", filename, strerror(errno));
+ return 0; /* fail */
+ }
+ fprintf(portfile, "%d\n", port);
+ fclose(portfile);
+ logmsg("Wrote port %d to %s", port, filename);
+ return 1; /* success */
+}
+
void set_advisor_read_lock(const char *filename)
{
FILE *lockfile;
@@ -500,3 +533,278 @@ long timediff(struct timeval newer, struct timeval older)
return (long)(newer.tv_sec-older.tv_sec)*1000+
(long)(newer.tv_usec-older.tv_usec)/1000;
}
+
+/* do-nothing macro replacement for systems which lack siginterrupt() */
+
+#ifndef HAVE_SIGINTERRUPT
+#define siginterrupt(x,y) do {} while(0)
+#endif
+
+/* vars used to keep around previous signal handlers */
+
+typedef RETSIGTYPE (*SIGHANDLER_T)(int);
+
+#ifdef SIGHUP
+static SIGHANDLER_T old_sighup_handler = SIG_ERR;
+#endif
+
+#ifdef SIGPIPE
+static SIGHANDLER_T old_sigpipe_handler = SIG_ERR;
+#endif
+
+#ifdef SIGALRM
+static SIGHANDLER_T old_sigalrm_handler = SIG_ERR;
+#endif
+
+#ifdef SIGINT
+static SIGHANDLER_T old_sigint_handler = SIG_ERR;
+#endif
+
+#ifdef SIGTERM
+static SIGHANDLER_T old_sigterm_handler = SIG_ERR;
+#endif
+
+#if defined(SIGBREAK) && defined(WIN32)
+static SIGHANDLER_T old_sigbreak_handler = SIG_ERR;
+#endif
+
+#ifdef WIN32
+static DWORD thread_main_id = 0;
+static HANDLE thread_main_window = NULL;
+static HWND hidden_main_window = NULL;
+#endif
+
+/* var which if set indicates that the program should finish execution */
+volatile int got_exit_signal = 0;
+
+/* if next is set indicates the first signal handled in exit_signal_handler */
+volatile int exit_signal = 0;
+
+/* signal handler that will be triggered to indicate that the program
+ * should finish its execution in a controlled manner as soon as possible.
+ * The first time this is called it will set got_exit_signal to one and
+ * store in exit_signal the signal that triggered its execution.
+ */
+static RETSIGTYPE exit_signal_handler(int signum)
+{
+ int old_errno = errno;
+ logmsg("exit_signal_handler: %d", signum);
+ if(got_exit_signal == 0) {
+ got_exit_signal = 1;
+ exit_signal = signum;
+ }
+ (void)signal(signum, exit_signal_handler);
+ errno = old_errno;
+}
+
+#ifdef WIN32
+/* CTRL event handler for Windows Console applications to simulate
+ * SIGINT, SIGTERM and SIGBREAK on CTRL events and trigger signal handler.
+ *
+ * Background information from MSDN:
+ * SIGINT is not supported for any Win32 application. When a CTRL+C
+ * interrupt occurs, Win32 operating systems generate a new thread
+ * to specifically handle that interrupt. This can cause a single-thread
+ * application, such as one in UNIX, to become multithreaded and cause
+ * unexpected behavior.
+ * [...]
+ * The SIGILL and SIGTERM signals are not generated under Windows.
+ * They are included for ANSI compatibility. Therefore, you can set
+ * signal handlers for these signals by using signal, and you can also
+ * explicitly generate these signals by calling raise. Source:
+ * https://docs.microsoft.com/de-de/cpp/c-runtime-library/reference/signal
+ */
+static BOOL WINAPI ctrl_event_handler(DWORD dwCtrlType)
+{
+ int signum = 0;
+ logmsg("ctrl_event_handler: %d", dwCtrlType);
+ switch(dwCtrlType) {
+#ifdef SIGINT
+ case CTRL_C_EVENT: signum = SIGINT; break;
+#endif
+#ifdef SIGTERM
+ case CTRL_CLOSE_EVENT: signum = SIGTERM; break;
+#endif
+#ifdef SIGBREAK
+ case CTRL_BREAK_EVENT: signum = SIGBREAK; break;
+#endif
+ default: return FALSE;
+ }
+ if(signum) {
+ logmsg("ctrl_event_handler: %d -> %d", dwCtrlType, signum);
+ exit_signal_handler(signum);
+ }
+ return TRUE;
+}
+/* Window message handler for Windows applications to add support
+ * for graceful process termination via taskkill (without /f) which
+ * sends WM_CLOSE to all Windows of a process (even hidden ones).
+ *
+ * Therefore we create and run a hidden Window in a separate thread
+ * to receive and handle the WM_CLOSE message as SIGTERM signal.
+ */
+static LRESULT CALLBACK main_window_proc(HWND hwnd, UINT uMsg,
+ WPARAM wParam, LPARAM lParam)
+{
+ int signum = 0;
+ if(hwnd == hidden_main_window) {
+ switch(uMsg) {
+#ifdef SIGTERM
+ case WM_CLOSE: signum = SIGTERM; break;
+#endif
+ case WM_DESTROY: PostQuitMessage(0); break;
+ }
+ if(signum) {
+ logmsg("main_window_proc: %d -> %d", uMsg, signum);
+ exit_signal_handler(signum);
+ }
+ }
+ return DefWindowProc(hwnd, uMsg, wParam, lParam);
+}
+/* Window message queue loop for hidden main window, details see above.
+ */
+static DWORD WINAPI main_window_loop(LPVOID lpParameter)
+{
+ WNDCLASS wc;
+ BOOL ret;
+ MSG msg;
+
+ ZeroMemory(&wc, sizeof(wc));
+ wc.lpfnWndProc = (WNDPROC)main_window_proc;
+ wc.hInstance = (HINSTANCE)lpParameter;
+ wc.lpszClassName = "MainWClass";
+ if(!RegisterClass(&wc)) {
+ perror("RegisterClass failed");
+ return (DWORD)-1;
+ }
+
+ hidden_main_window = CreateWindowEx(0, "MainWClass", "Recv WM_CLOSE msg",
+ WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ (HWND)NULL, (HMENU)NULL,
+ wc.hInstance, (LPVOID)NULL);
+ if(!hidden_main_window) {
+ perror("CreateWindowEx failed");
+ return (DWORD)-1;
+ }
+
+ do {
+ ret = GetMessage(&msg, NULL, 0, 0);
+ if(ret == -1) {
+ perror("GetMessage failed");
+ return (DWORD)-1;
+ }
+ else if(ret) {
+ if(msg.message == WM_APP) {
+ DestroyWindow(hidden_main_window);
+ }
+ else if(msg.hwnd && !TranslateMessage(&msg)) {
+ DispatchMessage(&msg);
+ }
+ }
+ } while(ret);
+
+ hidden_main_window = NULL;
+ return (DWORD)msg.wParam;
+}
+#endif
+
+void install_signal_handlers(bool keep_sigalrm)
+{
+#ifdef SIGHUP
+ /* ignore SIGHUP signal */
+ old_sighup_handler = signal(SIGHUP, SIG_IGN);
+ if(old_sighup_handler == SIG_ERR)
+ logmsg("cannot install SIGHUP handler: %s", strerror(errno));
+#endif
+#ifdef SIGPIPE
+ /* ignore SIGPIPE signal */
+ old_sigpipe_handler = signal(SIGPIPE, SIG_IGN);
+ if(old_sigpipe_handler == SIG_ERR)
+ logmsg("cannot install SIGPIPE handler: %s", strerror(errno));
+#endif
+#ifdef SIGALRM
+ if(!keep_sigalrm) {
+ /* ignore SIGALRM signal */
+ old_sigalrm_handler = signal(SIGALRM, SIG_IGN);
+ if(old_sigalrm_handler == SIG_ERR)
+ logmsg("cannot install SIGALRM handler: %s", strerror(errno));
+ }
+#else
+ (void)keep_sigalrm;
+#endif
+#ifdef SIGINT
+ /* handle SIGINT signal with our exit_signal_handler */
+ old_sigint_handler = signal(SIGINT, exit_signal_handler);
+ if(old_sigint_handler == SIG_ERR)
+ logmsg("cannot install SIGINT handler: %s", strerror(errno));
+ else
+ siginterrupt(SIGINT, 1);
+#endif
+#ifdef SIGTERM
+ /* handle SIGTERM signal with our exit_signal_handler */
+ old_sigterm_handler = signal(SIGTERM, exit_signal_handler);
+ if(old_sigterm_handler == SIG_ERR)
+ logmsg("cannot install SIGTERM handler: %s", strerror(errno));
+ else
+ siginterrupt(SIGTERM, 1);
+#endif
+#if defined(SIGBREAK) && defined(WIN32)
+ /* handle SIGBREAK signal with our exit_signal_handler */
+ old_sigbreak_handler = signal(SIGBREAK, exit_signal_handler);
+ if(old_sigbreak_handler == SIG_ERR)
+ logmsg("cannot install SIGBREAK handler: %s", strerror(errno));
+ else
+ siginterrupt(SIGBREAK, 1);
+#endif
+#ifdef WIN32
+ if(!SetConsoleCtrlHandler(ctrl_event_handler, TRUE))
+ logmsg("cannot install CTRL event handler");
+ thread_main_window = CreateThread(NULL, 0,
+ &main_window_loop,
+ (LPVOID)GetModuleHandle(NULL),
+ 0, &thread_main_id);
+ if(!thread_main_window || !thread_main_id)
+ logmsg("cannot start main window loop");
+#endif
+}
+
+void restore_signal_handlers(bool keep_sigalrm)
+{
+#ifdef SIGHUP
+ if(SIG_ERR != old_sighup_handler)
+ (void)signal(SIGHUP, old_sighup_handler);
+#endif
+#ifdef SIGPIPE
+ if(SIG_ERR != old_sigpipe_handler)
+ (void)signal(SIGPIPE, old_sigpipe_handler);
+#endif
+#ifdef SIGALRM
+ if(!keep_sigalrm) {
+ if(SIG_ERR != old_sigalrm_handler)
+ (void)signal(SIGALRM, old_sigalrm_handler);
+ }
+#else
+ (void)keep_sigalrm;
+#endif
+#ifdef SIGINT
+ if(SIG_ERR != old_sigint_handler)
+ (void)signal(SIGINT, old_sigint_handler);
+#endif
+#ifdef SIGTERM
+ if(SIG_ERR != old_sigterm_handler)
+ (void)signal(SIGTERM, old_sigterm_handler);
+#endif
+#if defined(SIGBREAK) && defined(WIN32)
+ if(SIG_ERR != old_sigbreak_handler)
+ (void)signal(SIGBREAK, old_sigbreak_handler);
+#endif
+#ifdef WIN32
+ (void)SetConsoleCtrlHandler(ctrl_event_handler, FALSE);
+ if(thread_main_window && thread_main_id) {
+ if(PostThreadMessage(thread_main_id, WM_APP, 0, 0))
+ (void)WaitForSingleObjectEx(thread_main_window, INFINITE, TRUE);
+ }
+#endif
+}
diff --git a/tests/server/util.h b/tests/server/util.h
index 236d4550e..73f5f45f6 100644
--- a/tests/server/util.h
+++ b/tests/server/util.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -28,6 +28,7 @@ void logmsg(const char *msg, ...);
long timediff(struct timeval newer, struct timeval older);
#define TEST_DATA_PATH "%s/data/test%ld"
+#define ALTTEST_DATA_PATH "%s/log/test%ld"
#define SERVERLOGS_LOCK "log/serverlogs.lock"
@@ -53,17 +54,23 @@ void win32_init(void);
void win32_cleanup(void);
#endif /* USE_WINSOCK */
-/* returns the path name to the test case file */
-char *test2file(long testno);
+/* fopens the test case file */
+FILE *test2fopen(long testno);
int wait_ms(int timeout_ms);
-
int write_pidfile(const char *filename);
-
+int write_portfile(const char *filename, int port);
void set_advisor_read_lock(const char *filename);
-
void clear_advisor_read_lock(const char *filename);
-
int strncasecompare(const char *first, const char *second, size_t max);
+/* global variable which if set indicates that the program should finish */
+extern volatile int got_exit_signal;
+
+/* global variable which if set indicates the first signal handled */
+extern volatile int exit_signal;
+
+void install_signal_handlers(bool keep_sigalrm);
+void restore_signal_handlers(bool keep_sigalrm);
+
#endif /* HEADER_CURL_SERVER_UTIL_H */