diff options
author | Haibo Huang <hhb@google.com> | 2020-04-29 13:49:57 -0700 |
---|---|---|
committer | Haibo Huang <hhb@google.com> | 2020-04-29 14:16:24 -0700 |
commit | 24c77a17e5380687755385e116cc407b9e0cdac5 (patch) | |
tree | e932df98deeb78b7fe8615fe094739eda91b8956 /tests/server | |
parent | 217d07615e14335a54eeee5fb4478f4125487514 (diff) | |
download | external_curl-24c77a17e5380687755385e116cc407b9e0cdac5.tar.gz external_curl-24c77a17e5380687755385e116cc407b9e0cdac5.tar.bz2 external_curl-24c77a17e5380687755385e116cc407b9e0cdac5.zip |
Upgrade curl to curl-7_70_0
Change-Id: I898f9e257c9b950ed79ccd16c4129ab58fd65f98
Diffstat (limited to 'tests/server')
-rw-r--r-- | tests/server/CMakeLists.txt | 21 | ||||
-rw-r--r-- | tests/server/Makefile.inc | 31 | ||||
-rwxr-xr-x | tests/server/base64.pl | 21 | ||||
-rw-r--r-- | tests/server/fake_ntlm.c | 17 | ||||
-rw-r--r-- | tests/server/getpart.c | 2 | ||||
-rw-r--r-- | tests/server/getpart.h | 2 | ||||
-rw-r--r-- | tests/server/mqttd.c | 1018 | ||||
-rw-r--r-- | tests/server/resolve.c | 4 | ||||
-rw-r--r-- | tests/server/rtspd.c | 215 | ||||
-rw-r--r-- | tests/server/server_setup.h | 2 | ||||
-rw-r--r-- | tests/server/server_sockaddr.h | 2 | ||||
-rw-r--r-- | tests/server/sockfilt.c | 604 | ||||
-rw-r--r-- | tests/server/socksd.c | 143 | ||||
-rw-r--r-- | tests/server/sws.c | 214 | ||||
-rw-r--r-- | tests/server/testpart.c | 2 | ||||
-rw-r--r-- | tests/server/tftp.h | 2 | ||||
-rw-r--r-- | tests/server/tftpd.c | 250 | ||||
-rw-r--r-- | tests/server/util.c | 322 | ||||
-rw-r--r-- | tests/server/util.h | 21 |
19 files changed, 1952 insertions, 941 deletions
diff --git a/tests/server/CMakeLists.txt b/tests/server/CMakeLists.txt index d5324360..7c64f0a8 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 6296af7c..fb13d79c 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 449c3103..d00673ec 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 c6e36b6f..c9211f7e 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 044705d0..664d8156 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 27736851..e8ddd22a 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 00000000..6785b001 --- /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 993e0312..f7256187 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 7563fd22..6ee7787b 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 76c46236..1f2cef10 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 bbcab834..c3602e1c 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 569be6f6..84c72f96 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 5e32bc90..e6e9c12b 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 b397ed19..48ea26d5 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 7d31e518..9c295e0f 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 3cdd6e6d..62a0cc35 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 35419b0b..0c0e9bed 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 263f0cec..f576b9c2 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 236d4550..73f5f45f 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 */ |