Update networking layer w/ CURL and emscripten impl
This commit is contained in:
+18
@@ -0,0 +1,18 @@
|
||||
# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
#
|
||||
# SPDX-License-Identifier: curl
|
||||
|
||||
allowfunc accept
|
||||
allowfunc fclose
|
||||
allowfunc fopen
|
||||
allowfunc fprintf
|
||||
allowfunc freeaddrinfo
|
||||
allowfunc getaddrinfo
|
||||
allowfunc open
|
||||
allowfunc printf
|
||||
allowfunc recv
|
||||
allowfunc send
|
||||
allowfunc snprintf
|
||||
allowfunc socket
|
||||
allowfunc sscanf
|
||||
allowfunc vsnprintf
|
||||
@@ -0,0 +1,47 @@
|
||||
#***************************************************************************
|
||||
# _ _ ____ _
|
||||
# Project ___| | | | _ \| |
|
||||
# / __| | | | |_) | |
|
||||
# | (__| |_| | _ <| |___
|
||||
# \___|\___/|_| \_\_____|
|
||||
#
|
||||
# Copyright (C) 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.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.
|
||||
#
|
||||
# SPDX-License-Identifier: curl
|
||||
#
|
||||
###########################################################################
|
||||
|
||||
# Get BUNDLE, FIRST_C, FIRST_H, UTILS_C, UTILS_H, CURLX_C, TESTS_C variables
|
||||
curl_transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake")
|
||||
include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake")
|
||||
|
||||
add_custom_command(OUTPUT "${BUNDLE}.c"
|
||||
COMMAND ${PERL_EXECUTABLE} "${PROJECT_SOURCE_DIR}/scripts/mk-unity.pl"
|
||||
--include ${UTILS_C} ${CURLX_C} --test ${TESTS_C} > "${BUNDLE}.c"
|
||||
DEPENDS
|
||||
"${PROJECT_SOURCE_DIR}/scripts/mk-unity.pl" "${CMAKE_CURRENT_SOURCE_DIR}/Makefile.inc"
|
||||
${FIRST_C} ${UTILS_C} ${CURLX_C} ${TESTS_C}
|
||||
VERBATIM)
|
||||
|
||||
add_executable(${BUNDLE} EXCLUDE_FROM_ALL "${BUNDLE}.c")
|
||||
add_dependencies(testdeps ${BUNDLE})
|
||||
target_link_libraries(${BUNDLE} ${CURL_NETWORK_AND_TIME_LIBS})
|
||||
target_include_directories(${BUNDLE} PRIVATE
|
||||
"${PROJECT_BINARY_DIR}/lib" # for "curl_config.h"
|
||||
"${PROJECT_SOURCE_DIR}/lib" # for "curl_setup.h", curlx
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}" # for the generated bundle source to find included test sources
|
||||
)
|
||||
set_target_properties(${BUNDLE} PROPERTIES OUTPUT_NAME "${BUNDLE}" PROJECT_LABEL "Test ${BUNDLE}" UNITY_BUILD OFF C_CLANG_TIDY "")
|
||||
|
||||
curl_add_clang_tidy_test_target("${BUNDLE}-clang-tidy" ${BUNDLE} ${FIRST_C} ${UTILS_C} ${TESTS_C})
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
#***************************************************************************
|
||||
# _ _ ____ _
|
||||
# Project ___| | | | _ \| |
|
||||
# / __| | | | |_) | |
|
||||
# | (__| |_| | _ <| |___
|
||||
# \___|\___/|_| \_\_____|
|
||||
#
|
||||
# Copyright (C) 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.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.
|
||||
#
|
||||
# SPDX-License-Identifier: curl
|
||||
#
|
||||
###########################################################################
|
||||
AUTOMAKE_OPTIONS = foreign nostdinc
|
||||
|
||||
# Specify our include paths here, and do it relative to $(top_srcdir) and
|
||||
# $(top_builddir), to ensure that these paths which belong to the library
|
||||
# being currently built and tested are searched before the library which
|
||||
# might possibly already be installed in the system.
|
||||
#
|
||||
# $(top_srcdir)/include is for libcurl's external include files
|
||||
# $(top_builddir)/lib is for libcurl's generated lib/curl_config.h file
|
||||
# $(top_srcdir)/lib for libcurl's lib/curl_setup.h and other "borrowed" files
|
||||
# $(srcdir) for the generated bundle source to find included test sources
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include \
|
||||
-I$(top_builddir)/lib \
|
||||
-I$(top_srcdir)/lib \
|
||||
-I$(srcdir)
|
||||
|
||||
# Get BUNDLE, FIRST_C, FIRST_H, UTILS_C, UTILS_H, CURLX_C, TESTS_C variables
|
||||
include Makefile.inc
|
||||
|
||||
EXTRA_DIST = CMakeLists.txt .checksrc $(FIRST_C) $(FIRST_H) $(UTILS_C) $(UTILS_H) $(TESTS_C)
|
||||
|
||||
CFLAGS += @CURL_CFLAG_EXTRAS@
|
||||
|
||||
# Prevent LIBS from being used for all link targets
|
||||
LIBS = $(BLANK_AT_MAKETIME)
|
||||
|
||||
$(BUNDLE).c: $(top_srcdir)/scripts/mk-unity.pl Makefile.inc $(FIRST_C) $(UTILS_C) $(CURLX_C) $(TESTS_C)
|
||||
@PERL@ $(top_srcdir)/scripts/mk-unity.pl --include $(UTILS_C) $(CURLX_C) --test $(TESTS_C) > $(BUNDLE).c
|
||||
|
||||
noinst_PROGRAMS = $(BUNDLE)
|
||||
LDADD = @CURL_NETWORK_AND_TIME_LIBS@
|
||||
CLEANFILES = $(BUNDLE).c
|
||||
|
||||
CHECKSRC = $(CS_$(V))
|
||||
CS_0 = @echo " RUN " $@;
|
||||
CS_1 =
|
||||
CS_ = $(CS_0)
|
||||
|
||||
# ignore generated C files since they play by slightly different rules!
|
||||
checksrc:
|
||||
$(CHECKSRC)(@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(srcdir) \
|
||||
-W$(srcdir)/$(BUNDLE).c \
|
||||
$(srcdir)/*.[ch])
|
||||
|
||||
if NOT_CURL_CI
|
||||
all-local: checksrc
|
||||
endif
|
||||
|
||||
clean-local:
|
||||
rm -f $(BUNDLE)
|
||||
+792
@@ -0,0 +1,792 @@
|
||||
# Makefile.in generated by automake 1.16.5 from Makefile.am.
|
||||
# @configure_input@
|
||||
|
||||
# Copyright (C) 1994-2021 Free Software Foundation, Inc.
|
||||
|
||||
# This Makefile.in is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
|
||||
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
# PARTICULAR PURPOSE.
|
||||
|
||||
@SET_MAKE@
|
||||
|
||||
#***************************************************************************
|
||||
# _ _ ____ _
|
||||
# Project ___| | | | _ \| |
|
||||
# / __| | | | |_) | |
|
||||
# | (__| |_| | _ <| |___
|
||||
# \___|\___/|_| \_\_____|
|
||||
#
|
||||
# Copyright (C) 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.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.
|
||||
#
|
||||
# SPDX-License-Identifier: curl
|
||||
#
|
||||
###########################################################################
|
||||
# Shared between CMakeLists.txt and Makefile.am
|
||||
|
||||
VPATH = @srcdir@
|
||||
am__is_gnu_make = { \
|
||||
if test -z '$(MAKELEVEL)'; then \
|
||||
false; \
|
||||
elif test -n '$(MAKE_HOST)'; then \
|
||||
true; \
|
||||
elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
|
||||
true; \
|
||||
else \
|
||||
false; \
|
||||
fi; \
|
||||
}
|
||||
am__make_running_with_option = \
|
||||
case $${target_option-} in \
|
||||
?) ;; \
|
||||
*) echo "am__make_running_with_option: internal error: invalid" \
|
||||
"target option '$${target_option-}' specified" >&2; \
|
||||
exit 1;; \
|
||||
esac; \
|
||||
has_opt=no; \
|
||||
sane_makeflags=$$MAKEFLAGS; \
|
||||
if $(am__is_gnu_make); then \
|
||||
sane_makeflags=$$MFLAGS; \
|
||||
else \
|
||||
case $$MAKEFLAGS in \
|
||||
*\\[\ \ ]*) \
|
||||
bs=\\; \
|
||||
sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
|
||||
| sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
|
||||
esac; \
|
||||
fi; \
|
||||
skip_next=no; \
|
||||
strip_trailopt () \
|
||||
{ \
|
||||
flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
|
||||
}; \
|
||||
for flg in $$sane_makeflags; do \
|
||||
test $$skip_next = yes && { skip_next=no; continue; }; \
|
||||
case $$flg in \
|
||||
*=*|--*) continue;; \
|
||||
-*I) strip_trailopt 'I'; skip_next=yes;; \
|
||||
-*I?*) strip_trailopt 'I';; \
|
||||
-*O) strip_trailopt 'O'; skip_next=yes;; \
|
||||
-*O?*) strip_trailopt 'O';; \
|
||||
-*l) strip_trailopt 'l'; skip_next=yes;; \
|
||||
-*l?*) strip_trailopt 'l';; \
|
||||
-[dEDm]) skip_next=yes;; \
|
||||
-[JT]) skip_next=yes;; \
|
||||
esac; \
|
||||
case $$flg in \
|
||||
*$$target_option*) has_opt=yes; break;; \
|
||||
esac; \
|
||||
done; \
|
||||
test $$has_opt = yes
|
||||
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
|
||||
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
|
||||
pkgdatadir = $(datadir)/@PACKAGE@
|
||||
pkgincludedir = $(includedir)/@PACKAGE@
|
||||
pkglibdir = $(libdir)/@PACKAGE@
|
||||
pkglibexecdir = $(libexecdir)/@PACKAGE@
|
||||
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
|
||||
install_sh_DATA = $(install_sh) -c -m 644
|
||||
install_sh_PROGRAM = $(install_sh) -c
|
||||
install_sh_SCRIPT = $(install_sh) -c
|
||||
INSTALL_HEADER = $(INSTALL_DATA)
|
||||
transform = $(program_transform_name)
|
||||
NORMAL_INSTALL = :
|
||||
PRE_INSTALL = :
|
||||
POST_INSTALL = :
|
||||
NORMAL_UNINSTALL = :
|
||||
PRE_UNINSTALL = :
|
||||
POST_UNINSTALL = :
|
||||
build_triplet = @build@
|
||||
host_triplet = @host@
|
||||
noinst_PROGRAMS = $(am__EXEEXT_1)
|
||||
subdir = tests/server
|
||||
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
|
||||
am__aclocal_m4_deps = $(top_srcdir)/m4/curl-amissl.m4 \
|
||||
$(top_srcdir)/m4/curl-apple-sectrust.m4 \
|
||||
$(top_srcdir)/m4/curl-compilers.m4 \
|
||||
$(top_srcdir)/m4/curl-confopts.m4 \
|
||||
$(top_srcdir)/m4/curl-functions.m4 \
|
||||
$(top_srcdir)/m4/curl-gnutls.m4 \
|
||||
$(top_srcdir)/m4/curl-mbedtls.m4 \
|
||||
$(top_srcdir)/m4/curl-openssl.m4 \
|
||||
$(top_srcdir)/m4/curl-override.m4 \
|
||||
$(top_srcdir)/m4/curl-reentrant.m4 \
|
||||
$(top_srcdir)/m4/curl-rustls.m4 \
|
||||
$(top_srcdir)/m4/curl-schannel.m4 \
|
||||
$(top_srcdir)/m4/curl-sysconfig.m4 \
|
||||
$(top_srcdir)/m4/curl-wolfssl.m4 $(top_srcdir)/m4/libtool.m4 \
|
||||
$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
|
||||
$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
|
||||
$(top_srcdir)/m4/xc-am-iface.m4 \
|
||||
$(top_srcdir)/m4/xc-cc-check.m4 \
|
||||
$(top_srcdir)/m4/xc-lt-iface.m4 \
|
||||
$(top_srcdir)/m4/xc-val-flgs.m4 \
|
||||
$(top_srcdir)/m4/zz40-xc-ovr.m4 \
|
||||
$(top_srcdir)/m4/zz50-xc-ovr.m4 \
|
||||
$(top_srcdir)/m4/zz60-xc-ovr.m4 $(top_srcdir)/acinclude.m4 \
|
||||
$(top_srcdir)/configure.ac
|
||||
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
|
||||
$(ACLOCAL_M4)
|
||||
DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
|
||||
mkinstalldirs = $(install_sh) -d
|
||||
CONFIG_HEADER = $(top_builddir)/lib/curl_config.h
|
||||
CONFIG_CLEAN_FILES =
|
||||
CONFIG_CLEAN_VPATH_FILES =
|
||||
am__EXEEXT_1 = servers$(EXEEXT)
|
||||
PROGRAMS = $(noinst_PROGRAMS)
|
||||
servers_SOURCES = servers.c
|
||||
servers_OBJECTS = servers.$(OBJEXT)
|
||||
servers_LDADD = $(LDADD)
|
||||
servers_DEPENDENCIES =
|
||||
AM_V_lt = $(am__v_lt_@AM_V@)
|
||||
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
|
||||
am__v_lt_0 = --silent
|
||||
am__v_lt_1 =
|
||||
AM_V_P = $(am__v_P_@AM_V@)
|
||||
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
|
||||
am__v_P_0 = false
|
||||
am__v_P_1 = :
|
||||
AM_V_GEN = $(am__v_GEN_@AM_V@)
|
||||
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
|
||||
am__v_GEN_0 = @echo " GEN " $@;
|
||||
am__v_GEN_1 =
|
||||
AM_V_at = $(am__v_at_@AM_V@)
|
||||
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
|
||||
am__v_at_0 = @
|
||||
am__v_at_1 =
|
||||
DEFAULT_INCLUDES =
|
||||
depcomp = $(SHELL) $(top_srcdir)/depcomp
|
||||
am__maybe_remake_depfiles = depfiles
|
||||
am__depfiles_remade = ./$(DEPDIR)/servers.Po
|
||||
am__mv = mv -f
|
||||
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
|
||||
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
|
||||
LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
|
||||
$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
|
||||
$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
|
||||
$(AM_CFLAGS) $(CFLAGS)
|
||||
AM_V_CC = $(am__v_CC_@AM_V@)
|
||||
am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
|
||||
am__v_CC_0 = @echo " CC " $@;
|
||||
am__v_CC_1 =
|
||||
CCLD = $(CC)
|
||||
LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
|
||||
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
|
||||
$(AM_LDFLAGS) $(LDFLAGS) -o $@
|
||||
AM_V_CCLD = $(am__v_CCLD_@AM_V@)
|
||||
am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
|
||||
am__v_CCLD_0 = @echo " CCLD " $@;
|
||||
am__v_CCLD_1 =
|
||||
SOURCES = servers.c
|
||||
DIST_SOURCES = servers.c
|
||||
am__can_run_installinfo = \
|
||||
case $$AM_UPDATE_INFO_DIR in \
|
||||
n|no|NO) false;; \
|
||||
*) (install-info --version) >/dev/null 2>&1;; \
|
||||
esac
|
||||
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
|
||||
# Read a list of newline-separated strings from the standard input,
|
||||
# and print each of them once, without duplicates. Input order is
|
||||
# *not* preserved.
|
||||
am__uniquify_input = $(AWK) '\
|
||||
BEGIN { nonempty = 0; } \
|
||||
{ items[$$0] = 1; nonempty = 1; } \
|
||||
END { if (nonempty) { for (i in items) print i; }; } \
|
||||
'
|
||||
# Make sure the list of sources is unique. This is necessary because,
|
||||
# e.g., the same source file might be shared among _SOURCES variables
|
||||
# for different programs/libraries.
|
||||
am__define_uniq_tagged_files = \
|
||||
list='$(am__tagged_files)'; \
|
||||
unique=`for i in $$list; do \
|
||||
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
|
||||
done | $(am__uniquify_input)`
|
||||
am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.inc \
|
||||
$(top_srcdir)/depcomp
|
||||
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
|
||||
ACLOCAL = @ACLOCAL@
|
||||
AMTAR = @AMTAR@
|
||||
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
|
||||
APXS = @APXS@
|
||||
AR = @AR@
|
||||
AR_FLAGS = @AR_FLAGS@
|
||||
AS = @AS@
|
||||
AUTOCONF = @AUTOCONF@
|
||||
AUTOHEADER = @AUTOHEADER@
|
||||
AUTOMAKE = @AUTOMAKE@
|
||||
AWK = @AWK@
|
||||
BLANK_AT_MAKETIME = @BLANK_AT_MAKETIME@
|
||||
CADDY = @CADDY@
|
||||
CC = @CC@
|
||||
CCDEPMODE = @CCDEPMODE@
|
||||
CFLAGS = @CFLAGS@ @CURL_CFLAG_EXTRAS@
|
||||
CFLAG_CURL_SYMBOL_HIDING = @CFLAG_CURL_SYMBOL_HIDING@
|
||||
CONFIGURE_OPTIONS = @CONFIGURE_OPTIONS@
|
||||
CPP = @CPP@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
CSCOPE = @CSCOPE@
|
||||
CTAGS = @CTAGS@
|
||||
CURLVERSION = @CURLVERSION@
|
||||
CURL_CA_BUNDLE = @CURL_CA_BUNDLE@
|
||||
CURL_CA_EMBED = @CURL_CA_EMBED@
|
||||
CURL_CFLAG_EXTRAS = @CURL_CFLAG_EXTRAS@
|
||||
CURL_CPP = @CURL_CPP@
|
||||
CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX = @CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX@
|
||||
CURL_LIBCURL_VERSIONED_SYMBOLS_SONAME = @CURL_LIBCURL_VERSIONED_SYMBOLS_SONAME@
|
||||
CURL_NETWORK_AND_TIME_LIBS = @CURL_NETWORK_AND_TIME_LIBS@
|
||||
CYGPATH_W = @CYGPATH_W@
|
||||
DANTED = @DANTED@
|
||||
DEFS = @DEFS@
|
||||
DEPDIR = @DEPDIR@
|
||||
DLLTOOL = @DLLTOOL@
|
||||
DSYMUTIL = @DSYMUTIL@
|
||||
DUMPBIN = @DUMPBIN@
|
||||
ECHO_C = @ECHO_C@
|
||||
ECHO_N = @ECHO_N@
|
||||
ECHO_T = @ECHO_T@
|
||||
EGREP = @EGREP@
|
||||
ENABLE_SHARED = @ENABLE_SHARED@
|
||||
ENABLE_STATIC = @ENABLE_STATIC@
|
||||
ETAGS = @ETAGS@
|
||||
EXEEXT = @EXEEXT@
|
||||
FGREP = @FGREP@
|
||||
FILECMD = @FILECMD@
|
||||
FISH_FUNCTIONS_DIR = @FISH_FUNCTIONS_DIR@
|
||||
GCOV = @GCOV@
|
||||
GREP = @GREP@
|
||||
HAVE_LIBZ = @HAVE_LIBZ@
|
||||
HTTPD = @HTTPD@
|
||||
HTTPD_NGHTTPX = @HTTPD_NGHTTPX@
|
||||
INSTALL = @INSTALL@
|
||||
INSTALL_DATA = @INSTALL_DATA@
|
||||
INSTALL_PROGRAM = @INSTALL_PROGRAM@
|
||||
INSTALL_SCRIPT = @INSTALL_SCRIPT@
|
||||
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
|
||||
LCOV = @LCOV@
|
||||
LD = @LD@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBCURL_PC_CFLAGS = @LIBCURL_PC_CFLAGS@
|
||||
LIBCURL_PC_CFLAGS_PRIVATE = @LIBCURL_PC_CFLAGS_PRIVATE@
|
||||
LIBCURL_PC_LDFLAGS_PRIVATE = @LIBCURL_PC_LDFLAGS_PRIVATE@
|
||||
LIBCURL_PC_LIBS = @LIBCURL_PC_LIBS@
|
||||
LIBCURL_PC_LIBS_PRIVATE = @LIBCURL_PC_LIBS_PRIVATE@
|
||||
LIBCURL_PC_REQUIRES = @LIBCURL_PC_REQUIRES@
|
||||
LIBCURL_PC_REQUIRES_PRIVATE = @LIBCURL_PC_REQUIRES_PRIVATE@
|
||||
LIBOBJS = @LIBOBJS@
|
||||
|
||||
# Prevent LIBS from being used for all link targets
|
||||
LIBS = $(BLANK_AT_MAKETIME)
|
||||
LIBTOOL = @LIBTOOL@
|
||||
LIPO = @LIPO@
|
||||
LN_S = @LN_S@
|
||||
LTLIBOBJS = @LTLIBOBJS@
|
||||
LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
|
||||
MAINT = @MAINT@
|
||||
MAKEINFO = @MAKEINFO@
|
||||
MANIFEST_TOOL = @MANIFEST_TOOL@
|
||||
MKDIR_P = @MKDIR_P@
|
||||
NM = @NM@
|
||||
NMEDIT = @NMEDIT@
|
||||
OBJDUMP = @OBJDUMP@
|
||||
OBJEXT = @OBJEXT@
|
||||
OTOOL = @OTOOL@
|
||||
OTOOL64 = @OTOOL64@
|
||||
PACKAGE = @PACKAGE@
|
||||
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
|
||||
PACKAGE_NAME = @PACKAGE_NAME@
|
||||
PACKAGE_STRING = @PACKAGE_STRING@
|
||||
PACKAGE_TARNAME = @PACKAGE_TARNAME@
|
||||
PACKAGE_URL = @PACKAGE_URL@
|
||||
PACKAGE_VERSION = @PACKAGE_VERSION@
|
||||
PATH_SEPARATOR = @PATH_SEPARATOR@
|
||||
PERL = @PERL@
|
||||
PKGCONFIG = @PKGCONFIG@
|
||||
RANLIB = @RANLIB@
|
||||
RC = @RC@
|
||||
SED = @SED@
|
||||
SET_MAKE = @SET_MAKE@
|
||||
SHELL = @SHELL@
|
||||
SSL_BACKENDS = @SSL_BACKENDS@
|
||||
STRIP = @STRIP@
|
||||
SUPPORT_FEATURES = @SUPPORT_FEATURES@
|
||||
SUPPORT_PROTOCOLS = @SUPPORT_PROTOCOLS@
|
||||
TEST_NGHTTPX = @TEST_NGHTTPX@
|
||||
VERSION = @VERSION@
|
||||
VERSIONNUM = @VERSIONNUM@
|
||||
VSFTPD = @VSFTPD@
|
||||
ZLIB_LIBS = @ZLIB_LIBS@
|
||||
ZSH_FUNCTIONS_DIR = @ZSH_FUNCTIONS_DIR@
|
||||
abs_builddir = @abs_builddir@
|
||||
abs_srcdir = @abs_srcdir@
|
||||
abs_top_builddir = @abs_top_builddir@
|
||||
abs_top_srcdir = @abs_top_srcdir@
|
||||
ac_ct_AR = @ac_ct_AR@
|
||||
ac_ct_CC = @ac_ct_CC@
|
||||
ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
|
||||
am__include = @am__include@
|
||||
am__leading_dot = @am__leading_dot@
|
||||
am__quote = @am__quote@
|
||||
am__tar = @am__tar@
|
||||
am__untar = @am__untar@
|
||||
bindir = @bindir@
|
||||
build = @build@
|
||||
build_alias = @build_alias@
|
||||
build_cpu = @build_cpu@
|
||||
build_os = @build_os@
|
||||
build_vendor = @build_vendor@
|
||||
builddir = @builddir@
|
||||
datadir = @datadir@
|
||||
datarootdir = @datarootdir@
|
||||
docdir = @docdir@
|
||||
dvidir = @dvidir@
|
||||
exec_prefix = @exec_prefix@
|
||||
host = @host@
|
||||
host_alias = @host_alias@
|
||||
host_cpu = @host_cpu@
|
||||
host_os = @host_os@
|
||||
host_vendor = @host_vendor@
|
||||
htmldir = @htmldir@
|
||||
includedir = @includedir@
|
||||
infodir = @infodir@
|
||||
install_sh = @install_sh@
|
||||
libdir = @libdir@
|
||||
libexecdir = @libexecdir@
|
||||
libext = @libext@
|
||||
localedir = @localedir@
|
||||
localstatedir = @localstatedir@
|
||||
mandir = @mandir@
|
||||
mkdir_p = @mkdir_p@
|
||||
oldincludedir = @oldincludedir@
|
||||
pdfdir = @pdfdir@
|
||||
prefix = @prefix@
|
||||
program_transform_name = @program_transform_name@
|
||||
psdir = @psdir@
|
||||
runstatedir = @runstatedir@
|
||||
sbindir = @sbindir@
|
||||
sharedstatedir = @sharedstatedir@
|
||||
srcdir = @srcdir@
|
||||
sysconfdir = @sysconfdir@
|
||||
target_alias = @target_alias@
|
||||
top_build_prefix = @top_build_prefix@
|
||||
top_builddir = @top_builddir@
|
||||
top_srcdir = @top_srcdir@
|
||||
|
||||
#***************************************************************************
|
||||
# _ _ ____ _
|
||||
# Project ___| | | | _ \| |
|
||||
# / __| | | | |_) | |
|
||||
# | (__| |_| | _ <| |___
|
||||
# \___|\___/|_| \_\_____|
|
||||
#
|
||||
# Copyright (C) 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.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.
|
||||
#
|
||||
# SPDX-License-Identifier: curl
|
||||
#
|
||||
###########################################################################
|
||||
AUTOMAKE_OPTIONS = foreign nostdinc
|
||||
|
||||
# Specify our include paths here, and do it relative to $(top_srcdir) and
|
||||
# $(top_builddir), to ensure that these paths which belong to the library
|
||||
# being currently built and tested are searched before the library which
|
||||
# might possibly already be installed in the system.
|
||||
#
|
||||
# $(top_srcdir)/include is for libcurl's external include files
|
||||
# $(top_builddir)/lib is for libcurl's generated lib/curl_config.h file
|
||||
# $(top_srcdir)/lib for libcurl's lib/curl_setup.h and other "borrowed" files
|
||||
# $(srcdir) for the generated bundle source to find included test sources
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include \
|
||||
-I$(top_builddir)/lib \
|
||||
-I$(top_srcdir)/lib \
|
||||
-I$(srcdir)
|
||||
|
||||
BUNDLE = servers
|
||||
|
||||
# Files referenced from the bundle source
|
||||
FIRST_C = first.c
|
||||
FIRST_H = first.h
|
||||
|
||||
# Common files used by test programs
|
||||
UTILS_C = getpart.c util.c
|
||||
UTILS_H =
|
||||
CURLX_C = \
|
||||
../../lib/curlx/base64.c \
|
||||
../../lib/curlx/fopen.c \
|
||||
../../lib/curlx/inet_ntop.c \
|
||||
../../lib/curlx/inet_pton.c \
|
||||
../../lib/curlx/multibyte.c \
|
||||
../../lib/curlx/nonblock.c \
|
||||
../../lib/curlx/strerr.c \
|
||||
../../lib/curlx/strparse.c \
|
||||
../../lib/curlx/timediff.c \
|
||||
../../lib/curlx/timeval.c \
|
||||
../../lib/curlx/version_win32.c \
|
||||
../../lib/curlx/wait.c \
|
||||
../../lib/curlx/warnless.c \
|
||||
../../lib/curlx/winapi.c
|
||||
|
||||
|
||||
# All test servers
|
||||
TESTS_C = \
|
||||
dnsd.c \
|
||||
mqttd.c \
|
||||
resolve.c \
|
||||
rtspd.c \
|
||||
sockfilt.c \
|
||||
socksd.c \
|
||||
sws.c \
|
||||
tftpd.c
|
||||
|
||||
|
||||
# Get BUNDLE, FIRST_C, FIRST_H, UTILS_C, UTILS_H, CURLX_C, TESTS_C variables
|
||||
EXTRA_DIST = CMakeLists.txt .checksrc $(FIRST_C) $(FIRST_H) $(UTILS_C) $(UTILS_H) $(TESTS_C)
|
||||
LDADD = @CURL_NETWORK_AND_TIME_LIBS@
|
||||
CLEANFILES = $(BUNDLE).c
|
||||
CHECKSRC = $(CS_$(V))
|
||||
CS_0 = @echo " RUN " $@;
|
||||
CS_1 =
|
||||
CS_ = $(CS_0)
|
||||
all: all-am
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .c .lo .o .obj
|
||||
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(srcdir)/Makefile.inc $(am__configure_deps)
|
||||
@for dep in $?; do \
|
||||
case '$(am__configure_deps)' in \
|
||||
*$$dep*) \
|
||||
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
|
||||
&& { if test -f $@; then exit 0; else break; fi; }; \
|
||||
exit 1;; \
|
||||
esac; \
|
||||
done; \
|
||||
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign tests/server/Makefile'; \
|
||||
$(am__cd) $(top_srcdir) && \
|
||||
$(AUTOMAKE) --foreign tests/server/Makefile
|
||||
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
|
||||
@case '$?' in \
|
||||
*config.status*) \
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
|
||||
*) \
|
||||
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
|
||||
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
|
||||
esac;
|
||||
$(srcdir)/Makefile.inc $(am__empty):
|
||||
|
||||
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
|
||||
$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
$(am__aclocal_m4_deps):
|
||||
|
||||
clean-noinstPROGRAMS:
|
||||
@list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
|
||||
echo " rm -f" $$list; \
|
||||
rm -f $$list || exit $$?; \
|
||||
test -n "$(EXEEXT)" || exit 0; \
|
||||
list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
|
||||
echo " rm -f" $$list; \
|
||||
rm -f $$list
|
||||
|
||||
servers$(EXEEXT): $(servers_OBJECTS) $(servers_DEPENDENCIES) $(EXTRA_servers_DEPENDENCIES)
|
||||
@rm -f servers$(EXEEXT)
|
||||
$(AM_V_CCLD)$(LINK) $(servers_OBJECTS) $(servers_LDADD) $(LIBS)
|
||||
|
||||
mostlyclean-compile:
|
||||
-rm -f *.$(OBJEXT)
|
||||
|
||||
distclean-compile:
|
||||
-rm -f *.tab.c
|
||||
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/servers.Po@am__quote@ # am--include-marker
|
||||
|
||||
$(am__depfiles_remade):
|
||||
@$(MKDIR_P) $(@D)
|
||||
@echo '# dummy' >$@-t && $(am__mv) $@-t $@
|
||||
|
||||
am--depfiles: $(am__depfiles_remade)
|
||||
|
||||
.c.o:
|
||||
@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
|
||||
@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
|
||||
|
||||
.c.obj:
|
||||
@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
|
||||
@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
|
||||
|
||||
.c.lo:
|
||||
@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
|
||||
@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
|
||||
|
||||
mostlyclean-libtool:
|
||||
-rm -f *.lo
|
||||
|
||||
clean-libtool:
|
||||
-rm -rf .libs _libs
|
||||
|
||||
ID: $(am__tagged_files)
|
||||
$(am__define_uniq_tagged_files); mkid -fID $$unique
|
||||
tags: tags-am
|
||||
TAGS: tags
|
||||
|
||||
tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
|
||||
set x; \
|
||||
here=`pwd`; \
|
||||
$(am__define_uniq_tagged_files); \
|
||||
shift; \
|
||||
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
|
||||
test -n "$$unique" || unique=$$empty_fix; \
|
||||
if test $$# -gt 0; then \
|
||||
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
|
||||
"$$@" $$unique; \
|
||||
else \
|
||||
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
|
||||
$$unique; \
|
||||
fi; \
|
||||
fi
|
||||
ctags: ctags-am
|
||||
|
||||
CTAGS: ctags
|
||||
ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
|
||||
$(am__define_uniq_tagged_files); \
|
||||
test -z "$(CTAGS_ARGS)$$unique" \
|
||||
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
|
||||
$$unique
|
||||
|
||||
GTAGS:
|
||||
here=`$(am__cd) $(top_builddir) && pwd` \
|
||||
&& $(am__cd) $(top_srcdir) \
|
||||
&& gtags -i $(GTAGS_ARGS) "$$here"
|
||||
cscopelist: cscopelist-am
|
||||
|
||||
cscopelist-am: $(am__tagged_files)
|
||||
list='$(am__tagged_files)'; \
|
||||
case "$(srcdir)" in \
|
||||
[\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
|
||||
*) sdir=$(subdir)/$(srcdir) ;; \
|
||||
esac; \
|
||||
for i in $$list; do \
|
||||
if test -f "$$i"; then \
|
||||
echo "$(subdir)/$$i"; \
|
||||
else \
|
||||
echo "$$sdir/$$i"; \
|
||||
fi; \
|
||||
done >> $(top_builddir)/cscope.files
|
||||
|
||||
distclean-tags:
|
||||
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
|
||||
distdir: $(BUILT_SOURCES)
|
||||
$(MAKE) $(AM_MAKEFLAGS) distdir-am
|
||||
|
||||
distdir-am: $(DISTFILES)
|
||||
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
|
||||
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
|
||||
list='$(DISTFILES)'; \
|
||||
dist_files=`for file in $$list; do echo $$file; done | \
|
||||
sed -e "s|^$$srcdirstrip/||;t" \
|
||||
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
|
||||
case $$dist_files in \
|
||||
*/*) $(MKDIR_P) `echo "$$dist_files" | \
|
||||
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
|
||||
sort -u` ;; \
|
||||
esac; \
|
||||
for file in $$dist_files; do \
|
||||
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
|
||||
if test -d $$d/$$file; then \
|
||||
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
|
||||
if test -d "$(distdir)/$$file"; then \
|
||||
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
|
||||
fi; \
|
||||
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
|
||||
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
|
||||
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
|
||||
fi; \
|
||||
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
|
||||
else \
|
||||
test -f "$(distdir)/$$file" \
|
||||
|| cp -p $$d/$$file "$(distdir)/$$file" \
|
||||
|| exit 1; \
|
||||
fi; \
|
||||
done
|
||||
check-am: all-am
|
||||
check: check-am
|
||||
@NOT_CURL_CI_FALSE@all-local:
|
||||
all-am: Makefile $(PROGRAMS) all-local
|
||||
installdirs:
|
||||
install: install-am
|
||||
install-exec: install-exec-am
|
||||
install-data: install-data-am
|
||||
uninstall: uninstall-am
|
||||
|
||||
install-am: all-am
|
||||
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
|
||||
|
||||
installcheck: installcheck-am
|
||||
install-strip:
|
||||
if test -z '$(STRIP)'; then \
|
||||
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
|
||||
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
|
||||
install; \
|
||||
else \
|
||||
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
|
||||
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
|
||||
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
|
||||
fi
|
||||
mostlyclean-generic:
|
||||
|
||||
clean-generic:
|
||||
-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
|
||||
|
||||
distclean-generic:
|
||||
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
|
||||
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
|
||||
|
||||
maintainer-clean-generic:
|
||||
@echo "This command is intended for maintainers to use"
|
||||
@echo "it deletes files that may require special tools to rebuild."
|
||||
clean: clean-am
|
||||
|
||||
clean-am: clean-generic clean-libtool clean-local clean-noinstPROGRAMS \
|
||||
mostlyclean-am
|
||||
|
||||
distclean: distclean-am
|
||||
-rm -f ./$(DEPDIR)/servers.Po
|
||||
-rm -f Makefile
|
||||
distclean-am: clean-am distclean-compile distclean-generic \
|
||||
distclean-tags
|
||||
|
||||
dvi: dvi-am
|
||||
|
||||
dvi-am:
|
||||
|
||||
html: html-am
|
||||
|
||||
html-am:
|
||||
|
||||
info: info-am
|
||||
|
||||
info-am:
|
||||
|
||||
install-data-am:
|
||||
|
||||
install-dvi: install-dvi-am
|
||||
|
||||
install-dvi-am:
|
||||
|
||||
install-exec-am:
|
||||
|
||||
install-html: install-html-am
|
||||
|
||||
install-html-am:
|
||||
|
||||
install-info: install-info-am
|
||||
|
||||
install-info-am:
|
||||
|
||||
install-man:
|
||||
|
||||
install-pdf: install-pdf-am
|
||||
|
||||
install-pdf-am:
|
||||
|
||||
install-ps: install-ps-am
|
||||
|
||||
install-ps-am:
|
||||
|
||||
installcheck-am:
|
||||
|
||||
maintainer-clean: maintainer-clean-am
|
||||
-rm -f ./$(DEPDIR)/servers.Po
|
||||
-rm -f Makefile
|
||||
maintainer-clean-am: distclean-am maintainer-clean-generic
|
||||
|
||||
mostlyclean: mostlyclean-am
|
||||
|
||||
mostlyclean-am: mostlyclean-compile mostlyclean-generic \
|
||||
mostlyclean-libtool
|
||||
|
||||
pdf: pdf-am
|
||||
|
||||
pdf-am:
|
||||
|
||||
ps: ps-am
|
||||
|
||||
ps-am:
|
||||
|
||||
uninstall-am:
|
||||
|
||||
.MAKE: install-am install-strip
|
||||
|
||||
.PHONY: CTAGS GTAGS TAGS all all-am all-local am--depfiles check \
|
||||
check-am clean clean-generic clean-libtool clean-local \
|
||||
clean-noinstPROGRAMS cscopelist-am ctags ctags-am distclean \
|
||||
distclean-compile distclean-generic distclean-libtool \
|
||||
distclean-tags distdir dvi dvi-am html html-am info info-am \
|
||||
install install-am install-data install-data-am install-dvi \
|
||||
install-dvi-am install-exec install-exec-am install-html \
|
||||
install-html-am install-info install-info-am install-man \
|
||||
install-pdf install-pdf-am install-ps install-ps-am \
|
||||
install-strip installcheck installcheck-am installdirs \
|
||||
maintainer-clean maintainer-clean-generic mostlyclean \
|
||||
mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
|
||||
pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am
|
||||
|
||||
.PRECIOUS: Makefile
|
||||
|
||||
|
||||
$(BUNDLE).c: $(top_srcdir)/scripts/mk-unity.pl Makefile.inc $(FIRST_C) $(UTILS_C) $(CURLX_C) $(TESTS_C)
|
||||
@PERL@ $(top_srcdir)/scripts/mk-unity.pl --include $(UTILS_C) $(CURLX_C) --test $(TESTS_C) > $(BUNDLE).c
|
||||
|
||||
# ignore generated C files since they play by slightly different rules!
|
||||
checksrc:
|
||||
$(CHECKSRC)(@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(srcdir) \
|
||||
-W$(srcdir)/$(BUNDLE).c \
|
||||
$(srcdir)/*.[ch])
|
||||
|
||||
@NOT_CURL_CI_TRUE@all-local: checksrc
|
||||
|
||||
clean-local:
|
||||
rm -f $(BUNDLE)
|
||||
|
||||
# Tell versions [3.59,3.63) of GNU make to not export all variables.
|
||||
# Otherwise a system limit (for SysV at least) may be exceeded.
|
||||
.NOEXPORT:
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
#***************************************************************************
|
||||
# _ _ ____ _
|
||||
# Project ___| | | | _ \| |
|
||||
# / __| | | | |_) | |
|
||||
# | (__| |_| | _ <| |___
|
||||
# \___|\___/|_| \_\_____|
|
||||
#
|
||||
# Copyright (C) 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.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.
|
||||
#
|
||||
# SPDX-License-Identifier: curl
|
||||
#
|
||||
###########################################################################
|
||||
# Shared between CMakeLists.txt and Makefile.am
|
||||
|
||||
BUNDLE = servers
|
||||
|
||||
# Files referenced from the bundle source
|
||||
FIRST_C = first.c
|
||||
FIRST_H = first.h
|
||||
|
||||
# Common files used by test programs
|
||||
UTILS_C = getpart.c util.c
|
||||
UTILS_H =
|
||||
|
||||
CURLX_C = \
|
||||
../../lib/curlx/base64.c \
|
||||
../../lib/curlx/fopen.c \
|
||||
../../lib/curlx/inet_ntop.c \
|
||||
../../lib/curlx/inet_pton.c \
|
||||
../../lib/curlx/multibyte.c \
|
||||
../../lib/curlx/nonblock.c \
|
||||
../../lib/curlx/strerr.c \
|
||||
../../lib/curlx/strparse.c \
|
||||
../../lib/curlx/timediff.c \
|
||||
../../lib/curlx/timeval.c \
|
||||
../../lib/curlx/version_win32.c \
|
||||
../../lib/curlx/wait.c \
|
||||
../../lib/curlx/warnless.c \
|
||||
../../lib/curlx/winapi.c
|
||||
|
||||
# All test servers
|
||||
TESTS_C = \
|
||||
dnsd.c \
|
||||
mqttd.c \
|
||||
resolve.c \
|
||||
rtspd.c \
|
||||
sockfilt.c \
|
||||
socksd.c \
|
||||
sws.c \
|
||||
tftpd.c
|
||||
+681
@@ -0,0 +1,681 @@
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 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.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.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
#include "first.h"
|
||||
|
||||
static int dnsd_wrotepidfile = 0;
|
||||
static int dnsd_wroteportfile = 0;
|
||||
|
||||
static unsigned short get16bit(const unsigned char **pkt,
|
||||
size_t *size)
|
||||
{
|
||||
const unsigned char *p = *pkt;
|
||||
(*pkt) += 2;
|
||||
*size -= 2;
|
||||
return (unsigned short)((p[0] << 8) | p[1]);
|
||||
}
|
||||
|
||||
static char name[256];
|
||||
|
||||
static int qname(const unsigned char **pkt, size_t *size)
|
||||
{
|
||||
unsigned char length;
|
||||
int o = 0;
|
||||
const unsigned char *p = *pkt;
|
||||
do {
|
||||
int i;
|
||||
length = *p++;
|
||||
if(*size < length)
|
||||
/* too long */
|
||||
return 1;
|
||||
if(length && o)
|
||||
name[o++] = '.';
|
||||
for(i = 0; i < length; i++) {
|
||||
name[o++] = *p++;
|
||||
}
|
||||
} while(length);
|
||||
*size -= (p - *pkt);
|
||||
*pkt = p;
|
||||
name[o++] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define QTYPE_A 1
|
||||
#define QTYPE_AAAA 28
|
||||
#define QTYPE_HTTPS 0x41
|
||||
|
||||
static const char *type2string(unsigned short qtype)
|
||||
{
|
||||
switch(qtype) {
|
||||
case QTYPE_A:
|
||||
return "A";
|
||||
case QTYPE_AAAA:
|
||||
return "AAAA";
|
||||
case QTYPE_HTTPS:
|
||||
return "HTTPS";
|
||||
}
|
||||
return "<unknown>";
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle initial connection protocol.
|
||||
*
|
||||
* Return query (qname + type + class), type and id.
|
||||
*/
|
||||
static int store_incoming(const unsigned char *data, size_t size,
|
||||
unsigned char *qbuf, size_t qbuflen, size_t *qlen,
|
||||
unsigned short *qtype, unsigned short *idp)
|
||||
{
|
||||
FILE *server;
|
||||
char dumpfile[256];
|
||||
#if 0
|
||||
size_t i;
|
||||
#endif
|
||||
unsigned short qd;
|
||||
const unsigned char *qptr;
|
||||
size_t qsize;
|
||||
|
||||
*qlen = 0;
|
||||
*qtype = 0;
|
||||
*idp = 0;
|
||||
|
||||
snprintf(dumpfile, sizeof(dumpfile), "%s/dnsd.input", logdir);
|
||||
|
||||
/* Open request dump file. */
|
||||
server = fopen(dumpfile, "ab");
|
||||
if(!server) {
|
||||
char errbuf[STRERROR_LEN];
|
||||
int error = errno;
|
||||
logmsg("fopen() failed with error (%d) %s",
|
||||
error, curlx_strerror(error, errbuf, sizeof(errbuf)));
|
||||
logmsg("Error opening file '%s'", dumpfile);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
1 1 1 1 1 1
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
| ID |
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
|QR| Opcode |AA|TC|RD|RA| Z | RCODE |
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
| QDCOUNT |
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
| ANCOUNT |
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
| NSCOUNT |
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
| ARCOUNT |
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
*/
|
||||
*idp = get16bit(&data, &size);
|
||||
data += 2; /* skip the next 16 bits */
|
||||
size -= 2;
|
||||
#if 0
|
||||
fprintf(server, "QR: %x\n", (id & 0x8000) > 15);
|
||||
fprintf(server, "OPCODE: %x\n", (id & 0x7800) >> 11);
|
||||
fprintf(server, "TC: %x\n", (id & 0x200) >> 9);
|
||||
fprintf(server, "RD: %x\n", (id & 0x100) >> 8);
|
||||
fprintf(server, "Z: %x\n", (id & 0x70) >> 4);
|
||||
fprintf(server, "RCODE: %x\n", (id & 0x0f));
|
||||
#endif
|
||||
(void) get16bit(&data, &size);
|
||||
|
||||
data += 6; /* skip ANCOUNT, NSCOUNT and ARCOUNT */
|
||||
size -= 6;
|
||||
|
||||
/* store pointer and size at the QD point */
|
||||
qsize = size;
|
||||
qptr = data;
|
||||
|
||||
if(!qname(&data, &size)) {
|
||||
qd = get16bit(&data, &size);
|
||||
fprintf(server, "QNAME %s QTYPE %s\n", name, type2string(qd));
|
||||
*qtype = qd;
|
||||
logmsg("Question for '%s' type %x / %s", name, qd,
|
||||
type2string(qd));
|
||||
|
||||
(void) get16bit(&data, &size);
|
||||
|
||||
*qlen = qsize - size; /* total size of the query */
|
||||
if(*qlen > qbuflen) {
|
||||
logmsg("dnsd: query too large: %lu > %lu",
|
||||
(unsigned long)*qlen, (unsigned long)qbuflen);
|
||||
fclose(server);
|
||||
return -1;
|
||||
}
|
||||
memcpy(qbuf, qptr, *qlen);
|
||||
}
|
||||
else
|
||||
logmsg("Bad input qname");
|
||||
#if 0
|
||||
for(i = 0; i < size; i++) {
|
||||
fprintf(server, "%02d", (unsigned int)data[i]);
|
||||
}
|
||||
fprintf(server, "\n");
|
||||
#endif
|
||||
|
||||
fclose(server);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void add_answer(unsigned char *bytes, size_t *w,
|
||||
const unsigned char *a, size_t alen,
|
||||
unsigned short qtype)
|
||||
{
|
||||
size_t i = *w;
|
||||
|
||||
/* add answer */
|
||||
bytes[i++] = 0xc0;
|
||||
bytes[i++] = 0x0c; /* points to the query at this fixed packet index */
|
||||
|
||||
/* QTYPE */
|
||||
bytes[i++] = (unsigned char)(qtype >> 8);
|
||||
bytes[i++] = (unsigned char)(qtype & 0xff);
|
||||
|
||||
/* QCLASS IN */
|
||||
bytes[i++] = 0x00;
|
||||
bytes[i++] = 0x01;
|
||||
|
||||
/* TTL, Time to live: 2580 (43 minutes) */
|
||||
bytes[i++] = 0x00;
|
||||
bytes[i++] = 0x00;
|
||||
bytes[i++] = 0x0a;
|
||||
bytes[i++] = 0x14;
|
||||
|
||||
/* QTYPE size */
|
||||
bytes[i++] = (unsigned char)(alen >> 8);
|
||||
bytes[i++] = (unsigned char)(alen & 0xff);
|
||||
|
||||
memcpy(&bytes[i], a, alen);
|
||||
i += alen;
|
||||
|
||||
*w = i;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#define SENDTO3 int
|
||||
#else
|
||||
#define SENDTO3 size_t
|
||||
#endif
|
||||
|
||||
#define INSTRUCTIONS "dnsd.cmd"
|
||||
|
||||
#define MAX_ALPN 5
|
||||
|
||||
static unsigned char ipv4_pref[4];
|
||||
static unsigned char ipv6_pref[16];
|
||||
static unsigned char alpn_pref[MAX_ALPN];
|
||||
static int alpn_count;
|
||||
static unsigned char ancount_a;
|
||||
static unsigned char ancount_aaaa;
|
||||
|
||||
/* this is an answer to a question */
|
||||
static int send_response(curl_socket_t sock,
|
||||
const struct sockaddr *addr, curl_socklen_t addrlen,
|
||||
unsigned char *qbuf, size_t qlen,
|
||||
unsigned short qtype, unsigned short id)
|
||||
{
|
||||
ssize_t rc;
|
||||
size_t i;
|
||||
int a;
|
||||
char addrbuf[128]; /* IP address buffer */
|
||||
unsigned char bytes[256] = {
|
||||
0x80, 0xea, /* ID, overwrite */
|
||||
0x81, 0x80,
|
||||
/*
|
||||
Flags: 0x8180 Standard query response, No error
|
||||
1... .... .... .... = Response: Message is a response
|
||||
.000 0... .... .... = Opcode: Standard query (0)
|
||||
.... .0.. .... .... = Authoritative: Server is not an authority for
|
||||
domain
|
||||
.... ..0. .... .... = Truncated: Message is not truncated
|
||||
.... ...1 .... .... = Recursion desired: Do query recursively
|
||||
.... .... 1... .... = Recursion available: Server can do recursive
|
||||
queries
|
||||
.... .... .0.. .... = Z: reserved (0)
|
||||
.... .... ..0. .... = Answer authenticated: Answer/authority portion
|
||||
was not authenticated by the server
|
||||
.... .... ...0 .... = Non-authenticated data: Unacceptable
|
||||
.... .... .... 0000 = Reply code: No error (0)
|
||||
*/
|
||||
0x0, 0x1, /* QDCOUNT a single question */
|
||||
0x0, 0x0, /* ANCOUNT number of answers */
|
||||
0x0, 0x0, /* NSCOUNT */
|
||||
0x0, 0x0 /* ARCOUNT */
|
||||
};
|
||||
|
||||
bytes[0] = (unsigned char)(id >> 8);
|
||||
bytes[1] = (unsigned char)(id & 0xff);
|
||||
|
||||
if(qlen > (sizeof(bytes) - 12))
|
||||
return -1;
|
||||
|
||||
/* append query, includes QTYPE and QCLASS */
|
||||
memcpy(&bytes[12], qbuf, qlen);
|
||||
|
||||
i = 12 + qlen;
|
||||
|
||||
switch(qtype) {
|
||||
case QTYPE_A:
|
||||
bytes[7] = ancount_a;
|
||||
for(a = 0; a < ancount_a; a++) {
|
||||
const unsigned char *store = ipv4_pref;
|
||||
add_answer(bytes, &i, store, sizeof(ipv4_pref), QTYPE_A);
|
||||
logmsg("Sending back A (%x) '%s'", QTYPE_A,
|
||||
curlx_inet_ntop(AF_INET, store, addrbuf, sizeof(addrbuf)));
|
||||
}
|
||||
break;
|
||||
case QTYPE_AAAA:
|
||||
bytes[7] = ancount_aaaa;
|
||||
for(a = 0; a < ancount_aaaa; a++) {
|
||||
const unsigned char *store = ipv6_pref;
|
||||
add_answer(bytes, &i, store, sizeof(ipv6_pref), QTYPE_AAAA);
|
||||
logmsg("Sending back AAAA (%x) '%s'", QTYPE_AAAA,
|
||||
curlx_inet_ntop(AF_INET6, store, addrbuf, sizeof(addrbuf)));
|
||||
}
|
||||
break;
|
||||
case QTYPE_HTTPS:
|
||||
bytes[7] = 1; /* one answer */
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef __AMIGA__
|
||||
/* Amiga breakage */
|
||||
(void)rc;
|
||||
(void)sock;
|
||||
(void)addr;
|
||||
(void)addrlen;
|
||||
fprintf(stderr, "Not working\n");
|
||||
return -1;
|
||||
#else
|
||||
rc = sendto(sock, (const void *)bytes, (SENDTO3) i, 0, addr, addrlen);
|
||||
if(rc != (ssize_t)i) {
|
||||
fprintf(stderr, "failed sending %d bytes\n", (int)i);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void read_instructions(void)
|
||||
{
|
||||
char file[256];
|
||||
FILE *f;
|
||||
snprintf(file, sizeof(file), "%s/" INSTRUCTIONS, logdir);
|
||||
f = fopen(file, FOPEN_READTEXT);
|
||||
if(f) {
|
||||
char buf[256];
|
||||
ancount_aaaa = ancount_a = 0;
|
||||
alpn_count = 0;
|
||||
while(fgets(buf, sizeof(buf), f)) {
|
||||
char *p = strchr(buf, '\n');
|
||||
if(p) {
|
||||
int rc;
|
||||
*p = 0;
|
||||
if(!strncmp("A: ", buf, 3)) {
|
||||
rc = curlx_inet_pton(AF_INET, &buf[3], ipv4_pref);
|
||||
ancount_a = (rc == 1);
|
||||
}
|
||||
else if(!strncmp("AAAA: ", buf, 6)) {
|
||||
char *p6 = &buf[6];
|
||||
if(*p6 == '[') {
|
||||
char *pt = strchr(p6, ']');
|
||||
if(pt)
|
||||
*pt = 0;
|
||||
p6++;
|
||||
}
|
||||
rc = curlx_inet_pton(AF_INET6, p6, ipv6_pref);
|
||||
ancount_aaaa = (rc == 1);
|
||||
}
|
||||
else if(!strncmp("ALPN: ", buf, 6)) {
|
||||
char *ap = &buf[6];
|
||||
rc = 0;
|
||||
while(*ap) {
|
||||
if('h' == *ap) {
|
||||
ap++;
|
||||
if(*ap >= '1' && *ap <= '3') {
|
||||
if(alpn_count < MAX_ALPN)
|
||||
alpn_pref[alpn_count++] = *ap;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
rc = 0;
|
||||
}
|
||||
if(rc != 1) {
|
||||
logmsg("Bad line in %s: '%s'\n", file, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
else
|
||||
logmsg("Error opening file '%s'", file);
|
||||
}
|
||||
|
||||
static int test_dnsd(int argc, char **argv)
|
||||
{
|
||||
srvr_sockaddr_union_t me;
|
||||
ssize_t n = 0;
|
||||
int arg = 1;
|
||||
unsigned short port = 9123; /* UDP */
|
||||
curl_socket_t sock = CURL_SOCKET_BAD;
|
||||
int flag;
|
||||
int rc;
|
||||
int error;
|
||||
char errbuf[STRERROR_LEN];
|
||||
int result = 0;
|
||||
|
||||
pidname = ".dnsd.pid";
|
||||
serverlogfile = "log/dnsd.log";
|
||||
serverlogslocked = 0;
|
||||
|
||||
while(argc > arg) {
|
||||
if(!strcmp("--verbose", argv[arg])) {
|
||||
arg++;
|
||||
/* nothing yet */
|
||||
}
|
||||
else if(!strcmp("--version", argv[arg])) {
|
||||
printf("dnsd IPv4%s\n",
|
||||
#ifdef USE_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("--logfile", argv[arg])) {
|
||||
arg++;
|
||||
if(argc > arg)
|
||||
serverlogfile = argv[arg++];
|
||||
}
|
||||
else if(!strcmp("--logdir", argv[arg])) {
|
||||
arg++;
|
||||
if(argc > arg)
|
||||
logdir = argv[arg++];
|
||||
}
|
||||
else if(!strcmp("--ipv4", argv[arg])) {
|
||||
#ifdef USE_IPV6
|
||||
ipv_inuse = "IPv4";
|
||||
use_ipv6 = FALSE;
|
||||
#endif
|
||||
arg++;
|
||||
}
|
||||
else if(!strcmp("--ipv6", argv[arg])) {
|
||||
#ifdef USE_IPV6
|
||||
ipv_inuse = "IPv6";
|
||||
use_ipv6 = TRUE;
|
||||
#endif
|
||||
arg++;
|
||||
}
|
||||
else if(!strcmp("--port", argv[arg])) {
|
||||
arg++;
|
||||
if(argc > arg) {
|
||||
port = (unsigned short)atoi(argv[arg]);
|
||||
arg++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(argv[arg])
|
||||
fprintf(stderr, "unknown option: %s\n", argv[arg]);
|
||||
puts("Usage: dnsd [option]\n"
|
||||
" --version\n"
|
||||
" --logfile [file]\n"
|
||||
" --logdir [directory]\n"
|
||||
" --pidfile [file]\n"
|
||||
" --portfile [file]\n"
|
||||
" --ipv4\n"
|
||||
" --ipv6\n"
|
||||
" --port [port]\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(loglockfile, sizeof(loglockfile), "%s/%s/dnsd-%s.lock",
|
||||
logdir, SERVERLOGS_LOCKDIR, ipv_inuse);
|
||||
|
||||
#ifdef _WIN32
|
||||
if(win32_init())
|
||||
return 2;
|
||||
#endif
|
||||
|
||||
#ifdef USE_IPV6
|
||||
if(!use_ipv6)
|
||||
#endif
|
||||
sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
#ifdef USE_IPV6
|
||||
else
|
||||
sock = socket(AF_INET6, SOCK_DGRAM, 0);
|
||||
#endif
|
||||
|
||||
if(CURL_SOCKET_BAD == sock) {
|
||||
error = SOCKERRNO;
|
||||
logmsg("Error creating socket (%d) %s",
|
||||
error, curlx_strerror(error, errbuf, sizeof(errbuf)));
|
||||
result = 1;
|
||||
goto dnsd_cleanup;
|
||||
}
|
||||
|
||||
flag = 1;
|
||||
if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
|
||||
(void *)&flag, sizeof(flag))) {
|
||||
error = SOCKERRNO;
|
||||
logmsg("setsockopt(SO_REUSEADDR) failed with error (%d) %s",
|
||||
error, curlx_strerror(error, errbuf, sizeof(errbuf)));
|
||||
result = 1;
|
||||
goto dnsd_cleanup;
|
||||
}
|
||||
|
||||
#ifdef USE_IPV6
|
||||
if(!use_ipv6) {
|
||||
#endif
|
||||
memset(&me.sa4, 0, sizeof(me.sa4));
|
||||
me.sa4.sin_family = AF_INET;
|
||||
me.sa4.sin_addr.s_addr = INADDR_ANY;
|
||||
me.sa4.sin_port = htons(port);
|
||||
rc = bind(sock, &me.sa, sizeof(me.sa4));
|
||||
#ifdef USE_IPV6
|
||||
}
|
||||
else {
|
||||
memset(&me.sa6, 0, sizeof(me.sa6));
|
||||
me.sa6.sin6_family = AF_INET6;
|
||||
me.sa6.sin6_addr = in6addr_any;
|
||||
me.sa6.sin6_port = htons(port);
|
||||
rc = bind(sock, &me.sa, sizeof(me.sa6));
|
||||
}
|
||||
#endif /* USE_IPV6 */
|
||||
if(rc) {
|
||||
error = SOCKERRNO;
|
||||
logmsg("Error binding socket on port %hu (%d) %s", port,
|
||||
error, curlx_strerror(error, errbuf, sizeof(errbuf)));
|
||||
result = 1;
|
||||
goto dnsd_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 USE_IPV6
|
||||
if(!use_ipv6)
|
||||
#endif
|
||||
la_size = sizeof(localaddr.sa4);
|
||||
#ifdef USE_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, curlx_strerror(error, errbuf, sizeof(errbuf)));
|
||||
sclose(sock);
|
||||
goto dnsd_cleanup;
|
||||
}
|
||||
switch(localaddr.sa.sa_family) {
|
||||
case AF_INET:
|
||||
port = ntohs(localaddr.sa4.sin_port);
|
||||
break;
|
||||
#ifdef USE_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 dnsd_cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
dnsd_wrotepidfile = write_pidfile(pidname);
|
||||
if(!dnsd_wrotepidfile) {
|
||||
result = 1;
|
||||
goto dnsd_cleanup;
|
||||
}
|
||||
|
||||
if(portname) {
|
||||
dnsd_wroteportfile = write_portfile(portname, port);
|
||||
if(!dnsd_wroteportfile) {
|
||||
result = 1;
|
||||
goto dnsd_cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
logmsg("Running %s version on port UDP/%d", ipv_inuse, (int)port);
|
||||
|
||||
for(;;) {
|
||||
unsigned short id = 0;
|
||||
unsigned char inbuffer[1500];
|
||||
srvr_sockaddr_union_t from;
|
||||
curl_socklen_t fromlen;
|
||||
unsigned char qbuf[256]; /* query storage */
|
||||
size_t qlen = 0; /* query size */
|
||||
unsigned short qtype = 0;
|
||||
fromlen = sizeof(from);
|
||||
#ifdef USE_IPV6
|
||||
if(!use_ipv6)
|
||||
#endif
|
||||
fromlen = sizeof(from.sa4);
|
||||
#ifdef USE_IPV6
|
||||
else
|
||||
fromlen = sizeof(from.sa6);
|
||||
#endif
|
||||
n = (ssize_t)recvfrom(sock, (char *)inbuffer, sizeof(inbuffer), 0,
|
||||
&from.sa, &fromlen);
|
||||
if(got_exit_signal)
|
||||
break;
|
||||
if(n < 0) {
|
||||
logmsg("recvfrom");
|
||||
result = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
/* read once per incoming query, which is probably more than one
|
||||
per test case */
|
||||
read_instructions();
|
||||
|
||||
store_incoming(inbuffer, n, qbuf, sizeof(qbuf), &qlen, &qtype, &id);
|
||||
|
||||
set_advisor_read_lock(loglockfile);
|
||||
serverlogslocked = 1;
|
||||
|
||||
send_response(sock, &from.sa, fromlen, qbuf, qlen, qtype, id);
|
||||
|
||||
if(got_exit_signal)
|
||||
break;
|
||||
|
||||
if(serverlogslocked) {
|
||||
serverlogslocked = 0;
|
||||
clear_advisor_read_lock(loglockfile);
|
||||
}
|
||||
|
||||
/* logmsg("end of one transfer"); */
|
||||
}
|
||||
|
||||
dnsd_cleanup:
|
||||
|
||||
#if 0
|
||||
if((peer != sock) && (peer != CURL_SOCKET_BAD))
|
||||
sclose(peer);
|
||||
#endif
|
||||
|
||||
if(sock != CURL_SOCKET_BAD)
|
||||
sclose(sock);
|
||||
|
||||
if(got_exit_signal)
|
||||
logmsg("signalled to die");
|
||||
|
||||
if(dnsd_wrotepidfile)
|
||||
unlink(pidname);
|
||||
if(dnsd_wroteportfile)
|
||||
unlink(portname);
|
||||
|
||||
if(serverlogslocked) {
|
||||
serverlogslocked = 0;
|
||||
clear_advisor_read_lock(loglockfile);
|
||||
}
|
||||
|
||||
restore_signal_handlers(true);
|
||||
|
||||
if(got_exit_signal) {
|
||||
logmsg("========> %s dnsd (port: %d pid: %ld) exits with signal (%d)",
|
||||
ipv_inuse, (int)port, (long)our_getpid(), 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("========> dnsd quits");
|
||||
return result;
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 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.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.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
#include "first.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
entry_func_t entry_func;
|
||||
char *entry_name;
|
||||
size_t tmp;
|
||||
|
||||
if(argc < 2) {
|
||||
fprintf(stderr, "Pass servername as first argument\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
entry_name = argv[1];
|
||||
entry_func = NULL;
|
||||
for(tmp = 0; s_entries[tmp].ptr; ++tmp) {
|
||||
if(strcmp(entry_name, s_entries[tmp].name) == 0) {
|
||||
entry_func = s_entries[tmp].ptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!entry_func) {
|
||||
fprintf(stderr, "Test '%s' not found.\n", entry_name);
|
||||
return 99;
|
||||
}
|
||||
|
||||
return entry_func(argc - 1, argv + 1);
|
||||
}
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
#ifndef HEADER_SERVER_FIRST_H
|
||||
#define HEADER_SERVER_FIRST_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 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.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.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
/* Test servers simply are standalone programs that do not use libcurl
|
||||
* library. For convenience and to ease portability of these servers,
|
||||
* some source code files from the libcurl subdirectory are also used
|
||||
* to build the servers. In order to achieve proper linkage of these
|
||||
* files on Windows targets it is necessary to build the test servers
|
||||
* with CURL_STATICLIB defined, independently of how libcurl is built.
|
||||
* For other platforms, this macro is a no-op and safe to set.
|
||||
*/
|
||||
#define CURL_STATICLIB
|
||||
|
||||
#define WITHOUT_LIBCURL
|
||||
#define CURL_NO_OLDIES
|
||||
|
||||
#include "curl_setup.h"
|
||||
|
||||
typedef int (*entry_func_t)(int, char **);
|
||||
|
||||
struct entry_s {
|
||||
const char *name;
|
||||
entry_func_t ptr;
|
||||
};
|
||||
|
||||
extern const struct entry_s s_entries[];
|
||||
|
||||
#ifndef UNDER_CE
|
||||
#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
|
||||
|
||||
#include <curlx/curlx.h>
|
||||
|
||||
/* adjust for old MSVC */
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||
# define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
# define CURL_STRNICMP(p1, p2, n) _strnicmp(p1, p2, n)
|
||||
#elif defined(HAVE_STRCASECMP)
|
||||
# ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
# endif
|
||||
# define CURL_STRNICMP(p1, p2, n) strncasecmp(p1, p2, n)
|
||||
#elif defined(HAVE_STRCMPI)
|
||||
# define CURL_STRNICMP(p1, p2, n) strncmpi(p1, p2, n)
|
||||
#elif defined(HAVE_STRICMP)
|
||||
# define CURL_STRNICMP(p1, p2, n) strnicmp(p1, p2, n)
|
||||
#else
|
||||
# error "missing case insensitive comparison function"
|
||||
#endif
|
||||
|
||||
enum {
|
||||
DOCNUMBER_NOTHING = -7,
|
||||
DOCNUMBER_QUIT = -6,
|
||||
DOCNUMBER_BADCONNECT = -5,
|
||||
DOCNUMBER_INTERNAL = -4,
|
||||
DOCNUMBER_CONNECT = -3,
|
||||
DOCNUMBER_WERULEZ = -2,
|
||||
DOCNUMBER_404 = -1
|
||||
};
|
||||
|
||||
#include <curl/curl.h> /* for curl_socket_t */
|
||||
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
#include <sys/un.h> /* for sockaddr_un */
|
||||
#endif
|
||||
#endif /* USE_UNIX_SOCKETS */
|
||||
|
||||
typedef union {
|
||||
struct sockaddr sa;
|
||||
struct sockaddr_in sa4;
|
||||
#ifdef USE_IPV6
|
||||
struct sockaddr_in6 sa6;
|
||||
#endif
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
struct sockaddr_un sau;
|
||||
#endif
|
||||
} srvr_sockaddr_union_t;
|
||||
|
||||
/* getpart */
|
||||
#define GPE_NO_BUFFER_SPACE -2
|
||||
#define GPE_OUT_OF_MEMORY -1
|
||||
#define GPE_OK 0
|
||||
#define GPE_END_OF_FILE 1
|
||||
|
||||
extern int getpart(char **outbuf, size_t *outlen,
|
||||
const char *main, const char *sub, FILE *stream);
|
||||
|
||||
/* utility functions */
|
||||
extern char *data_to_hex(char *data, size_t len);
|
||||
extern void logmsg(const char *msg, ...);
|
||||
extern void loghex(unsigned char *buffer, ssize_t len);
|
||||
extern unsigned char byteval(char *value);
|
||||
extern int win32_init(void);
|
||||
extern FILE *test2fopen(long testno, const char *logdir2);
|
||||
extern curl_off_t our_getpid(void);
|
||||
extern int write_pidfile(const char *filename);
|
||||
extern int write_portfile(const char *filename, int port);
|
||||
extern void set_advisor_read_lock(const char *filename);
|
||||
extern void clear_advisor_read_lock(const char *filename);
|
||||
static volatile int got_exit_signal = 0;
|
||||
static volatile int exit_signal = 0;
|
||||
#ifdef _WIN32
|
||||
static HANDLE exit_event = NULL;
|
||||
#endif
|
||||
extern void install_signal_handlers(bool keep_sigalrm);
|
||||
extern void restore_signal_handlers(bool keep_sigalrm);
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
extern int bind_unix_socket(curl_socket_t sock, const char *unix_socket,
|
||||
struct sockaddr_un *sau);
|
||||
#endif
|
||||
extern curl_socket_t sockdaemon(curl_socket_t sock,
|
||||
unsigned short *listenport,
|
||||
const char *unix_socket,
|
||||
bool bind_only);
|
||||
|
||||
/* global variables */
|
||||
static const char *srcpath = "."; /* pointing to the test dir */
|
||||
static const char *pidname = NULL;
|
||||
static const char *portname = NULL; /* none by default */
|
||||
static const char *serverlogfile = NULL;
|
||||
static int serverlogslocked;
|
||||
static const char *configfile = NULL;
|
||||
static const char *logdir = "log";
|
||||
static char loglockfile[256];
|
||||
#ifdef USE_IPV6
|
||||
static bool use_ipv6 = FALSE;
|
||||
#endif
|
||||
static const char *ipv_inuse = "IPv4";
|
||||
static unsigned short server_port = 0;
|
||||
static const char *socket_type = "IPv4";
|
||||
static int socket_domain = AF_INET;
|
||||
|
||||
#define SERVERLOGS_LOCKDIR "lock" /* within logdir */
|
||||
|
||||
#endif /* HEADER_SERVER_FIRST_H */
|
||||
+449
@@ -0,0 +1,449 @@
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 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.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.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
#include "first.h"
|
||||
|
||||
#define EAT_SPACE(p) while(*(p) && ISSPACE(*(p))) (p)++
|
||||
|
||||
#define EAT_WORD(p) while(*(p) && !ISSPACE(*(p)) && ('>' != *(p))) (p)++
|
||||
|
||||
#ifdef DEBUG_GETPART
|
||||
#define show(x) printf x
|
||||
#else
|
||||
#define show(x) Curl_nop_stmt
|
||||
#endif
|
||||
|
||||
/*
|
||||
* line_length()
|
||||
*
|
||||
* Counts the number of characters in a line including a new line.
|
||||
* Unlike strlen() it does not stop at nul bytes.
|
||||
*
|
||||
*/
|
||||
static size_t line_length(const char *buffer, int bytestocheck)
|
||||
{
|
||||
size_t length = 1;
|
||||
|
||||
while(*buffer != '\n' && --bytestocheck) {
|
||||
length++;
|
||||
buffer++;
|
||||
}
|
||||
if(*buffer != '\n') {
|
||||
/*
|
||||
* We didn't find a new line so the last byte must be a
|
||||
* '\0' character inserted by fgets() which we should not
|
||||
* count.
|
||||
*/
|
||||
length--;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
/*
|
||||
* readline()
|
||||
*
|
||||
* Reads a complete line from a file into a dynamically allocated buffer.
|
||||
*
|
||||
* Calling function may call this multiple times with same 'buffer'
|
||||
* and 'bufsize' pointers to avoid multiple buffer allocations. Buffer
|
||||
* will be reallocated and 'bufsize' increased until whole line fits in
|
||||
* buffer before returning it.
|
||||
*
|
||||
* Calling function is responsible to free allocated buffer.
|
||||
*
|
||||
* This function may return:
|
||||
* GPE_OUT_OF_MEMORY
|
||||
* GPE_END_OF_FILE
|
||||
* GPE_OK
|
||||
*/
|
||||
static int readline(char **buffer, size_t *bufsize, size_t *length,
|
||||
FILE *stream)
|
||||
{
|
||||
size_t offset = 0;
|
||||
char *newptr;
|
||||
|
||||
if(!*buffer) {
|
||||
*buffer = calloc(1, 128);
|
||||
if(!*buffer)
|
||||
return GPE_OUT_OF_MEMORY;
|
||||
*bufsize = 128;
|
||||
}
|
||||
|
||||
for(;;) {
|
||||
int bytestoread = curlx_uztosi(*bufsize - offset);
|
||||
|
||||
if(!fgets(*buffer + offset, bytestoread, stream)) {
|
||||
*length = 0;
|
||||
return (offset != 0) ? GPE_OK : GPE_END_OF_FILE;
|
||||
}
|
||||
|
||||
*length = offset + line_length(*buffer + offset, bytestoread);
|
||||
if(*(*buffer + *length - 1) == '\n')
|
||||
break;
|
||||
offset = *length;
|
||||
if(*length < *bufsize - 1)
|
||||
continue;
|
||||
|
||||
newptr = realloc(*buffer, *bufsize * 2);
|
||||
if(!newptr)
|
||||
return GPE_OUT_OF_MEMORY;
|
||||
memset(&newptr[*bufsize], 0, *bufsize);
|
||||
*buffer = newptr;
|
||||
*bufsize *= 2;
|
||||
}
|
||||
|
||||
return GPE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* appenddata()
|
||||
*
|
||||
* This appends data from a given source buffer to the end of the used part of
|
||||
* a destination buffer. Arguments relative to the destination buffer are, the
|
||||
* address of a pointer to the destination buffer 'dst_buf', the length of data
|
||||
* in destination buffer excluding potential null string termination 'dst_len',
|
||||
* the allocated size of destination buffer 'dst_alloc'. All three destination
|
||||
* buffer arguments may be modified by this function. Arguments relative to the
|
||||
* source buffer are, a pointer to the source buffer 'src_buf' and indication
|
||||
* whether the source buffer is base64 encoded or not 'src_b64'.
|
||||
*
|
||||
* If the source buffer is indicated to be base64 encoded, this appends the
|
||||
* decoded data, binary or whatever, to the destination. The source buffer
|
||||
* may not hold binary data, only a null-terminated string is valid content.
|
||||
*
|
||||
* Destination buffer will be enlarged and relocated as needed.
|
||||
*
|
||||
* Calling function is responsible to provide preallocated destination
|
||||
* buffer and also to deallocate it when no longer needed.
|
||||
*
|
||||
* This function may return:
|
||||
* GPE_OUT_OF_MEMORY
|
||||
* GPE_OK
|
||||
*/
|
||||
static int appenddata(char **dst_buf, /* dest buffer */
|
||||
size_t *dst_len, /* dest buffer data length */
|
||||
size_t *dst_alloc, /* dest buffer allocated size */
|
||||
char *src_buf, /* source buffer */
|
||||
size_t src_len, /* source buffer length */
|
||||
int src_b64) /* != 0 if source is base64 encoded */
|
||||
{
|
||||
size_t need_alloc = 0;
|
||||
|
||||
if(!src_len)
|
||||
return GPE_OK;
|
||||
|
||||
need_alloc = src_len + *dst_len + 1;
|
||||
|
||||
if(src_b64) {
|
||||
if(src_buf[src_len - 1] == '\r')
|
||||
src_len--;
|
||||
|
||||
if(src_buf[src_len - 1] == '\n')
|
||||
src_len--;
|
||||
}
|
||||
|
||||
/* enlarge destination buffer if required */
|
||||
if(need_alloc > *dst_alloc) {
|
||||
size_t newsize = need_alloc * 2;
|
||||
char *newptr = realloc(*dst_buf, newsize);
|
||||
if(!newptr) {
|
||||
return GPE_OUT_OF_MEMORY;
|
||||
}
|
||||
*dst_alloc = newsize;
|
||||
*dst_buf = newptr;
|
||||
}
|
||||
|
||||
/* memcpy to support binary blobs */
|
||||
memcpy(*dst_buf + *dst_len, src_buf, src_len);
|
||||
*dst_len += src_len;
|
||||
*(*dst_buf + *dst_len) = '\0';
|
||||
|
||||
return GPE_OK;
|
||||
}
|
||||
|
||||
static int decodedata(char **buf, /* dest buffer */
|
||||
size_t *len) /* dest buffer data length */
|
||||
{
|
||||
CURLcode error = CURLE_OK;
|
||||
unsigned char *buf64 = NULL;
|
||||
size_t src_len = 0;
|
||||
|
||||
if(!*len)
|
||||
return GPE_OK;
|
||||
|
||||
/* base64 decode the given buffer */
|
||||
error = curlx_base64_decode(*buf, &buf64, &src_len);
|
||||
if(error)
|
||||
return GPE_OUT_OF_MEMORY;
|
||||
|
||||
if(!src_len) {
|
||||
/*
|
||||
** currently there is no way to tell apart an OOM condition in
|
||||
** curlx_base64_decode() from zero length decoded data. For now,
|
||||
** let's just assume it is an OOM condition, currently we have
|
||||
** no input for this function that decodes to zero length data.
|
||||
*/
|
||||
free(buf64);
|
||||
|
||||
return GPE_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
/* memcpy to support binary blobs */
|
||||
memcpy(*buf, buf64, src_len);
|
||||
*len = src_len;
|
||||
*(*buf + src_len) = '\0';
|
||||
|
||||
free(buf64);
|
||||
|
||||
return GPE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* getpart()
|
||||
*
|
||||
* This returns whole contents of specified XML-like section and subsection
|
||||
* from the given file. This is mostly used to retrieve a specific part from
|
||||
* a test definition file for consumption by test suite servers.
|
||||
*
|
||||
* Data is returned in a dynamically allocated buffer, a pointer to this data
|
||||
* and the size of the data is stored at the addresses that caller specifies.
|
||||
*
|
||||
* If the returned data is a string the returned size will be the length of
|
||||
* the string excluding null-termination. Otherwise it will just be the size
|
||||
* of the returned binary data.
|
||||
*
|
||||
* Calling function is responsible to free returned buffer.
|
||||
*
|
||||
* This function may return:
|
||||
* GPE_NO_BUFFER_SPACE
|
||||
* GPE_OUT_OF_MEMORY
|
||||
* GPE_OK
|
||||
*/
|
||||
int getpart(char **outbuf, size_t *outlen,
|
||||
const char *main, const char *sub, FILE *stream)
|
||||
{
|
||||
# define MAX_TAG_LEN 200
|
||||
char curouter[MAX_TAG_LEN + 1]; /* current outermost section */
|
||||
char curmain[MAX_TAG_LEN + 1]; /* current main section */
|
||||
char cursub[MAX_TAG_LEN + 1]; /* current sub section */
|
||||
char ptag[MAX_TAG_LEN + 1]; /* potential tag */
|
||||
char patt[MAX_TAG_LEN + 1]; /* potential attributes */
|
||||
char *buffer = NULL;
|
||||
char *ptr;
|
||||
char *end;
|
||||
union {
|
||||
ssize_t sig;
|
||||
size_t uns;
|
||||
} len;
|
||||
size_t bufsize = 0;
|
||||
size_t outalloc = 256;
|
||||
size_t datalen;
|
||||
int in_wanted_part = 0;
|
||||
int base64 = 0;
|
||||
int nonewline = 0;
|
||||
int error;
|
||||
|
||||
enum {
|
||||
STATE_OUTSIDE = 0,
|
||||
STATE_OUTER = 1,
|
||||
STATE_INMAIN = 2,
|
||||
STATE_INSUB = 3,
|
||||
STATE_ILLEGAL = 4
|
||||
} state = STATE_OUTSIDE;
|
||||
|
||||
*outlen = 0;
|
||||
*outbuf = malloc(outalloc);
|
||||
if(!*outbuf)
|
||||
return GPE_OUT_OF_MEMORY;
|
||||
*(*outbuf) = '\0';
|
||||
|
||||
curouter[0] = curmain[0] = cursub[0] = ptag[0] = patt[0] = '\0';
|
||||
|
||||
while((error = readline(&buffer, &bufsize, &datalen, stream)) == GPE_OK) {
|
||||
|
||||
ptr = buffer;
|
||||
EAT_SPACE(ptr);
|
||||
|
||||
if('<' != *ptr) {
|
||||
if(in_wanted_part) {
|
||||
show(("=> %s", buffer));
|
||||
error = appenddata(outbuf, outlen, &outalloc, buffer, datalen,
|
||||
base64);
|
||||
if(error)
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
ptr++;
|
||||
|
||||
if('/' == *ptr) {
|
||||
/*
|
||||
** closing section tag
|
||||
*/
|
||||
|
||||
ptr++;
|
||||
end = ptr;
|
||||
EAT_WORD(end);
|
||||
len.sig = end - ptr;
|
||||
if(len.sig > MAX_TAG_LEN) {
|
||||
error = GPE_NO_BUFFER_SPACE;
|
||||
break;
|
||||
}
|
||||
memcpy(ptag, ptr, len.uns);
|
||||
ptag[len.uns] = '\0';
|
||||
|
||||
if((STATE_INSUB == state) && !strcmp(cursub, ptag)) {
|
||||
/* end of current sub section */
|
||||
state = STATE_INMAIN;
|
||||
cursub[0] = '\0';
|
||||
if(in_wanted_part) {
|
||||
/* Do we need to base64 decode the data? */
|
||||
if(base64) {
|
||||
error = decodedata(outbuf, outlen);
|
||||
if(error)
|
||||
return error;
|
||||
}
|
||||
if(nonewline)
|
||||
(*outlen)--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if((STATE_INMAIN == state) && !strcmp(curmain, ptag)) {
|
||||
/* end of current main section */
|
||||
state = STATE_OUTER;
|
||||
curmain[0] = '\0';
|
||||
if(in_wanted_part) {
|
||||
/* Do we need to base64 decode the data? */
|
||||
if(base64) {
|
||||
error = decodedata(outbuf, outlen);
|
||||
if(error)
|
||||
return error;
|
||||
}
|
||||
if(nonewline)
|
||||
(*outlen)--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if((STATE_OUTER == state) && !strcmp(curouter, ptag)) {
|
||||
/* end of outermost file section */
|
||||
state = STATE_OUTSIDE;
|
||||
curouter[0] = '\0';
|
||||
if(in_wanted_part)
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
else if(!in_wanted_part) {
|
||||
/*
|
||||
** opening section tag
|
||||
*/
|
||||
|
||||
/* get potential tag */
|
||||
end = ptr;
|
||||
EAT_WORD(end);
|
||||
len.sig = end - ptr;
|
||||
if(len.sig > MAX_TAG_LEN) {
|
||||
error = GPE_NO_BUFFER_SPACE;
|
||||
break;
|
||||
}
|
||||
memcpy(ptag, ptr, len.uns);
|
||||
ptag[len.uns] = '\0';
|
||||
|
||||
/* ignore comments, doctypes and xml declarations */
|
||||
if(('!' == ptag[0]) || ('?' == ptag[0])) {
|
||||
show(("* ignoring (%s)", buffer));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* get all potential attributes */
|
||||
ptr = end;
|
||||
EAT_SPACE(ptr);
|
||||
end = ptr;
|
||||
while(*end && ('>' != *end))
|
||||
end++;
|
||||
len.sig = end - ptr;
|
||||
if(len.sig > MAX_TAG_LEN) {
|
||||
error = GPE_NO_BUFFER_SPACE;
|
||||
break;
|
||||
}
|
||||
memcpy(patt, ptr, len.uns);
|
||||
patt[len.uns] = '\0';
|
||||
|
||||
if(STATE_OUTSIDE == state) {
|
||||
/* outermost element (<testcase>) */
|
||||
strcpy(curouter, ptag);
|
||||
state = STATE_OUTER;
|
||||
continue;
|
||||
}
|
||||
else if(STATE_OUTER == state) {
|
||||
/* start of a main section */
|
||||
strcpy(curmain, ptag);
|
||||
state = STATE_INMAIN;
|
||||
continue;
|
||||
}
|
||||
else if(STATE_INMAIN == state) {
|
||||
/* start of a sub section */
|
||||
strcpy(cursub, ptag);
|
||||
state = STATE_INSUB;
|
||||
if(!strcmp(curmain, main) && !strcmp(cursub, sub)) {
|
||||
/* start of wanted part */
|
||||
in_wanted_part = 1;
|
||||
if(strstr(patt, "base64="))
|
||||
/* bit rough test, but "mostly" functional, */
|
||||
/* treat wanted part data as base64 encoded */
|
||||
base64 = 1;
|
||||
if(strstr(patt, "nonewline=")) {
|
||||
show(("* setting nonewline\n"));
|
||||
nonewline = 1;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(in_wanted_part) {
|
||||
show(("=> %s", buffer));
|
||||
error = appenddata(outbuf, outlen, &outalloc, buffer, datalen, base64);
|
||||
if(error)
|
||||
break;
|
||||
}
|
||||
|
||||
} /* while */
|
||||
|
||||
free(buffer);
|
||||
|
||||
if(error != GPE_OK) {
|
||||
if(error == GPE_END_OF_FILE)
|
||||
error = GPE_OK;
|
||||
else {
|
||||
free(*outbuf);
|
||||
*outbuf = NULL;
|
||||
*outlen = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
+891
@@ -0,0 +1,891 @@
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 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.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.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
#include "first.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.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.
|
||||
*
|
||||
*/
|
||||
|
||||
/* based on sockfilt.c */
|
||||
|
||||
#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
|
||||
|
||||
struct mqttd_configurable {
|
||||
unsigned char version; /* initial version byte in the request must match
|
||||
this */
|
||||
bool publish_before_suback;
|
||||
bool short_publish;
|
||||
bool excessive_remaining;
|
||||
unsigned char error_connack;
|
||||
int testnum;
|
||||
};
|
||||
|
||||
#define REQUEST_DUMP "server.input"
|
||||
#define CONFIG_VERSION 5
|
||||
|
||||
static struct mqttd_configurable m_config;
|
||||
|
||||
static void mqttd_resetdefaults(void)
|
||||
{
|
||||
logmsg("Reset to defaults");
|
||||
m_config.version = CONFIG_VERSION;
|
||||
m_config.publish_before_suback = FALSE;
|
||||
m_config.short_publish = FALSE;
|
||||
m_config.excessive_remaining = FALSE;
|
||||
m_config.error_connack = 0;
|
||||
m_config.testnum = 0;
|
||||
}
|
||||
|
||||
static void mqttd_getconfig(void)
|
||||
{
|
||||
FILE *fp = fopen(configfile, FOPEN_READTEXT);
|
||||
mqttd_resetdefaults();
|
||||
if(fp) {
|
||||
char buffer[512];
|
||||
logmsg("parse config file");
|
||||
while(fgets(buffer, sizeof(buffer), fp)) {
|
||||
char key[32];
|
||||
char value[32];
|
||||
if(sscanf(buffer, "%31s %31s", key, value) == 2) {
|
||||
if(!strcmp(key, "version")) {
|
||||
m_config.version = byteval(value);
|
||||
logmsg("version [%d] set", m_config.version);
|
||||
}
|
||||
else if(!strcmp(key, "PUBLISH-before-SUBACK")) {
|
||||
logmsg("PUBLISH-before-SUBACK set");
|
||||
m_config.publish_before_suback = TRUE;
|
||||
}
|
||||
else if(!strcmp(key, "short-PUBLISH")) {
|
||||
logmsg("short-PUBLISH set");
|
||||
m_config.short_publish = TRUE;
|
||||
}
|
||||
else if(!strcmp(key, "error-CONNACK")) {
|
||||
m_config.error_connack = byteval(value);
|
||||
logmsg("error-CONNACK = %d", m_config.error_connack);
|
||||
}
|
||||
else if(!strcmp(key, "Testnum")) {
|
||||
m_config.testnum = atoi(value);
|
||||
logmsg("testnum = %d", m_config.testnum);
|
||||
}
|
||||
else if(!strcmp(key, "excessive-remaining")) {
|
||||
logmsg("excessive-remaining set");
|
||||
m_config.excessive_remaining = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
else {
|
||||
logmsg("No config file '%s' to read", configfile);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
int left = sizeof(data);
|
||||
|
||||
for(i = 0; i < len && (left >= 0); i++) {
|
||||
snprintf(optr, left, "%02x", ptr[i]);
|
||||
optr += 2;
|
||||
left -= 2;
|
||||
}
|
||||
fprintf(output, "%s %s %x %s\n",
|
||||
dir == FROM_CLIENT ? "client" : "server",
|
||||
prefix, (int)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] = m_config.error_connack;
|
||||
|
||||
rc = swrite(fd, (char *)packet, sizeof(packet));
|
||||
if(rc > 0) {
|
||||
logmsg("WROTE %zd 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 %zd 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 %zd 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 %zd 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 size_t encode_length(size_t packetlen,
|
||||
unsigned char *remlength) /* 4 bytes */
|
||||
{
|
||||
size_t 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 *buffer,
|
||||
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 = buffer[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, const char *payload, size_t payloadlen)
|
||||
{
|
||||
size_t topiclen = strlen(topic);
|
||||
unsigned char *packet;
|
||||
size_t payloadindex;
|
||||
size_t remaininglength = topiclen + 2 + payloadlen;
|
||||
size_t packetlen;
|
||||
size_t sendamount;
|
||||
ssize_t rc;
|
||||
unsigned char rembuffer[4];
|
||||
size_t encodedlen;
|
||||
|
||||
if(m_config.excessive_remaining) {
|
||||
/* manually set illegal remaining length */
|
||||
rembuffer[0] = 0xff;
|
||||
rembuffer[1] = 0xff;
|
||||
rembuffer[2] = 0xff;
|
||||
rembuffer[3] = 0x80; /* maximum allowed here by spec is 0x7f */
|
||||
encodedlen = 4;
|
||||
}
|
||||
else
|
||||
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;
|
||||
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(m_config.short_publish)
|
||||
sendamount -= 2;
|
||||
|
||||
rc = swrite(fd, (char *)packet, sendamount);
|
||||
if(rc > 0) {
|
||||
logmsg("WROTE %zd bytes [PUBLISH]", rc);
|
||||
loghex(packet, rc);
|
||||
logprotocol(FROM_SERVER, "PUBLISH", remaininglength, dump, packet, rc);
|
||||
}
|
||||
free(packet);
|
||||
if((size_t)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);
|
||||
size_t i;
|
||||
if(rc < 2) {
|
||||
logmsg("READ %zd bytes [SHORT!]", rc);
|
||||
return 1; /* fail */
|
||||
}
|
||||
logmsg("READ %zd 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: %zu [%zu bytes]", *remaining_lengthp,
|
||||
*remaining_length_bytesp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static curl_socket_t mqttit(curl_socket_t fd)
|
||||
{
|
||||
size_t buff_size = 10*1024;
|
||||
unsigned char *buffer = NULL;
|
||||
ssize_t rc;
|
||||
unsigned char byte;
|
||||
unsigned short packet_id;
|
||||
size_t payload_len;
|
||||
size_t client_id_length;
|
||||
size_t 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;
|
||||
FILE *stream = NULL;
|
||||
FILE *dump;
|
||||
char dumpfile[256];
|
||||
|
||||
static const char protocol[7] = {
|
||||
0x00, 0x04, /* protocol length */
|
||||
'M','Q','T','T', /* protocol name */
|
||||
0x04 /* protocol level */
|
||||
};
|
||||
snprintf(dumpfile, sizeof(dumpfile), "%s/%s", logdir, REQUEST_DUMP);
|
||||
dump = fopen(dumpfile, "ab");
|
||||
if(!dump)
|
||||
goto end;
|
||||
|
||||
mqttd_getconfig();
|
||||
|
||||
testno = m_config.testnum;
|
||||
|
||||
if(testno)
|
||||
logmsg("Found test number %ld", testno);
|
||||
|
||||
buffer = malloc(buff_size);
|
||||
if(!buffer) {
|
||||
logmsg("Out of memory, unable to allocate buffer");
|
||||
goto end;
|
||||
}
|
||||
memset(buffer, 0, buff_size);
|
||||
|
||||
do {
|
||||
unsigned char usr_flag = 0x80;
|
||||
unsigned char passwd_flag = 0x40;
|
||||
unsigned char conn_flags;
|
||||
const size_t client_id_offset = 12;
|
||||
size_t start_usr;
|
||||
size_t start_passwd;
|
||||
|
||||
/* get the fixed header */
|
||||
rc = fixedheader(fd, &byte, &remaining_length, &bytes);
|
||||
if(rc)
|
||||
break;
|
||||
|
||||
if(remaining_length >= buff_size) {
|
||||
unsigned char *newbuffer;
|
||||
buff_size = remaining_length;
|
||||
newbuffer = realloc(buffer, buff_size);
|
||||
if(!newbuffer) {
|
||||
logmsg("Failed realloc of size %zu", buff_size);
|
||||
goto end;
|
||||
}
|
||||
buffer = newbuffer;
|
||||
}
|
||||
|
||||
if(remaining_length) {
|
||||
/* reading variable header and payload into buffer */
|
||||
rc = sread(fd, (char *)buffer, remaining_length);
|
||||
if(rc > 0) {
|
||||
logmsg("READ %zd 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 = (size_t)(buffer[10] << 8) | buffer[11];
|
||||
/* first part of the payload is the client ID */
|
||||
client_id_length = payload_len;
|
||||
|
||||
/* checking if user and password flags were set */
|
||||
conn_flags = buffer[7];
|
||||
|
||||
start_usr = client_id_offset + payload_len;
|
||||
if(usr_flag == (unsigned char)(conn_flags & usr_flag)) {
|
||||
logmsg("User flag is present in CONN flag");
|
||||
payload_len += (size_t)(buffer[start_usr] << 8) |
|
||||
buffer[start_usr + 1];
|
||||
payload_len += 2; /* MSB and LSB for user length */
|
||||
}
|
||||
|
||||
start_passwd = client_id_offset + payload_len;
|
||||
if(passwd_flag == (char)(conn_flags & passwd_flag)) {
|
||||
logmsg("Password flag is present in CONN flags");
|
||||
payload_len += (size_t)(buffer[start_passwd] << 8) |
|
||||
buffer[start_passwd + 1];
|
||||
payload_len += 2; /* MSB and LSB for password length */
|
||||
}
|
||||
|
||||
/* check the length of the payload */
|
||||
if((ssize_t)payload_len != (rc - 12)) {
|
||||
logmsg("Payload length mismatch, expected %zx got %zx",
|
||||
rc - 12, payload_len);
|
||||
goto end;
|
||||
}
|
||||
/* check the length of the client ID */
|
||||
else if((client_id_length + 1) > MAX_CLIENT_ID_LENGTH) {
|
||||
logmsg("Too large client id");
|
||||
goto end;
|
||||
}
|
||||
memcpy(client_id, &buffer[12], client_id_length);
|
||||
client_id[client_id_length] = 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) {
|
||||
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 = (size_t)(buffer[2] << 8) | buffer[3];
|
||||
if(topic_len != (remaining_length - 5)) {
|
||||
logmsg("Wrong topic length, got %zu expected %zu",
|
||||
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, logdir);
|
||||
if(!stream) {
|
||||
char errbuf[STRERROR_LEN];
|
||||
error = errno;
|
||||
logmsg("fopen() failed with error (%d) %s",
|
||||
error, curlx_strerror(error, errbuf, sizeof(errbuf)));
|
||||
logmsg("Couldn't open test file %ld", testno);
|
||||
goto end;
|
||||
}
|
||||
error = getpart(&data, &datalen, "reply", "data", stream);
|
||||
if(!error) {
|
||||
if(!m_config.publish_before_suback) {
|
||||
if(suback(dump, fd, packet_id)) {
|
||||
logmsg("failed sending SUBACK");
|
||||
free(data);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
if(publish(dump, fd, packet_id, topic, data, datalen)) {
|
||||
logmsg("PUBLISH failed");
|
||||
free(data);
|
||||
goto end;
|
||||
}
|
||||
free(data);
|
||||
if(m_config.publish_before_suback) {
|
||||
if(suback(dump, fd, packet_id)) {
|
||||
logmsg("failed sending SUBACK");
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
const char *def = "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 = (size_t)(buffer[1 + bytes] << 8) | buffer[2 + bytes];
|
||||
logmsg("Got %zu bytes topic", topiclen);
|
||||
|
||||
#ifdef QOS
|
||||
/* 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 %zd 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:
|
||||
if(buffer)
|
||||
free(buffer);
|
||||
if(dump)
|
||||
fclose(dump);
|
||||
if(stream)
|
||||
fclose(stream);
|
||||
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 mqttd_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;
|
||||
char errbuf[STRERROR_LEN];
|
||||
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 */
|
||||
#ifdef __DJGPP__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Warith-conversion"
|
||||
#endif
|
||||
FD_SET(sockfd, &fds_read);
|
||||
#ifdef __DJGPP__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
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) == SOCKEINTR));
|
||||
|
||||
if(rc < 0) {
|
||||
logmsg("select() failed with error (%d) %s",
|
||||
error, curlx_strerror(error, errbuf, sizeof(errbuf)));
|
||||
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() failed with error (%d) %s",
|
||||
error, curlx_strerror(error, errbuf, sizeof(errbuf)));
|
||||
}
|
||||
else {
|
||||
logmsg("====> Client connect, fd %ld. "
|
||||
"Read config from %s", (long)newfd, configfile);
|
||||
set_advisor_read_lock(loglockfile);
|
||||
(void)mqttit(newfd); /* until done */
|
||||
clear_advisor_read_lock(loglockfile);
|
||||
|
||||
logmsg("====> Client disconnect");
|
||||
sclose(newfd);
|
||||
}
|
||||
}
|
||||
} while(clients);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int test_mqttd(int argc, char *argv[])
|
||||
{
|
||||
curl_socket_t sock = CURL_SOCKET_BAD;
|
||||
curl_socket_t msgsock = CURL_SOCKET_BAD;
|
||||
int wrotepidfile = 0;
|
||||
int wroteportfile = 0;
|
||||
bool juggle_again;
|
||||
int error;
|
||||
char errbuf[STRERROR_LEN];
|
||||
int arg = 1;
|
||||
|
||||
pidname = ".mqttd.pid";
|
||||
portname = ".mqttd.port";
|
||||
serverlogfile = "log/mqttd.log";
|
||||
configfile = "mqttd.config";
|
||||
server_port = 1883; /* MQTT default port */
|
||||
|
||||
while(argc > arg) {
|
||||
if(!strcmp("--version", argv[arg])) {
|
||||
printf("mqttd IPv4%s\n",
|
||||
#ifdef USE_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("--logdir", argv[arg])) {
|
||||
arg++;
|
||||
if(argc > arg)
|
||||
logdir = argv[arg++];
|
||||
}
|
||||
else if(!strcmp("--ipv6", argv[arg])) {
|
||||
#ifdef USE_IPV6
|
||||
socket_domain = AF_INET6;
|
||||
ipv_inuse = "IPv6";
|
||||
#endif
|
||||
arg++;
|
||||
}
|
||||
else if(!strcmp("--ipv4", argv[arg])) {
|
||||
/* for completeness, we support this option as well */
|
||||
#ifdef USE_IPV6
|
||||
socket_domain = AF_INET;
|
||||
ipv_inuse = "IPv4";
|
||||
#endif
|
||||
arg++;
|
||||
}
|
||||
else if(!strcmp("--port", argv[arg])) {
|
||||
arg++;
|
||||
if(argc > arg) {
|
||||
int inum = atoi(argv[arg]);
|
||||
if(inum && ((inum < 1025) || (inum > 65535))) {
|
||||
fprintf(stderr, "mqttd: invalid --port argument (%s)\n",
|
||||
argv[arg]);
|
||||
return 0;
|
||||
}
|
||||
server_port = (unsigned short)inum;
|
||||
arg++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
puts("Usage: mqttd [option]\n"
|
||||
" --config [file]\n"
|
||||
" --version\n"
|
||||
" --logfile [file]\n"
|
||||
" --logdir [directory]\n"
|
||||
" --pidfile [file]\n"
|
||||
" --portfile [file]\n"
|
||||
" --ipv4\n"
|
||||
" --ipv6\n"
|
||||
" --port [port]\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(loglockfile, sizeof(loglockfile), "%s/%s/mqtt-%s.lock",
|
||||
logdir, SERVERLOGS_LOCKDIR, ipv_inuse);
|
||||
|
||||
#ifdef _WIN32
|
||||
if(win32_init())
|
||||
return 2;
|
||||
#endif
|
||||
|
||||
CURLX_SET_BINMODE(stdin);
|
||||
CURLX_SET_BINMODE(stdout);
|
||||
CURLX_SET_BINMODE(stderr);
|
||||
|
||||
install_signal_handlers(FALSE);
|
||||
|
||||
sock = socket(socket_domain, SOCK_STREAM, 0);
|
||||
|
||||
if(CURL_SOCKET_BAD == sock) {
|
||||
error = SOCKERRNO;
|
||||
logmsg("Error creating socket (%d) %s",
|
||||
error, curlx_strerror(error, errbuf, sizeof(errbuf)));
|
||||
goto mqttd_cleanup;
|
||||
}
|
||||
|
||||
{
|
||||
/* passive daemon style */
|
||||
sock = sockdaemon(sock, &server_port, NULL, FALSE);
|
||||
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", server_port);
|
||||
|
||||
wrotepidfile = write_pidfile(pidname);
|
||||
if(!wrotepidfile) {
|
||||
goto mqttd_cleanup;
|
||||
}
|
||||
|
||||
wroteportfile = write_portfile(portname, server_port);
|
||||
if(!wroteportfile) {
|
||||
goto mqttd_cleanup;
|
||||
}
|
||||
|
||||
do {
|
||||
juggle_again = mqttd_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);
|
||||
if(wroteportfile)
|
||||
unlink(portname);
|
||||
|
||||
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;
|
||||
}
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 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.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.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
#include "first.h"
|
||||
|
||||
/* Purpose
|
||||
*
|
||||
* Resolve the given name, using system name resolve functions (NOT any
|
||||
* function provided by libcurl). Used to see if the name exists and thus if
|
||||
* we can allow a test case to use it for testing.
|
||||
*
|
||||
* Like if 'localhost' actual exists etc.
|
||||
*
|
||||
*/
|
||||
|
||||
static int test_resolve(int argc, char *argv[])
|
||||
{
|
||||
int arg = 1;
|
||||
const char *host = NULL;
|
||||
int rc = 0;
|
||||
|
||||
while(argc > arg) {
|
||||
if(!strcmp("--version", argv[arg])) {
|
||||
printf("resolve IPv4%s\n",
|
||||
#ifdef CURLRES_IPV6
|
||||
"/IPv6"
|
||||
#else
|
||||
""
|
||||
#endif
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
else if(!strcmp("--ipv6", argv[arg])) {
|
||||
#ifdef CURLRES_IPV6
|
||||
ipv_inuse = "IPv6";
|
||||
use_ipv6 = TRUE;
|
||||
arg++;
|
||||
#else
|
||||
puts("IPv6 support has been disabled in this program");
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
else if(!strcmp("--ipv4", argv[arg])) {
|
||||
/* for completeness, we support this option as well */
|
||||
ipv_inuse = "IPv4";
|
||||
#ifdef CURLRES_IPV6
|
||||
use_ipv6 = FALSE;
|
||||
#endif
|
||||
arg++;
|
||||
}
|
||||
else {
|
||||
host = argv[arg++];
|
||||
}
|
||||
}
|
||||
if(!host) {
|
||||
puts("Usage: resolve [option] <host>\n"
|
||||
" --version\n"
|
||||
" --ipv4"
|
||||
#ifdef CURLRES_IPV6
|
||||
"\n --ipv6"
|
||||
#endif
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if(win32_init())
|
||||
return 2;
|
||||
#endif
|
||||
|
||||
#ifdef CURLRES_IPV6
|
||||
if(use_ipv6) {
|
||||
/* Check that the system has IPv6 enabled before checking the resolver */
|
||||
curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0);
|
||||
if(s == CURL_SOCKET_BAD)
|
||||
/* an IPv6 address was requested and we can't get/use one */
|
||||
rc = -1;
|
||||
else {
|
||||
sclose(s);
|
||||
}
|
||||
}
|
||||
|
||||
if(rc == 0) {
|
||||
/* getaddrinfo() resolve */
|
||||
struct addrinfo *ai;
|
||||
struct addrinfo hints;
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = use_ipv6 ? PF_INET6 : PF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = 0;
|
||||
rc = getaddrinfo(host, "80", &hints, &ai);
|
||||
if(rc == 0)
|
||||
freeaddrinfo(ai);
|
||||
}
|
||||
#else
|
||||
{
|
||||
struct hostent *he; /* gethostbyname() resolve */
|
||||
|
||||
#ifdef __AMIGA__
|
||||
he = gethostbyname((unsigned char *)CURL_UNCONST(host));
|
||||
#else
|
||||
he = gethostbyname(host);
|
||||
#endif
|
||||
|
||||
rc = !he;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(rc)
|
||||
printf("Resolving %s '%s' didn't work\n", ipv_inuse, host);
|
||||
|
||||
return !!rc;
|
||||
}
|
||||
+1345
File diff suppressed because it is too large
Load Diff
+42
@@ -0,0 +1,42 @@
|
||||
/* !checksrc! disable COPYRIGHT all */
|
||||
|
||||
#include "first.h"
|
||||
|
||||
#include "getpart.c"
|
||||
#include "util.c"
|
||||
#include "../../lib/curlx/base64.c"
|
||||
#include "../../lib/curlx/fopen.c"
|
||||
#include "../../lib/curlx/inet_ntop.c"
|
||||
#include "../../lib/curlx/inet_pton.c"
|
||||
#include "../../lib/curlx/multibyte.c"
|
||||
#include "../../lib/curlx/nonblock.c"
|
||||
#include "../../lib/curlx/strerr.c"
|
||||
#include "../../lib/curlx/strparse.c"
|
||||
#include "../../lib/curlx/timediff.c"
|
||||
#include "../../lib/curlx/timeval.c"
|
||||
#include "../../lib/curlx/version_win32.c"
|
||||
#include "../../lib/curlx/wait.c"
|
||||
#include "../../lib/curlx/warnless.c"
|
||||
#include "../../lib/curlx/winapi.c"
|
||||
#include "dnsd.c"
|
||||
#include "mqttd.c"
|
||||
#include "resolve.c"
|
||||
#include "rtspd.c"
|
||||
#include "sockfilt.c"
|
||||
#include "socksd.c"
|
||||
#include "sws.c"
|
||||
#include "tftpd.c"
|
||||
|
||||
const struct entry_s s_entries[] = {
|
||||
{"dnsd", test_dnsd},
|
||||
{"mqttd", test_mqttd},
|
||||
{"resolve", test_resolve},
|
||||
{"rtspd", test_rtspd},
|
||||
{"sockfilt", test_sockfilt},
|
||||
{"socksd", test_socksd},
|
||||
{"sws", test_sws},
|
||||
{"tftpd", test_tftpd},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
#include "first.c"
|
||||
+1462
File diff suppressed because it is too large
Load Diff
+951
@@ -0,0 +1,951 @@
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 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.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.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
#include "first.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Function
|
||||
*
|
||||
* Accepts a TCP connection on a custom port (IPv4 or IPv6). Connects to a
|
||||
* given addr + port backend (that is NOT extracted form the client's
|
||||
* request). The backend server default to connect to can be set with
|
||||
* --backend and --backendport.
|
||||
*
|
||||
* 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:
|
||||
*
|
||||
* "version [number: 5]" - requires the communication to use this version.
|
||||
* "nmethods_min [number: 1]" - the minimum numberf NMETHODS the client must
|
||||
* state
|
||||
* "nmethods_max [number: 3]" - the minimum numberf NMETHODS the client must
|
||||
* state
|
||||
* "user [string]" - the user name that must match (if method is 2)
|
||||
* "password [string]" - the password that must match (if method is 2)
|
||||
* "backend [IPv4]" - numerical IPv4 address of backend to connect to
|
||||
* "backendport [number:0]" - TCP port of backend to connect to. 0 means use
|
||||
the client's specified port number.
|
||||
* "method [number: 0]" - connect method to respond with:
|
||||
* 0 - no auth
|
||||
* 1 - GSSAPI (not supported)
|
||||
* 2 - user + password
|
||||
* "response [number]" - the decimal number to respond to a connect
|
||||
* SOCKS5: 0 is OK, SOCKS4: 90 is ok
|
||||
*
|
||||
*/
|
||||
|
||||
/* based on sockfilt.c */
|
||||
|
||||
static const char *backendaddr = "127.0.0.1";
|
||||
static unsigned short backendport = 0; /* default is use client's */
|
||||
|
||||
struct socksd_configurable {
|
||||
unsigned char version; /* initial version byte in the request must match
|
||||
this */
|
||||
unsigned char nmethods_min; /* minimum number of nmethods to expect */
|
||||
unsigned char nmethods_max; /* maximum number of nmethods to expect */
|
||||
unsigned char responseversion;
|
||||
unsigned char responsemethod;
|
||||
unsigned char reqcmd;
|
||||
unsigned char connectrep;
|
||||
unsigned short port; /* backend port */
|
||||
char addr[32]; /* backend IPv4 numerical */
|
||||
char user[256];
|
||||
char password[256];
|
||||
};
|
||||
|
||||
#define CONFIG_VERSION 5
|
||||
#define CONFIG_NMETHODS_MIN 1 /* unauth, gssapi, auth */
|
||||
#define CONFIG_NMETHODS_MAX 3
|
||||
#define CONFIG_RESPONSEVERSION CONFIG_VERSION
|
||||
#define CONFIG_RESPONSEMETHOD 0 /* no auth */
|
||||
#define CONFIG_REQCMD 1 /* CONNECT */
|
||||
#define CONFIG_PORT backendport
|
||||
#define CONFIG_ADDR backendaddr
|
||||
#define CONFIG_CONNECTREP 0
|
||||
|
||||
static struct socksd_configurable s_config;
|
||||
|
||||
static const char *reqlogfile = "log/socksd-request.log";
|
||||
|
||||
static void socksd_resetdefaults(void)
|
||||
{
|
||||
logmsg("Reset to defaults");
|
||||
s_config.version = CONFIG_VERSION;
|
||||
s_config.nmethods_min = CONFIG_NMETHODS_MIN;
|
||||
s_config.nmethods_max = CONFIG_NMETHODS_MAX;
|
||||
s_config.responseversion = CONFIG_RESPONSEVERSION;
|
||||
s_config.responsemethod = CONFIG_RESPONSEMETHOD;
|
||||
s_config.reqcmd = CONFIG_REQCMD;
|
||||
s_config.connectrep = CONFIG_CONNECTREP;
|
||||
s_config.port = CONFIG_PORT;
|
||||
strcpy(s_config.addr, CONFIG_ADDR);
|
||||
strcpy(s_config.user, "user");
|
||||
strcpy(s_config.password, "password");
|
||||
}
|
||||
|
||||
static unsigned short shortval(char *value)
|
||||
{
|
||||
unsigned long num = (unsigned long)atol(value);
|
||||
return num & 0xffff;
|
||||
}
|
||||
|
||||
static void socksd_getconfig(void)
|
||||
{
|
||||
FILE *fp = fopen(configfile, FOPEN_READTEXT);
|
||||
socksd_resetdefaults();
|
||||
if(fp) {
|
||||
char buffer[512];
|
||||
logmsg("parse config file");
|
||||
while(fgets(buffer, sizeof(buffer), fp)) {
|
||||
char key[32];
|
||||
char value[260];
|
||||
if(sscanf(buffer, "%31s %259s", key, value) == 2) {
|
||||
if(!strcmp(key, "version")) {
|
||||
s_config.version = byteval(value);
|
||||
logmsg("version [%d] set", s_config.version);
|
||||
}
|
||||
else if(!strcmp(key, "nmethods_min")) {
|
||||
s_config.nmethods_min = byteval(value);
|
||||
logmsg("nmethods_min [%d] set", s_config.nmethods_min);
|
||||
}
|
||||
else if(!strcmp(key, "nmethods_max")) {
|
||||
s_config.nmethods_max = byteval(value);
|
||||
logmsg("nmethods_max [%d] set", s_config.nmethods_max);
|
||||
}
|
||||
else if(!strcmp(key, "backend")) {
|
||||
strcpy(s_config.addr, value);
|
||||
logmsg("backend [%s] set", s_config.addr);
|
||||
}
|
||||
else if(!strcmp(key, "backendport")) {
|
||||
s_config.port = shortval(value);
|
||||
logmsg("backendport [%d] set", s_config.port);
|
||||
}
|
||||
else if(!strcmp(key, "user")) {
|
||||
strcpy(s_config.user, value);
|
||||
logmsg("user [%s] set", s_config.user);
|
||||
}
|
||||
else if(!strcmp(key, "password")) {
|
||||
strcpy(s_config.password, value);
|
||||
logmsg("password [%s] set", s_config.password);
|
||||
}
|
||||
/* Methods:
|
||||
o X'00' NO AUTHENTICATION REQUIRED
|
||||
o X'01' GSSAPI
|
||||
o X'02' USERNAME/PASSWORD
|
||||
*/
|
||||
else if(!strcmp(key, "method")) {
|
||||
s_config.responsemethod = byteval(value);
|
||||
logmsg("method [%d] set", s_config.responsemethod);
|
||||
}
|
||||
else if(!strcmp(key, "response")) {
|
||||
s_config.connectrep = byteval(value);
|
||||
logmsg("response [%d] set", s_config.connectrep);
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
/* RFC 1928, SOCKS5 byte index */
|
||||
#define SOCKS5_VERSION 0
|
||||
#define SOCKS5_NMETHODS 1 /* number of methods that is listed */
|
||||
|
||||
/* in the request: */
|
||||
#define SOCKS5_REQCMD 1
|
||||
#define SOCKS5_RESERVED 2
|
||||
#define SOCKS5_ATYP 3
|
||||
#define SOCKS5_DSTADDR 4
|
||||
|
||||
/* connect response */
|
||||
#define SOCKS5_REP 1
|
||||
#define SOCKS5_BNDADDR 4
|
||||
|
||||
/* auth request */
|
||||
#define SOCKS5_ULEN 1
|
||||
#define SOCKS5_UNAME 2
|
||||
|
||||
#define SOCKS4_CD 1
|
||||
#define SOCKS4_DSTPORT 2
|
||||
|
||||
/* connect to a given IPv4 address, not the one asked for */
|
||||
static curl_socket_t socksconnect(unsigned short connectport,
|
||||
const char *connectaddr)
|
||||
{
|
||||
int rc;
|
||||
srvr_sockaddr_union_t me;
|
||||
curl_socket_t sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if(sock == CURL_SOCKET_BAD)
|
||||
return CURL_SOCKET_BAD;
|
||||
memset(&me.sa4, 0, sizeof(me.sa4));
|
||||
me.sa4.sin_family = AF_INET;
|
||||
me.sa4.sin_port = htons(connectport);
|
||||
me.sa4.sin_addr.s_addr = INADDR_ANY;
|
||||
curlx_inet_pton(AF_INET, connectaddr, &me.sa4.sin_addr);
|
||||
|
||||
rc = connect(sock, &me.sa, sizeof(me.sa4));
|
||||
|
||||
if(rc) {
|
||||
char errbuf[STRERROR_LEN];
|
||||
int error = SOCKERRNO;
|
||||
logmsg("Failed connecting to %s:%hu (%d) %s", connectaddr, connectport,
|
||||
error, curlx_strerror(error, errbuf, sizeof(errbuf)));
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
logmsg("Connected fine to %s:%d", connectaddr, connectport);
|
||||
return sock;
|
||||
}
|
||||
|
||||
static curl_socket_t socks4(curl_socket_t fd,
|
||||
unsigned char *buffer,
|
||||
ssize_t rc)
|
||||
{
|
||||
unsigned char response[256 + 16];
|
||||
curl_socket_t connfd;
|
||||
unsigned char cd;
|
||||
unsigned short s4port;
|
||||
|
||||
if(buffer[SOCKS4_CD] != 1) {
|
||||
logmsg("SOCKS4 CD is not 1: %d", buffer[SOCKS4_CD]);
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
if(rc < 9) {
|
||||
logmsg("SOCKS4 connect message too short: %zd", rc);
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
if(!s_config.port)
|
||||
s4port = (unsigned short)((buffer[SOCKS4_DSTPORT] << 8) |
|
||||
(buffer[SOCKS4_DSTPORT + 1]));
|
||||
else
|
||||
s4port = s_config.port;
|
||||
|
||||
connfd = socksconnect(s4port, s_config.addr);
|
||||
if(connfd == CURL_SOCKET_BAD) {
|
||||
/* failed */
|
||||
cd = 91;
|
||||
}
|
||||
else {
|
||||
/* success */
|
||||
cd = 90;
|
||||
}
|
||||
response[0] = 0; /* reply version 0 */
|
||||
response[1] = cd; /* result */
|
||||
/* copy port and address from connect request */
|
||||
memcpy(&response[2], &buffer[SOCKS4_DSTPORT], 6);
|
||||
rc = (send)(fd, (char *)response, 8, 0);
|
||||
if(rc != 8) {
|
||||
logmsg("Sending SOCKS4 response failed!");
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
logmsg("Sent %zd bytes", rc);
|
||||
loghex(response, rc);
|
||||
|
||||
if(cd == 90)
|
||||
/* now do the transfer */
|
||||
return connfd;
|
||||
|
||||
if(connfd != CURL_SOCKET_BAD)
|
||||
sclose(connfd);
|
||||
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
|
||||
static curl_socket_t sockit(curl_socket_t fd)
|
||||
{
|
||||
unsigned char buffer[2*256 + 16];
|
||||
unsigned char response[2*256 + 16];
|
||||
ssize_t rc;
|
||||
unsigned char len;
|
||||
unsigned char type;
|
||||
unsigned char rep = 0;
|
||||
unsigned char *address;
|
||||
unsigned short socksport;
|
||||
curl_socket_t connfd = CURL_SOCKET_BAD;
|
||||
unsigned short s5port;
|
||||
|
||||
socksd_getconfig();
|
||||
|
||||
rc = recv(fd, (char *)buffer, sizeof(buffer), 0);
|
||||
if(rc <= 0) {
|
||||
logmsg("SOCKS identifier message missing, recv returned %zd", rc);
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
|
||||
logmsg("READ %zd bytes", rc);
|
||||
loghex(buffer, rc);
|
||||
|
||||
if(buffer[SOCKS5_VERSION] == 4)
|
||||
return socks4(fd, buffer, rc);
|
||||
|
||||
if(rc < 3) {
|
||||
logmsg("SOCKS5 identifier message too short: %zd", rc);
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
|
||||
if(buffer[SOCKS5_VERSION] != s_config.version) {
|
||||
logmsg("VERSION byte not %d", s_config.version);
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
if((buffer[SOCKS5_NMETHODS] < s_config.nmethods_min) ||
|
||||
(buffer[SOCKS5_NMETHODS] > s_config.nmethods_max)) {
|
||||
logmsg("NMETHODS byte not within %d - %d ",
|
||||
s_config.nmethods_min, s_config.nmethods_max);
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
/* after NMETHODS follows that many bytes listing the methods the client
|
||||
says it supports */
|
||||
if(rc != (buffer[SOCKS5_NMETHODS] + 2)) {
|
||||
logmsg("Expected %d bytes, got %zd", buffer[SOCKS5_NMETHODS] + 2, rc);
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
logmsg("Incoming request deemed fine!");
|
||||
|
||||
/* respond with two bytes: VERSION + METHOD */
|
||||
response[0] = s_config.responseversion;
|
||||
response[1] = s_config.responsemethod;
|
||||
rc = (send)(fd, (char *)response, 2, 0);
|
||||
if(rc != 2) {
|
||||
logmsg("Sending response failed!");
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
logmsg("Sent %zd bytes", rc);
|
||||
loghex(response, rc);
|
||||
|
||||
/* expect the request or auth */
|
||||
rc = recv(fd, (char *)buffer, sizeof(buffer), 0);
|
||||
if(rc <= 0) {
|
||||
logmsg("SOCKS5 request or auth message missing, recv returned %zd", rc);
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
|
||||
logmsg("READ %zd bytes", rc);
|
||||
loghex(buffer, rc);
|
||||
|
||||
if(s_config.responsemethod == 2) {
|
||||
/* RFC 1929 authentication
|
||||
+----+------+----------+------+----------+
|
||||
|VER | ULEN | UNAME | PLEN | PASSWD |
|
||||
+----+------+----------+------+----------+
|
||||
| 1 | 1 | 1 to 255 | 1 | 1 to 255 |
|
||||
+----+------+----------+------+----------+
|
||||
*/
|
||||
unsigned char ulen;
|
||||
unsigned char plen;
|
||||
bool login = TRUE;
|
||||
if(rc < 5) {
|
||||
logmsg("Too short auth input: %zd", rc);
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
if(buffer[SOCKS5_VERSION] != 1) {
|
||||
logmsg("Auth VERSION byte not 1, got %d", buffer[SOCKS5_VERSION]);
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
ulen = buffer[SOCKS5_ULEN];
|
||||
if(rc < 4 + ulen) {
|
||||
logmsg("Too short packet for username: %zd", rc);
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
plen = buffer[SOCKS5_ULEN + ulen + 1];
|
||||
if(rc < 3 + ulen + plen) {
|
||||
logmsg("Too short packet for ulen %d plen %d: %zd", ulen, plen, rc);
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
if((ulen != strlen(s_config.user)) ||
|
||||
(plen != strlen(s_config.password)) ||
|
||||
memcmp(&buffer[SOCKS5_UNAME], s_config.user, ulen) ||
|
||||
memcmp(&buffer[SOCKS5_UNAME + ulen + 1], s_config.password, plen)) {
|
||||
/* no match! */
|
||||
logmsg("mismatched credentials!");
|
||||
login = FALSE;
|
||||
}
|
||||
response[0] = 1;
|
||||
response[1] = login ? 0 : 1;
|
||||
rc = (send)(fd, (char *)response, 2, 0);
|
||||
if(rc != 2) {
|
||||
logmsg("Sending auth response failed!");
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
logmsg("Sent %zd bytes", rc);
|
||||
loghex(response, rc);
|
||||
if(!login)
|
||||
return CURL_SOCKET_BAD;
|
||||
|
||||
/* expect the request */
|
||||
rc = recv(fd, (char *)buffer, sizeof(buffer), 0);
|
||||
if(rc <= 0) {
|
||||
logmsg("SOCKS5 request message missing, recv returned %zd", rc);
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
|
||||
logmsg("READ %zd bytes", rc);
|
||||
loghex(buffer, rc);
|
||||
}
|
||||
if(rc < 6) {
|
||||
logmsg("Too short for request: %zd", rc);
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
|
||||
if(buffer[SOCKS5_VERSION] != s_config.version) {
|
||||
logmsg("Request VERSION byte not %d", s_config.version);
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
/* 1 == CONNECT */
|
||||
if(buffer[SOCKS5_REQCMD] != s_config.reqcmd) {
|
||||
logmsg("Request COMMAND byte not %d", s_config.reqcmd);
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
/* reserved, should be zero */
|
||||
if(buffer[SOCKS5_RESERVED]) {
|
||||
logmsg("Request COMMAND byte not %d", s_config.reqcmd);
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
/* ATYP:
|
||||
o IP V4 address: X'01'
|
||||
o DOMAINNAME: X'03'
|
||||
o IP V6 address: X'04'
|
||||
*/
|
||||
type = buffer[SOCKS5_ATYP];
|
||||
address = &buffer[SOCKS5_DSTADDR];
|
||||
switch(type) {
|
||||
case 1:
|
||||
/* 4 bytes IPv4 address */
|
||||
len = 4;
|
||||
break;
|
||||
case 3:
|
||||
/* The first octet of the address field contains the number of octets of
|
||||
name that follow */
|
||||
len = buffer[SOCKS5_DSTADDR];
|
||||
len++;
|
||||
break;
|
||||
case 4:
|
||||
/* 16 bytes IPv6 address */
|
||||
len = 16;
|
||||
break;
|
||||
default:
|
||||
logmsg("Unknown ATYP %d", type);
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
if(rc < (4 + len + 2)) {
|
||||
logmsg("Request too short: %zd, expected %d", rc, 4 + len + 2);
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
logmsg("Received ATYP %d", type);
|
||||
|
||||
{
|
||||
FILE *dump;
|
||||
dump = fopen(reqlogfile, "ab");
|
||||
if(dump) {
|
||||
int i;
|
||||
fprintf(dump, "atyp %u =>", type);
|
||||
switch(type) {
|
||||
case 1:
|
||||
/* 4 bytes IPv4 address */
|
||||
fprintf(dump, " %u.%u.%u.%u\n",
|
||||
address[0], address[1], address[2], address[3]);
|
||||
break;
|
||||
case 3:
|
||||
/* The first octet of the address field contains the number of octets
|
||||
of name that follow */
|
||||
fprintf(dump, " %.*s\n", len-1, &address[1]);
|
||||
break;
|
||||
case 4:
|
||||
/* 16 bytes IPv6 address */
|
||||
for(i = 0; i < 16; i++) {
|
||||
fprintf(dump, " %02x", address[i]);
|
||||
}
|
||||
fprintf(dump, "\n");
|
||||
break;
|
||||
}
|
||||
fclose(dump);
|
||||
}
|
||||
}
|
||||
|
||||
if(!s_config.port) {
|
||||
unsigned char *portp = &buffer[SOCKS5_DSTADDR + len];
|
||||
s5port = (unsigned short)((portp[0] << 8) | (portp[1]));
|
||||
}
|
||||
else
|
||||
s5port = s_config.port;
|
||||
|
||||
if(!s_config.connectrep)
|
||||
connfd = socksconnect(s5port, s_config.addr);
|
||||
|
||||
if(connfd == CURL_SOCKET_BAD) {
|
||||
/* failed */
|
||||
rep = 1;
|
||||
}
|
||||
else {
|
||||
rep = s_config.connectrep;
|
||||
}
|
||||
|
||||
/* */
|
||||
response[SOCKS5_VERSION] = s_config.responseversion;
|
||||
|
||||
/*
|
||||
o REP Reply field:
|
||||
o X'00' succeeded
|
||||
o X'01' general SOCKS server failure
|
||||
o X'02' connection not allowed by ruleset
|
||||
o X'03' Network unreachable
|
||||
o X'04' Host unreachable
|
||||
o X'05' Connection refused
|
||||
o X'06' TTL expired
|
||||
o X'07' Command not supported
|
||||
o X'08' Address type not supported
|
||||
o X'09' to X'FF' unassigned
|
||||
*/
|
||||
response[SOCKS5_REP] = rep;
|
||||
response[SOCKS5_RESERVED] = 0; /* must be zero */
|
||||
response[SOCKS5_ATYP] = type; /* address type */
|
||||
|
||||
/* mirror back the original addr + port */
|
||||
|
||||
/* address or hostname */
|
||||
memcpy(&response[SOCKS5_BNDADDR], address, len);
|
||||
|
||||
/* port number */
|
||||
memcpy(&response[SOCKS5_BNDADDR + len],
|
||||
&buffer[SOCKS5_DSTADDR + len], sizeof(socksport));
|
||||
|
||||
rc = (send)(fd, (char *)response, (SEND_TYPE_ARG3)(len + 6), 0);
|
||||
if(rc != (len + 6)) {
|
||||
logmsg("Sending connect response failed!");
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
logmsg("Sent %zd bytes", rc);
|
||||
loghex(response, rc);
|
||||
|
||||
if(!rep)
|
||||
return connfd;
|
||||
|
||||
if(connfd != CURL_SOCKET_BAD)
|
||||
sclose(connfd);
|
||||
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
|
||||
struct perclient {
|
||||
size_t fromremote;
|
||||
size_t fromclient;
|
||||
curl_socket_t remotefd;
|
||||
curl_socket_t clientfd;
|
||||
bool used;
|
||||
};
|
||||
|
||||
/* return non-zero when transfer is done */
|
||||
static int tunnel(struct perclient *cp, fd_set *fds)
|
||||
{
|
||||
ssize_t nread;
|
||||
ssize_t nwrite;
|
||||
char buffer[512];
|
||||
if(FD_ISSET(cp->clientfd, fds)) {
|
||||
/* read from client, send to remote */
|
||||
nread = recv(cp->clientfd, buffer, sizeof(buffer), 0);
|
||||
if(nread > 0) {
|
||||
nwrite = send(cp->remotefd, (char *)buffer,
|
||||
(SEND_TYPE_ARG3)nread, 0);
|
||||
if(nwrite != nread)
|
||||
return 1;
|
||||
cp->fromclient += nwrite;
|
||||
}
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
if(FD_ISSET(cp->remotefd, fds)) {
|
||||
/* read from remote, send to client */
|
||||
nread = recv(cp->remotefd, buffer, sizeof(buffer), 0);
|
||||
if(nread > 0) {
|
||||
nwrite = send(cp->clientfd, (char *)buffer,
|
||||
(SEND_TYPE_ARG3)nread, 0);
|
||||
if(nwrite != nread)
|
||||
return 1;
|
||||
cp->fromremote += nwrite;
|
||||
}
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
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 socksd_incoming(curl_socket_t listenfd)
|
||||
{
|
||||
fd_set fds_read;
|
||||
fd_set fds_write;
|
||||
fd_set fds_err;
|
||||
int clients = 0; /* connected clients */
|
||||
struct perclient c[2];
|
||||
|
||||
memset(c, 0, sizeof(c));
|
||||
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 {
|
||||
int i;
|
||||
ssize_t rc;
|
||||
int error = 0;
|
||||
char errbuf[STRERROR_LEN];
|
||||
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 */
|
||||
#ifdef __DJGPP__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Warith-conversion"
|
||||
#endif
|
||||
FD_SET(sockfd, &fds_read);
|
||||
#ifdef __DJGPP__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
for(i = 0; i < 2; i++) {
|
||||
if(c[i].used) {
|
||||
curl_socket_t fd = c[i].clientfd;
|
||||
#ifdef __DJGPP__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Warith-conversion"
|
||||
#endif
|
||||
FD_SET(fd, &fds_read);
|
||||
#ifdef __DJGPP__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
if((int)fd > maxfd)
|
||||
maxfd = (int)fd;
|
||||
fd = c[i].remotefd;
|
||||
#ifdef __DJGPP__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Warith-conversion"
|
||||
#endif
|
||||
FD_SET(fd, &fds_read);
|
||||
#ifdef __DJGPP__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
if((int)fd > maxfd)
|
||||
maxfd = (int)fd;
|
||||
}
|
||||
}
|
||||
|
||||
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) == SOCKEINTR));
|
||||
|
||||
if(rc < 0) {
|
||||
logmsg("select() failed with error (%d) %s",
|
||||
error, curlx_strerror(error, errbuf, sizeof(errbuf)));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if((clients < 2) && FD_ISSET(sockfd, &fds_read)) {
|
||||
curl_socket_t newfd = accept(sockfd, NULL, NULL);
|
||||
if(CURL_SOCKET_BAD == newfd) {
|
||||
error = SOCKERRNO;
|
||||
logmsg("accept() failed with error (%d) %s",
|
||||
error, curlx_strerror(error, errbuf, sizeof(errbuf)));
|
||||
}
|
||||
else {
|
||||
curl_socket_t remotefd;
|
||||
logmsg("====> Client connect, "
|
||||
"Read config from %s", configfile);
|
||||
remotefd = sockit(newfd); /* SOCKS until done */
|
||||
if(remotefd == CURL_SOCKET_BAD) {
|
||||
logmsg("====> Client disconnect");
|
||||
sclose(newfd);
|
||||
}
|
||||
else {
|
||||
struct perclient *cp = &c[0];
|
||||
logmsg("====> Tunnel transfer");
|
||||
|
||||
if(c[0].used)
|
||||
cp = &c[1];
|
||||
cp->fromremote = 0;
|
||||
cp->fromclient = 0;
|
||||
cp->clientfd = newfd;
|
||||
cp->remotefd = remotefd;
|
||||
cp->used = TRUE;
|
||||
clients++;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
for(i = 0; i < 2; i++) {
|
||||
struct perclient *cp = &c[i];
|
||||
if(cp->used) {
|
||||
if(tunnel(cp, &fds_read)) {
|
||||
logmsg("SOCKS transfer completed. Bytes: < %zu > %zu",
|
||||
cp->fromremote, cp->fromclient);
|
||||
sclose(cp->clientfd);
|
||||
sclose(cp->remotefd);
|
||||
cp->used = FALSE;
|
||||
clients--;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while(clients);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int test_socksd(int argc, char *argv[])
|
||||
{
|
||||
curl_socket_t sock = CURL_SOCKET_BAD;
|
||||
curl_socket_t msgsock = CURL_SOCKET_BAD;
|
||||
int wrotepidfile = 0;
|
||||
int wroteportfile = 0;
|
||||
bool juggle_again;
|
||||
int error;
|
||||
char errbuf[STRERROR_LEN];
|
||||
int arg = 1;
|
||||
|
||||
const char *unix_socket = NULL;
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
bool unlink_socket = false;
|
||||
#endif
|
||||
|
||||
pidname = ".socksd.pid";
|
||||
serverlogfile = "log/socksd.log";
|
||||
configfile = "socksd.config";
|
||||
server_port = 8905;
|
||||
|
||||
while(argc > arg) {
|
||||
if(!strcmp("--version", argv[arg])) {
|
||||
printf("socksd IPv4%s\n",
|
||||
#ifdef USE_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("--backend", argv[arg])) {
|
||||
arg++;
|
||||
if(argc > arg)
|
||||
backendaddr = argv[arg++];
|
||||
}
|
||||
else if(!strcmp("--backendport", argv[arg])) {
|
||||
arg++;
|
||||
if(argc > arg)
|
||||
backendport = (unsigned short)atoi(argv[arg++]);
|
||||
}
|
||||
else if(!strcmp("--logfile", argv[arg])) {
|
||||
arg++;
|
||||
if(argc > arg)
|
||||
serverlogfile = argv[arg++];
|
||||
}
|
||||
else if(!strcmp("--reqfile", argv[arg])) {
|
||||
arg++;
|
||||
if(argc > arg)
|
||||
reqlogfile = argv[arg++];
|
||||
}
|
||||
else if(!strcmp("--ipv6", argv[arg])) {
|
||||
#ifdef USE_IPV6
|
||||
socket_domain = AF_INET6;
|
||||
socket_type = "IPv6";
|
||||
#endif
|
||||
arg++;
|
||||
}
|
||||
else if(!strcmp("--ipv4", argv[arg])) {
|
||||
/* for completeness, we support this option as well */
|
||||
#ifdef USE_IPV6
|
||||
socket_type = "IPv4";
|
||||
#endif
|
||||
arg++;
|
||||
}
|
||||
else if(!strcmp("--unix-socket", argv[arg])) {
|
||||
arg++;
|
||||
if(argc > arg) {
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
struct sockaddr_un sau;
|
||||
unix_socket = argv[arg];
|
||||
if(strlen(unix_socket) >= sizeof(sau.sun_path)) {
|
||||
fprintf(stderr,
|
||||
"socksd: socket path must be shorter than %u chars: %s\n",
|
||||
(unsigned int)sizeof(sau.sun_path), unix_socket);
|
||||
return 0;
|
||||
}
|
||||
socket_domain = AF_UNIX;
|
||||
socket_type = "unix";
|
||||
#endif
|
||||
arg++;
|
||||
}
|
||||
}
|
||||
else if(!strcmp("--port", argv[arg])) {
|
||||
arg++;
|
||||
if(argc > arg) {
|
||||
server_port = (unsigned short)atol(argv[arg]);
|
||||
arg++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
puts("Usage: socksd [option]\n"
|
||||
" --backend [ipv4 addr]\n"
|
||||
" --backendport [TCP port]\n"
|
||||
" --config [file]\n"
|
||||
" --version\n"
|
||||
" --logfile [file]\n"
|
||||
" --pidfile [file]\n"
|
||||
" --portfile [file]\n"
|
||||
" --reqfile [file]\n"
|
||||
" --ipv4\n"
|
||||
" --ipv6\n"
|
||||
" --unix-socket [file]\n"
|
||||
" --port [port]\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if(win32_init())
|
||||
return 2;
|
||||
#endif
|
||||
|
||||
CURLX_SET_BINMODE(stdin);
|
||||
CURLX_SET_BINMODE(stdout);
|
||||
CURLX_SET_BINMODE(stderr);
|
||||
|
||||
install_signal_handlers(false);
|
||||
|
||||
sock = socket(socket_domain, SOCK_STREAM, 0);
|
||||
|
||||
if(CURL_SOCKET_BAD == sock) {
|
||||
error = SOCKERRNO;
|
||||
logmsg("Error creating socket (%d) %s",
|
||||
error, curlx_strerror(error, errbuf, sizeof(errbuf)));
|
||||
goto socks5_cleanup;
|
||||
}
|
||||
|
||||
{
|
||||
/* passive daemon style */
|
||||
sock = sockdaemon(sock, &server_port, unix_socket, FALSE);
|
||||
if(CURL_SOCKET_BAD == sock) {
|
||||
goto socks5_cleanup;
|
||||
}
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
unlink_socket = true;
|
||||
#endif
|
||||
msgsock = CURL_SOCKET_BAD; /* no stream socket yet */
|
||||
}
|
||||
|
||||
logmsg("Running %s version", socket_type);
|
||||
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
if(socket_domain == AF_UNIX)
|
||||
logmsg("Listening on Unix socket %s", unix_socket);
|
||||
else
|
||||
#endif
|
||||
logmsg("Listening on port %hu", server_port);
|
||||
|
||||
wrotepidfile = write_pidfile(pidname);
|
||||
if(!wrotepidfile) {
|
||||
goto socks5_cleanup;
|
||||
}
|
||||
|
||||
if(portname) {
|
||||
wroteportfile = write_portfile(portname, server_port);
|
||||
if(!wroteportfile) {
|
||||
goto socks5_cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
juggle_again = socksd_incoming(sock);
|
||||
} while(juggle_again);
|
||||
|
||||
socks5_cleanup:
|
||||
|
||||
if((msgsock != sock) && (msgsock != CURL_SOCKET_BAD))
|
||||
sclose(msgsock);
|
||||
|
||||
if(sock != CURL_SOCKET_BAD)
|
||||
sclose(sock);
|
||||
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
if(unlink_socket && socket_domain == AF_UNIX && unix_socket) {
|
||||
error = unlink(unix_socket);
|
||||
logmsg("unlink(%s) = %d (%s)", unix_socket,
|
||||
error, curlx_strerror(error, errbuf, sizeof(errbuf)));
|
||||
}
|
||||
#endif
|
||||
|
||||
if(wrotepidfile)
|
||||
unlink(pidname);
|
||||
if(wroteportfile)
|
||||
unlink(portname);
|
||||
|
||||
restore_signal_handlers(false);
|
||||
|
||||
if(got_exit_signal) {
|
||||
logmsg("============> socksd 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("============> socksd quits");
|
||||
return 0;
|
||||
}
|
||||
+2499
File diff suppressed because it is too large
Load Diff
+1377
File diff suppressed because it is too large
Load Diff
+922
@@ -0,0 +1,922 @@
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 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.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.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
#include "first.h"
|
||||
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
/* This function returns a pointer to STATIC memory. It converts the given
|
||||
* binary lump to a hex formatted string usable for output in logs or
|
||||
* whatever.
|
||||
*/
|
||||
char *data_to_hex(char *data, size_t len)
|
||||
{
|
||||
static char buf[256*3];
|
||||
size_t i;
|
||||
char *optr = buf;
|
||||
char *iptr = data;
|
||||
|
||||
if(len > 255)
|
||||
len = 255;
|
||||
|
||||
for(i = 0; i < len; i++) {
|
||||
if((data[i] >= 0x20) && (data[i] < 0x7f))
|
||||
*optr++ = *iptr++;
|
||||
else {
|
||||
snprintf(optr, 4, "%%%02x", (unsigned char)*iptr++);
|
||||
optr += 3;
|
||||
}
|
||||
}
|
||||
*optr = 0; /* in case no sprintf was used */
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
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++) {
|
||||
snprintf(optr, left, "%02x", ptr[i]);
|
||||
width += 2;
|
||||
optr += 2;
|
||||
left -= 2;
|
||||
}
|
||||
if(width)
|
||||
logmsg("'%s'", data);
|
||||
}
|
||||
|
||||
void logmsg(const char *msg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char buffer[2048 + 1];
|
||||
FILE *logfp;
|
||||
struct curltime tv;
|
||||
time_t sec;
|
||||
struct tm *now;
|
||||
char timebuf[50];
|
||||
static time_t epoch_offset;
|
||||
static int known_offset;
|
||||
|
||||
if(!serverlogfile) {
|
||||
fprintf(stderr, "Serverlogfile not set error\n");
|
||||
return;
|
||||
}
|
||||
|
||||
tv = curlx_now();
|
||||
if(!known_offset) {
|
||||
epoch_offset = time(NULL) - tv.tv_sec;
|
||||
known_offset = 1;
|
||||
}
|
||||
sec = epoch_offset + tv.tv_sec;
|
||||
/* !checksrc! disable BANNEDFUNC 1 */
|
||||
now = localtime(&sec); /* not thread safe but we don't care */
|
||||
|
||||
snprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d.%06ld",
|
||||
(int)now->tm_hour, (int)now->tm_min, (int)now->tm_sec,
|
||||
(long)tv.tv_usec);
|
||||
|
||||
va_start(ap, msg);
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wformat-nonliteral"
|
||||
#endif
|
||||
vsnprintf(buffer, sizeof(buffer), msg, ap);
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
va_end(ap);
|
||||
|
||||
do {
|
||||
logfp = fopen(serverlogfile, "ab");
|
||||
/* !checksrc! disable ERRNOVAR 1 */
|
||||
} while(!logfp && (errno == EINTR));
|
||||
if(logfp) {
|
||||
fprintf(logfp, "%s %s\n", timebuf, buffer);
|
||||
fclose(logfp);
|
||||
}
|
||||
else {
|
||||
char errbuf[STRERROR_LEN];
|
||||
int error = errno;
|
||||
fprintf(stderr, "fopen() failed with error (%d) %s\n",
|
||||
error, curlx_strerror(error, errbuf, sizeof(errbuf)));
|
||||
fprintf(stderr, "Error opening file '%s'\n", serverlogfile);
|
||||
fprintf(stderr, "Msg not logged: %s %s\n", timebuf, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char byteval(char *value)
|
||||
{
|
||||
unsigned int num = (unsigned int)atoi(value);
|
||||
return num & 0xff;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
/* use instead of perror() on generic Windows */
|
||||
static void win32_perror(const char *msg)
|
||||
{
|
||||
char buf[512];
|
||||
int err = SOCKERRNO;
|
||||
curlx_winapi_strerror(err, buf, sizeof(buf));
|
||||
if(msg)
|
||||
fprintf(stderr, "%s: ", msg);
|
||||
fprintf(stderr, "%s\n", buf);
|
||||
}
|
||||
|
||||
static void win32_cleanup(void)
|
||||
{
|
||||
#ifdef USE_WINSOCK
|
||||
WSACleanup();
|
||||
#endif
|
||||
|
||||
/* flush buffers of all streams regardless of their mode */
|
||||
_flushall();
|
||||
}
|
||||
|
||||
int win32_init(void)
|
||||
{
|
||||
curlx_now_init();
|
||||
#ifdef USE_WINSOCK
|
||||
{
|
||||
WORD wVersionRequested;
|
||||
WSADATA wsaData;
|
||||
int err;
|
||||
|
||||
wVersionRequested = MAKEWORD(2, 2);
|
||||
err = WSAStartup(wVersionRequested, &wsaData);
|
||||
|
||||
if(err) {
|
||||
win32_perror("Winsock init failed");
|
||||
logmsg("Error initialising Winsock -- aborting");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(LOBYTE(wsaData.wVersion) != LOBYTE(wVersionRequested) ||
|
||||
HIBYTE(wsaData.wVersion) != HIBYTE(wVersionRequested) ) {
|
||||
WSACleanup();
|
||||
win32_perror("Winsock init failed");
|
||||
logmsg("No suitable winsock.dll found -- aborting");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif /* USE_WINSOCK */
|
||||
atexit(win32_cleanup);
|
||||
return 0;
|
||||
}
|
||||
#endif /* _WIN32 */
|
||||
|
||||
/* fopens the test case file */
|
||||
FILE *test2fopen(long testno, const char *logdir2)
|
||||
{
|
||||
FILE *stream;
|
||||
char filename[256];
|
||||
/* first try the alternative, preprocessed, file */
|
||||
snprintf(filename, sizeof(filename), "%s/test%ld", logdir2, testno);
|
||||
stream = fopen(filename, "rb");
|
||||
if(stream)
|
||||
return stream;
|
||||
|
||||
/* then try the source version */
|
||||
snprintf(filename, sizeof(filename), "%s/data/test%ld", srcpath, testno);
|
||||
stream = fopen(filename, "rb");
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#define t_getpid() GetCurrentProcessId()
|
||||
#else
|
||||
#define t_getpid() getpid()
|
||||
#endif
|
||||
|
||||
curl_off_t our_getpid(void)
|
||||
{
|
||||
curl_off_t pid = (curl_off_t)t_getpid();
|
||||
#ifdef _WIN32
|
||||
/* store pid + MAX_PID to avoid conflict with Cygwin/msys PIDs, see also:
|
||||
* - 2019-01-31: https://cygwin.com/git/?p=newlib-cygwin.git;a=commit;h=b5e1003722cb14235c4f166be72c09acdffc62ea
|
||||
* - 2019-02-02: https://cygwin.com/git/?p=newlib-cygwin.git;a=commit;h=448cf5aa4b429d5a9cebf92a0da4ab4b5b6d23fe
|
||||
* - 2024-12-19: https://cygwin.com/git/?p=newlib-cygwin.git;a=commit;h=363357c023ce01e936bdaedf0f479292a8fa4e0f
|
||||
*/
|
||||
pid += 4194304;
|
||||
#endif
|
||||
return pid;
|
||||
}
|
||||
|
||||
int write_pidfile(const char *filename)
|
||||
{
|
||||
FILE *pidfile;
|
||||
curl_off_t pid;
|
||||
|
||||
pid = our_getpid();
|
||||
pidfile = fopen(filename, "wb");
|
||||
if(!pidfile) {
|
||||
char errbuf[STRERROR_LEN];
|
||||
logmsg("Couldn't write pid file: %s (%d) %s", filename,
|
||||
errno, curlx_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
return 0; /* fail */
|
||||
}
|
||||
fprintf(pidfile, "%ld\n", (long)pid);
|
||||
fclose(pidfile);
|
||||
logmsg("Wrote pid %ld to %s", (long)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) {
|
||||
char errbuf[STRERROR_LEN];
|
||||
logmsg("Couldn't write port file: %s (%d) %s", filename,
|
||||
errno, curlx_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
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;
|
||||
int error = 0;
|
||||
char errbuf[STRERROR_LEN];
|
||||
int res;
|
||||
|
||||
do {
|
||||
lockfile = fopen(filename, "wb");
|
||||
/* !checksrc! disable ERRNOVAR 1 */
|
||||
} while(!lockfile && ((error = errno) == EINTR));
|
||||
if(!lockfile) {
|
||||
logmsg("Error creating lock file %s error (%d) %s", filename,
|
||||
error, curlx_strerror(error, errbuf, sizeof(errbuf)));
|
||||
return;
|
||||
}
|
||||
|
||||
res = fclose(lockfile);
|
||||
if(res)
|
||||
logmsg("Error closing lock file %s error (%d) %s", filename,
|
||||
errno, curlx_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
}
|
||||
|
||||
void clear_advisor_read_lock(const char *filename)
|
||||
{
|
||||
int error = 0;
|
||||
int res;
|
||||
|
||||
/*
|
||||
** Log all removal failures. Even those due to file not existing.
|
||||
** This allows to detect if unexpectedly the file has already been
|
||||
** removed by a process different than the one that should do this.
|
||||
*/
|
||||
|
||||
do {
|
||||
res = unlink(filename);
|
||||
/* !checksrc! disable ERRNOVAR 1 */
|
||||
} while(res && ((error = errno) == EINTR));
|
||||
if(res) {
|
||||
char errbuf[STRERROR_LEN];
|
||||
logmsg("Error removing lock file %s error (%d) %s", filename,
|
||||
error, curlx_strerror(error, errbuf, sizeof(errbuf)));
|
||||
}
|
||||
}
|
||||
|
||||
/* vars used to keep around previous signal handlers */
|
||||
|
||||
typedef void (*SIGHANDLER_T)(int);
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER <= 1700)
|
||||
/* Workaround for warning C4306:
|
||||
'type cast' : conversion from 'int' to 'void (__cdecl *)(int)' */
|
||||
#undef SIG_ERR
|
||||
#define SIG_ERR ((SIGHANDLER_T)(size_t)-1)
|
||||
#endif
|
||||
|
||||
#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
|
||||
|
||||
#if defined(_WIN32) && !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE)
|
||||
static DWORD thread_main_id = 0;
|
||||
static HANDLE thread_main_window = NULL;
|
||||
static HWND hidden_main_window = NULL;
|
||||
#endif
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
#ifndef UNDER_CE
|
||||
/*
|
||||
* Only call signal-safe functions from the signal handler, as required by
|
||||
* the POSIX specification:
|
||||
* https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html
|
||||
* Hence, do not call 'logmsg()', and instead use 'open/write/close' to
|
||||
* log errors.
|
||||
*/
|
||||
static void exit_signal_handler(int signum)
|
||||
{
|
||||
int old_errno = errno;
|
||||
if(!serverlogfile) {
|
||||
static const char msg[] = "exit_signal_handler: serverlogfile not set\n";
|
||||
(void)!write(STDERR_FILENO, msg, sizeof(msg) - 1);
|
||||
}
|
||||
else {
|
||||
#ifdef _WIN32
|
||||
#define OPENMODE S_IREAD | S_IWRITE
|
||||
#else
|
||||
#define OPENMODE S_IRUSR | S_IWUSR
|
||||
#endif
|
||||
int fd = open(serverlogfile, O_WRONLY | O_CREAT | O_APPEND, OPENMODE);
|
||||
if(fd != -1) {
|
||||
static const char msg[] = "exit_signal_handler: called\n";
|
||||
(void)!write(fd, msg, sizeof(msg) - 1);
|
||||
close(fd);
|
||||
}
|
||||
else {
|
||||
static const char msg[] = "exit_signal_handler: failed opening ";
|
||||
(void)!write(STDERR_FILENO, msg, sizeof(msg) - 1);
|
||||
(void)!write(STDERR_FILENO, serverlogfile, strlen(serverlogfile));
|
||||
(void)!write(STDERR_FILENO, "\n", 1);
|
||||
}
|
||||
}
|
||||
if(got_exit_signal == 0) {
|
||||
got_exit_signal = 1;
|
||||
exit_signal = signum;
|
||||
#ifdef _WIN32
|
||||
if(exit_event)
|
||||
(void)SetEvent(exit_event);
|
||||
#endif
|
||||
}
|
||||
(void)signal(signum, exit_signal_handler);
|
||||
CURL_SETERRNO(old_errno);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && !defined(UNDER_CE)
|
||||
/* 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 SIGKILL 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://learn.microsoft.com/cpp/c-runtime-library/reference/signal
|
||||
*/
|
||||
static BOOL WINAPI ctrl_event_handler(DWORD dwCtrlType)
|
||||
{
|
||||
int signum = 0;
|
||||
logmsg("ctrl_event_handler: %lu", 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: %lu -> %d", dwCtrlType, signum);
|
||||
raise(signum);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE)
|
||||
/* 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);
|
||||
raise(signum);
|
||||
}
|
||||
}
|
||||
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||
}
|
||||
/* Window message queue loop for hidden main window, details see above.
|
||||
*/
|
||||
static DWORD WINAPI main_window_loop(void *lpParameter)
|
||||
{
|
||||
WNDCLASS wc;
|
||||
BOOL ret;
|
||||
MSG msg;
|
||||
|
||||
ZeroMemory(&wc, sizeof(wc));
|
||||
wc.lpfnWndProc = (WNDPROC)main_window_proc;
|
||||
wc.hInstance = (HINSTANCE)lpParameter;
|
||||
wc.lpszClassName = TEXT("MainWClass");
|
||||
if(!RegisterClass(&wc)) {
|
||||
win32_perror("RegisterClass failed");
|
||||
return (DWORD)-1;
|
||||
}
|
||||
|
||||
hidden_main_window = CreateWindowEx(0, TEXT("MainWClass"),
|
||||
TEXT("Recv WM_CLOSE msg"),
|
||||
WS_OVERLAPPEDWINDOW,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
(HWND)NULL, (HMENU)NULL,
|
||||
wc.hInstance, NULL);
|
||||
if(!hidden_main_window) {
|
||||
win32_perror("CreateWindowEx failed");
|
||||
return (DWORD)-1;
|
||||
}
|
||||
|
||||
do {
|
||||
ret = GetMessage(&msg, NULL, 0, 0);
|
||||
if(ret == -1) {
|
||||
win32_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
|
||||
|
||||
#ifndef UNDER_CE
|
||||
static SIGHANDLER_T set_signal(int signum, SIGHANDLER_T handler,
|
||||
bool restartable)
|
||||
{
|
||||
#if defined(HAVE_SIGACTION) && defined(SA_RESTART)
|
||||
struct sigaction sa, oldsa;
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_handler = handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaddset(&sa.sa_mask, signum);
|
||||
sa.sa_flags = restartable ? SA_RESTART : 0;
|
||||
|
||||
if(sigaction(signum, &sa, &oldsa))
|
||||
return SIG_ERR;
|
||||
|
||||
return oldsa.sa_handler;
|
||||
#else
|
||||
SIGHANDLER_T oldhdlr = signal(signum, handler);
|
||||
|
||||
#ifdef HAVE_SIGINTERRUPT
|
||||
if(oldhdlr != SIG_ERR)
|
||||
siginterrupt(signum, (int) restartable);
|
||||
#else
|
||||
(void)restartable;
|
||||
#endif
|
||||
|
||||
return oldhdlr;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void install_signal_handlers(bool keep_sigalrm)
|
||||
{
|
||||
char errbuf[STRERROR_LEN];
|
||||
(void)errbuf;
|
||||
#ifdef _WIN32
|
||||
/* setup Windows exit event before any signal can trigger */
|
||||
exit_event = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if(!exit_event)
|
||||
logmsg("cannot create exit event");
|
||||
#endif
|
||||
#ifdef SIGHUP
|
||||
/* ignore SIGHUP signal */
|
||||
old_sighup_handler = set_signal(SIGHUP, SIG_IGN, FALSE);
|
||||
if(old_sighup_handler == SIG_ERR)
|
||||
logmsg("cannot install SIGHUP handler: (%d) %s",
|
||||
errno, curlx_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
#endif
|
||||
#ifdef SIGPIPE
|
||||
/* ignore SIGPIPE signal */
|
||||
old_sigpipe_handler = set_signal(SIGPIPE, SIG_IGN, FALSE);
|
||||
if(old_sigpipe_handler == SIG_ERR)
|
||||
logmsg("cannot install SIGPIPE handler: (%d) %s",
|
||||
errno, curlx_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
#endif
|
||||
#ifdef SIGALRM
|
||||
if(!keep_sigalrm) {
|
||||
/* ignore SIGALRM signal */
|
||||
old_sigalrm_handler = set_signal(SIGALRM, SIG_IGN, FALSE);
|
||||
if(old_sigalrm_handler == SIG_ERR)
|
||||
logmsg("cannot install SIGALRM handler: (%d) %s",
|
||||
errno, curlx_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
}
|
||||
#else
|
||||
(void)keep_sigalrm;
|
||||
#endif
|
||||
#ifdef SIGINT
|
||||
/* handle SIGINT signal with our exit_signal_handler */
|
||||
old_sigint_handler = set_signal(SIGINT, exit_signal_handler, TRUE);
|
||||
if(old_sigint_handler == SIG_ERR)
|
||||
logmsg("cannot install SIGINT handler: (%d) %s",
|
||||
errno, curlx_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
#endif
|
||||
#ifdef SIGTERM
|
||||
/* handle SIGTERM signal with our exit_signal_handler */
|
||||
old_sigterm_handler = set_signal(SIGTERM, exit_signal_handler, TRUE);
|
||||
if(old_sigterm_handler == SIG_ERR)
|
||||
logmsg("cannot install SIGTERM handler: (%d) %s",
|
||||
errno, curlx_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
#endif
|
||||
#if defined(SIGBREAK) && defined(_WIN32)
|
||||
/* handle SIGBREAK signal with our exit_signal_handler */
|
||||
old_sigbreak_handler = set_signal(SIGBREAK, exit_signal_handler, TRUE);
|
||||
if(old_sigbreak_handler == SIG_ERR)
|
||||
logmsg("cannot install SIGBREAK handler: (%d) %s",
|
||||
errno, curlx_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
#ifndef UNDER_CE
|
||||
if(!SetConsoleCtrlHandler(ctrl_event_handler, TRUE))
|
||||
logmsg("cannot install CTRL event handler");
|
||||
#endif
|
||||
|
||||
#if !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE)
|
||||
thread_main_window = CreateThread(NULL, 0, &main_window_loop,
|
||||
GetModuleHandle(NULL), 0, &thread_main_id);
|
||||
if(!thread_main_window || !thread_main_id)
|
||||
logmsg("cannot start main window loop");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void restore_signal_handlers(bool keep_sigalrm)
|
||||
{
|
||||
#ifdef SIGHUP
|
||||
if(SIG_ERR != old_sighup_handler)
|
||||
(void)set_signal(SIGHUP, old_sighup_handler, FALSE);
|
||||
#endif
|
||||
#ifdef SIGPIPE
|
||||
if(SIG_ERR != old_sigpipe_handler)
|
||||
(void)set_signal(SIGPIPE, old_sigpipe_handler, FALSE);
|
||||
#endif
|
||||
#ifdef SIGALRM
|
||||
if(!keep_sigalrm) {
|
||||
if(SIG_ERR != old_sigalrm_handler)
|
||||
(void)set_signal(SIGALRM, old_sigalrm_handler, FALSE);
|
||||
}
|
||||
#else
|
||||
(void)keep_sigalrm;
|
||||
#endif
|
||||
#ifdef SIGINT
|
||||
if(SIG_ERR != old_sigint_handler)
|
||||
(void)set_signal(SIGINT, old_sigint_handler, FALSE);
|
||||
#endif
|
||||
#ifdef SIGTERM
|
||||
if(SIG_ERR != old_sigterm_handler)
|
||||
(void)set_signal(SIGTERM, old_sigterm_handler, FALSE);
|
||||
#endif
|
||||
#if defined(SIGBREAK) && defined(_WIN32)
|
||||
if(SIG_ERR != old_sigbreak_handler)
|
||||
(void)set_signal(SIGBREAK, old_sigbreak_handler, FALSE);
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
#ifndef UNDER_CE
|
||||
(void)SetConsoleCtrlHandler(ctrl_event_handler, FALSE);
|
||||
#endif
|
||||
#if !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE)
|
||||
if(thread_main_window && thread_main_id) {
|
||||
if(PostThreadMessage(thread_main_id, WM_APP, 0, 0)) {
|
||||
if(WaitForSingleObjectEx(thread_main_window, INFINITE, TRUE)) {
|
||||
if(CloseHandle(thread_main_window)) {
|
||||
thread_main_window = NULL;
|
||||
thread_main_id = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(exit_event) {
|
||||
if(CloseHandle(exit_event)) {
|
||||
exit_event = NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
|
||||
int bind_unix_socket(curl_socket_t sock, const char *unix_socket,
|
||||
struct sockaddr_un *sau)
|
||||
{
|
||||
int error;
|
||||
char errbuf[STRERROR_LEN];
|
||||
int rc;
|
||||
size_t len = strlen(unix_socket);
|
||||
|
||||
memset(sau, 0, sizeof(struct sockaddr_un));
|
||||
sau->sun_family = AF_UNIX;
|
||||
if(len >= sizeof(sau->sun_path) - 1) {
|
||||
logmsg("Too long unix socket domain path (%zd)", len);
|
||||
return -1;
|
||||
}
|
||||
strcpy(sau->sun_path, unix_socket);
|
||||
rc = bind(sock, (struct sockaddr*)sau, sizeof(struct sockaddr_un));
|
||||
if(rc && SOCKERRNO == SOCKEADDRINUSE) {
|
||||
struct_stat statbuf;
|
||||
/* socket already exists. Perhaps it is stale? */
|
||||
curl_socket_t unixfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if(CURL_SOCKET_BAD == unixfd) {
|
||||
logmsg("Failed to create socket at %s (%d) %s", unix_socket,
|
||||
SOCKERRNO, curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf)));
|
||||
return -1;
|
||||
}
|
||||
/* check whether the server is alive */
|
||||
rc = connect(unixfd, (struct sockaddr*)sau, sizeof(struct sockaddr_un));
|
||||
error = SOCKERRNO;
|
||||
sclose(unixfd);
|
||||
if(rc && error != SOCKECONNREFUSED) {
|
||||
logmsg("Failed to connect to %s (%d) %s", unix_socket,
|
||||
error, curlx_strerror(error, errbuf, sizeof(errbuf)));
|
||||
return rc;
|
||||
}
|
||||
/* socket server is not alive, now check if it was actually a socket. */
|
||||
#ifdef _WIN32
|
||||
/* Windows does not have lstat function. */
|
||||
rc = curlx_win32_stat(unix_socket, &statbuf);
|
||||
#else
|
||||
rc = lstat(unix_socket, &statbuf);
|
||||
#endif
|
||||
if(rc) {
|
||||
logmsg("Error binding socket, failed to stat %s (%d) %s", unix_socket,
|
||||
errno, curlx_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
return rc;
|
||||
}
|
||||
#ifdef S_IFSOCK
|
||||
if((statbuf.st_mode & S_IFSOCK) != S_IFSOCK) {
|
||||
logmsg("Error binding socket, failed to stat %s", unix_socket);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
/* dead socket, cleanup and retry bind */
|
||||
rc = unlink(unix_socket);
|
||||
if(rc) {
|
||||
logmsg("Error binding socket, failed to unlink %s (%d) %s", unix_socket,
|
||||
errno, curlx_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
return rc;
|
||||
}
|
||||
/* stale socket is gone, retry bind */
|
||||
rc = bind(sock, (struct sockaddr*)sau, sizeof(struct sockaddr_un));
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
curl_socket_t sockdaemon(curl_socket_t sock,
|
||||
unsigned short *listenport,
|
||||
const char *unix_socket,
|
||||
bool bind_only)
|
||||
{
|
||||
/* 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;
|
||||
char errbuf[STRERROR_LEN];
|
||||
|
||||
#ifndef USE_UNIX_SOCKETS
|
||||
(void)unix_socket;
|
||||
#endif
|
||||
|
||||
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, curlx_strerror(error, errbuf, sizeof(errbuf)));
|
||||
if(maxretr) {
|
||||
rc = curlx_wait_ms(delay);
|
||||
if(rc) {
|
||||
/* should not happen */
|
||||
error = SOCKERRNO;
|
||||
logmsg("curlx_wait_ms() failed with error (%d) %s",
|
||||
error, curlx_strerror(error, errbuf, sizeof(errbuf)));
|
||||
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, curlx_strerror(error, errbuf, sizeof(errbuf)));
|
||||
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. */
|
||||
|
||||
switch(socket_domain) {
|
||||
case AF_INET:
|
||||
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));
|
||||
break;
|
||||
#ifdef USE_IPV6
|
||||
case AF_INET6:
|
||||
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));
|
||||
break;
|
||||
#endif /* USE_IPV6 */
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
case AF_UNIX:
|
||||
rc = bind_unix_socket(sock, unix_socket, &listener.sau);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
rc = 1;
|
||||
}
|
||||
|
||||
if(rc) {
|
||||
error = SOCKERRNO;
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
if(socket_domain == AF_UNIX)
|
||||
logmsg("Error binding socket on path %s (%d) %s", unix_socket,
|
||||
error, curlx_strerror(error, errbuf, sizeof(errbuf)));
|
||||
else
|
||||
#endif
|
||||
logmsg("Error binding socket on port %hu (%d) %s", *listenport,
|
||||
error, curlx_strerror(error, errbuf, sizeof(errbuf)));
|
||||
sclose(sock);
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
|
||||
if(!*listenport
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
&& !unix_socket
|
||||
#endif
|
||||
) {
|
||||
/* 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 USE_IPV6
|
||||
if(socket_domain == AF_INET6)
|
||||
la_size = sizeof(localaddr.sa6);
|
||||
else
|
||||
#endif
|
||||
la_size = sizeof(localaddr.sa4);
|
||||
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, curlx_strerror(error, errbuf, sizeof(errbuf)));
|
||||
sclose(sock);
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
switch(localaddr.sa.sa_family) {
|
||||
case AF_INET:
|
||||
*listenport = ntohs(localaddr.sa4.sin_port);
|
||||
break;
|
||||
#ifdef USE_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;
|
||||
}
|
||||
}
|
||||
|
||||
/* bindonly option forces no listening */
|
||||
if(bind_only) {
|
||||
logmsg("instructed to bind port without listening");
|
||||
return sock;
|
||||
}
|
||||
|
||||
/* start accepting connections */
|
||||
rc = listen(sock, 5);
|
||||
if(rc) {
|
||||
error = SOCKERRNO;
|
||||
logmsg("listen(%ld, 5) failed with error (%d) %s", (long)sock,
|
||||
error, curlx_strerror(error, errbuf, sizeof(errbuf)));
|
||||
sclose(sock);
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
Reference in New Issue
Block a user