Merge pull request #96 from korizza/master
merge #95
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bb616a6..b261b67 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,6 +14,9 @@
if(COMMAND cmake_policy)
cmake_policy(SET CMP0003 OLD)
cmake_policy(SET CMP0005 OLD)
+ if ( POLICY CMP0042 )
+ cmake_policy(SET CMP0042 NEW)
+ endif ( POLICY CMP0042 )
if ( POLICY CMP0046 )
cmake_policy(SET CMP0046 OLD)
endif ( POLICY CMP0046 )
@@ -32,7 +35,7 @@
# Package architecture
IF ( NOT DEFINED PACKAGE_ARCHITECTURE )
- EXECUTE_PROCESS(COMMAND "/bin/uname" "-m" OUTPUT_VARIABLE UNAME_M)
+ EXECUTE_PROCESS(COMMAND "uname" "-m" OUTPUT_VARIABLE UNAME_M)
# strip trailing newline
STRING(REPLACE "\n" "" PACKAGE_ARCHITECTURE ${UNAME_M})
ENDIF ( NOT DEFINED PACKAGE_ARCHITECTURE )
@@ -44,9 +47,13 @@
IF ( DEFINED LIB )
SET ( LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${LIB}" )
ELSE ( DEFINED LIB )
- IF (CMAKE_SIZEOF_VOID_P MATCHES "8")
- SET( LIB_SUFFIX "64" )
- ENDIF(CMAKE_SIZEOF_VOID_P MATCHES "8")
+ IF (APPLE)
+ SET(LIB_SUFFIX "")
+ ELSE (APPLE)
+ IF (CMAKE_SIZEOF_VOID_P MATCHES "8")
+ SET( LIB_SUFFIX "64" )
+ ENDIF(CMAKE_SIZEOF_VOID_P MATCHES "8")
+ ENDIF (APPLE)
SET ( LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}" )
ENDIF ( DEFINED LIB )
MESSAGE(STATUS "Libraries will be installed in ${LIB_INSTALL_DIR}" )
@@ -170,9 +177,9 @@
IF( BUILD_PYTHON )
MESSAGE(STATUS "Building Python bindings" )
- FIND_PACKAGE(PythonLibs)
+ FIND_PACKAGE(PythonLibs 2.6 REQUIRED)
IF (PYTHON_LIBRARY)
- FIND_PACKAGE(PythonInterp REQUIRED)
+ FIND_PACKAGE(PythonInterp 2.6 REQUIRED)
MESSAGE(STATUS "Found PythonLibs...")
FIND_PACKAGE(PythonLinkLibs)
IF (PYTHON_LINK_LIBS)
@@ -378,6 +385,10 @@
SET(HAVE_ALLOCA 0)
SET(C_ALLOCA 0)
CHECK_INCLUDE_FILE( "alloca.h" HAVE_ALLOCA_H )
+ELSE (NOT HAVE_ALLOCA)
+ SET(HAVE_ALLOCA 1)
+ SET(C_ALLOCA 1)
+ SET(HAVE_ALLOCA_H 0)
ENDIF (NOT HAVE_ALLOCA)
#
diff --git a/ChangeLog b/ChangeLog
index 3481a80..13d4ba9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,6 +2,10 @@
- Bugfixes
- Prevent buffer overflow if digest realm too long
- Ensure path starts with '/' in client data endpoint
+ - Java bindings build fix
+ - Fix SSL library, usage is not thread safe (vcrhonek)
+ - Improve NetBSD and OSX builds (apjanke)
+ - Install winrs with correct name and permissions
2.6.2
- Features
diff --git a/VERSION.cmake b/VERSION.cmake
index 0ad437e..4e6d033 100644
--- a/VERSION.cmake
+++ b/VERSION.cmake
@@ -44,10 +44,10 @@
# set COMPATMINOR to MINOR. (binary incompatible change)
#
-# Package version 2.6.2
+# Package version 2.6.3
SET(OPENWSMAN_MAJOR "2")
SET(OPENWSMAN_MINOR "6")
-SET(OPENWSMAN_PATCH "2")
+SET(OPENWSMAN_PATCH "3")
# Plugin API 2.2
SET(OPENWSMAN_PLUGIN_API_MAJOR "2")
diff --git a/autoconfiscate.sh b/autoconfiscate.sh
index 254ccf5..3c09572 100755
--- a/autoconfiscate.sh
+++ b/autoconfiscate.sh
@@ -1,8 +1,18 @@
-#!/bin/sh
+#!/bin/sh
-echo "*** Autoconf/automake is deprecated for Openwsman"
-echo "*** and might not fully work."
-echo "*** Use cmake instead !"
+cat <<EOS >&2
+*** Autoconf/automake is deprecated for Openwsman and might not fully work.
+*** Please use CMake instead!
+*** Pull requests welcome ;-)
+EOS
+
+if [ "$1" != "--ignore-deprecation-warning" ]; then
+ cat <<EOS >&2
+*** To ignore this warning and proceed regardless, re-run as follows:
+*** $0 --ignore-deprecation-warning
+EOS
+ exit 1
+fi
UNAME=`uname`
diff --git a/bindings/java/CMakeLists.txt b/bindings/java/CMakeLists.txt
index b961916..b3c95a9 100644
--- a/bindings/java/CMakeLists.txt
+++ b/bindings/java/CMakeLists.txt
@@ -41,7 +41,7 @@
COMMAND ${JAVA_COMPILE} ${java_SOURCE} ${EXPLICIT_SOURCE} ${java_TARGET} ${EXPLICIT_TARGET} -d . *.java
COMMAND ${CMAKE_COMMAND} -E echo_append "Creating JAR ..."
COMMAND ${JAVA_ARCHIVE} cvf ${jar_NAME} *.so org/*
- DEPENDS ${SWIG_OUTPUT}
+ DEPENDS jwsman
)
ADD_CUSTOM_TARGET ( jwsman_all ALL
diff --git a/bindings/java/tests/identify.java b/bindings/java/tests/identify.java
index 180afa6..6432b45 100644
--- a/bindings/java/tests/identify.java
+++ b/bindings/java/tests/identify.java
@@ -4,7 +4,10 @@
// Java test for WS-Identify
//
-import org.openwsman.*;
+import org.openwsman.Client;
+import org.openwsman.ClientOptions;
+import org.openwsman.OpenWSManConstants;
+import org.openwsman.XmlDoc;
public class identify {
diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt
index 59f60aa..e27b70e 100644
--- a/bindings/python/CMakeLists.txt
+++ b/bindings/python/CMakeLists.txt
@@ -12,7 +12,7 @@
add_subdirectory(tests)
-EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1)" OUTPUT_VARIABLE PYTHON_LIB_DIR)
+EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(1))" OUTPUT_VARIABLE PYTHON_LIB_DIR)
STRING(REPLACE "\n" "" PYTHON_LIB_DIR "${PYTHON_LIB_DIR}")
MESSAGE(STATUS "Python executable: ${PYTHON_EXECUTABLE}")
diff --git a/package/openwsman.spec.in b/package/openwsman.spec.in
index 7f079e8..8926198 100644
--- a/package/openwsman.spec.in
+++ b/package/openwsman.spec.in
@@ -388,9 +388,6 @@
install -D -m 644 %{S:3} $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/SuSEfirewall2.d/services/openwsman
%endif
-# rename winrs.rb -> winrs
-mv $RPM_BUILD_ROOT%{_bindir}/winrs.rb $RPM_BUILD_ROOT%{_bindir}/winrs
-
%post -n libwsman3 -p /sbin/ldconfig
%postun -n libwsman3
diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt
index d625c47..2e23b27 100644
--- a/src/lib/CMakeLists.txt
+++ b/src/lib/CMakeLists.txt
@@ -53,6 +53,9 @@
SET( wsman_curl_client_transport_SOURCES wsman-client-transport.c wsman-curl-client-transport.c )
ADD_LIBRARY( ${WSMAN_CLIENT_TRANSPORT_PKG} SHARED ${wsman_curl_client_transport_SOURCES} )
TARGET_LINK_LIBRARIES( ${WSMAN_CLIENT_TRANSPORT_PKG} ${CURL_LIBRARIES} )
+IF( ENABLE_EVENTING_SUPPORT )
+TARGET_LINK_LIBRARIES( ${WSMAN_CLIENT_TRANSPORT_PKG} ${OPENSSL_LIBRARIES} )
+ENDIF( ENABLE_EVENTING_SUPPORT )
SET_TARGET_PROPERTIES( ${WSMAN_CLIENT_TRANSPORT_PKG} PROPERTIES VERSION 1.0.0 SOVERSION 1)
INSTALL(TARGETS ${WSMAN_CLIENT_TRANSPORT_PKG} DESTINATION ${LIB_INSTALL_DIR})
diff --git a/src/lib/wsman-curl-client-transport.c b/src/lib/wsman-curl-client-transport.c
index d58fd72..cd7f517 100644
--- a/src/lib/wsman-curl-client-transport.c
+++ b/src/lib/wsman-curl-client-transport.c
@@ -46,8 +46,10 @@
#include <curl/curl.h>
#include <curl/easy.h>
+#ifdef ENABLE_EVENTING_SUPPORT
#include <openssl/opensslv.h>
#include <openssl/ssl.h>
+#endif
#include "u/libu.h"
#include "wsman-types.h"
diff --git a/src/plugins/cim/cim_data_stubs.c b/src/plugins/cim/cim_data_stubs.c
index 7bd84bb..079746a 100644
--- a/src/plugins/cim/cim_data_stubs.c
+++ b/src/plugins/cim/cim_data_stubs.c
@@ -170,27 +170,30 @@
hscan_t hs;
hnode_t *hn;
int rv = 0;
- if(!client){
+ if (!client) {
return 0;
}
- if (client->resource_uri && (strcmp( client->resource_uri, CIM_ALL_AVAILABLE_CLASSES ) ==0) ) {
+ if (!client->resource_uri) {
+ return 0;
+ }
+ if (strcmp( client->resource_uri, CIM_ALL_AVAILABLE_CLASSES ) == 0 ) {
return 1;
}
- if ( client->resource_uri && (strstr( client->resource_uri, XML_NS_CIM_INTRINSIC ) != NULL )) {
+ if (strstr( client->resource_uri, XML_NS_CIM_INTRINSIC ) != NULL ) {
return 1;
}
/* Ok if class schema is CIM, uri starts with XML_NS_CIM_CLASS
* and method is not 'Create' (CIM_ is abstract, cannot be created)
*/
- if (client->requested_class && client->resource_uri && client->method
+ if (client->requested_class && client->method
&& (strncmp(client->requested_class, "CIM_", 4) == 0 )
&& (strstr(client->resource_uri , XML_NS_CIM_CLASS) == client->resource_uri )
&& (strcmp(client->method, TRANSFER_CREATE) != 0)) {
return 1;
}
- if (client->requested_class && client->namespaces && client->resource_uri) {
+ if (client->requested_class && client->namespaces) {
hash_scan_begin(&hs, client->namespaces);
while ((hn = hash_scan_next(&hs))) {
if ( ( strstr(client->requested_class,
diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt
index e48b086..f4ec4a8 100644
--- a/src/server/CMakeLists.txt
+++ b/src/server/CMakeLists.txt
@@ -6,9 +6,9 @@
SET( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g" )
SET(openwsmand_SOURCES shttpd/string.c shttpd/shttpd.c shttpd/auth.c shttpd/md5.c shttpd/adapter.c shttpd/cgi.c)
-SET(openwsmand_SOURCES ${openwsmand_SOURCES} shttpd/mime_type.c shttpd/config.c shttpd/io_socket.c)
+SET(openwsmand_SOURCES ${openwsmand_SOURCES} shttpd/log.c shttpd/io_socket.c)
SET(openwsmand_SOURCES ${openwsmand_SOURCES} shttpd/io_ssl.c shttpd/io_emb.c shttpd/compat_unix.c shttpd/io_dir.c shttpd/io_file.c)
-SET(openwsmand_SOURCES ${openwsmand_SOURCES} shttpd/shttpd_defs.h shttpd/llist.h shttpd/shttpd.h shttpd/std_includes.h shttpd/io.h shttpd/md5.h shttpd/ssl.h)
+SET(openwsmand_SOURCES ${openwsmand_SOURCES} shttpd/defs.h shttpd/llist.h shttpd/shttpd.h shttpd/std_includes.h shttpd/io.h shttpd/md5.h shttpd/ssl.h)
SET(openwsmand_SOURCES ${openwsmand_SOURCES} shttpd/compat_unix.h shttpd/compat_win32.h shttpd/compat_rtems.h shttpd/adapter.h)
SET(openwsmand_SOURCES ${openwsmand_SOURCES} wsmand-listener.h wsmand-daemon.c wsmand-daemon.h wsmand-listener.c)
SET(openwsmand_SOURCES ${openwsmand_SOURCES} gss.c wsmand.c)
diff --git a/src/server/Makefile.am b/src/server/Makefile.am
index 4c11de7..b050f65 100644
--- a/src/server/Makefile.am
+++ b/src/server/Makefile.am
@@ -28,16 +28,16 @@
sbin_PROGRAMS = openwsmand
openwsmand_SOURCES = \
+ shttpd/adapter.c \
shttpd/string.c \
shttpd/shttpd.c \
shttpd/auth.c \
shttpd/md5.c \
- shttpd/adapter.c \
shttpd/cgi.c \
- shttpd/mime_type.c shttpd/config.c shttpd/io_socket.c \
- shttpd/io_ssl.c shttpd/io_emb.c shttpd/compat_unix.c shttpd/io_dir.c shttpd/io_file.c \
- shttpd/shttpd_defs.h shttpd/llist.h shttpd/shttpd.h shttpd/std_includes.h shttpd/io.h shttpd/md5.h shttpd/ssl.h \
- shttpd/compat_unix.h shttpd/compat_win32.h shttpd/compat_rtems.h shttpd/adapter.h\
+ shttpd/io_socket.c \
+ shttpd/io_ssl.c shttpd/io_emb.c shttpd/compat_unix.c shttpd/io_dir.c shttpd/io_file.c shttpd/log.c \
+ shttpd/llist.h shttpd/shttpd.h shttpd/std_includes.h shttpd/io.h shttpd/md5.h shttpd/ssl.h shttpd/adapter.h \
+ shttpd/compat_unix.h shttpd/compat_win32.h shttpd/compat_rtems.h \
wsmand-listener.h \
wsmand-daemon.c \
wsmand-daemon.h \
diff --git a/src/server/shttpd/adapter.c b/src/server/shttpd/adapter.c
index 58a2d9a..65215c4 100644
--- a/src/server/shttpd/adapter.c
+++ b/src/server/shttpd/adapter.c
@@ -1,6 +1,6 @@
-#include "shttpd_defs.h"
+#include "defs.h"
#include "adapter.h"
#ifdef SHTTPD_GSS
void getGssName(struct conn *c, char **user);
diff --git a/src/server/shttpd/auth.c b/src/server/shttpd/auth.c
index cf70d23..1e07485 100644
--- a/src/server/shttpd/auth.c
+++ b/src/server/shttpd/auth.c
@@ -8,10 +8,7 @@
* this stuff is worth it, you can buy me a beer in return.
*/
-#include "shttpd_defs.h"
-#ifdef SHTTPD_GSS
-void do_gss(struct conn *c);
-#endif
+#include "defs.h"
#if !defined(NO_AUTH)
/*
@@ -161,16 +158,18 @@
now - strtoul(dig->nonce, NULL, 10) > 3600 */)
return (0);
- md5(a2, &known_http_methods[method], &digest->uri, NULL);
+ md5(a2, &_shttpd_known_http_methods[method], &digest->uri, NULL);
vec_a2.ptr = a2;
vec_a2.len = sizeof(a2);
md5(resp, ha1, &digest->nonce, &digest->nc,
&digest->cnonce, &digest->qop, &vec_a2, NULL);
+ DBG(("%s: uri [%.*s] expected_resp [%.*s] resp [%.*s]",
+ "check_password", digest->uri.len, digest->uri.ptr,
+ 32, resp, digest->resp.len, digest->resp.ptr));
return (!memcmp(resp, digest->resp.ptr, 32));
}
-#if 0
static FILE *
open_auth_file(struct shttpd_ctx *ctx, const char *path)
{
@@ -179,21 +178,32 @@
FILE *fp = NULL;
int fd;
- if (ctx->global_passwd_file) {
+ if (ctx->options[OPT_AUTH_GPASSWD] != NULL) {
/* Use global passwords file */
- snprintf(name, sizeof(name), "%s", ctx->global_passwd_file);
+ _shttpd_snprintf(name, sizeof(name), "%s",
+ ctx->options[OPT_AUTH_GPASSWD]);
} else {
- /* Try to find .htpasswd in requested directory */
+ /*
+ * Try to find .htpasswd in requested directory.
+ * Given the path, create the path to .htpasswd file
+ * in the same directory. Find the right-most
+ * directory separator character first. That would be the
+ * directory name. If directory separator character is not
+ * found, 'e' will point to 'p'.
+ */
for (p = path, e = p + strlen(p) - 1; e > p; e--)
if (IS_DIRSEP_CHAR(*e))
break;
- assert(IS_DIRSEP_CHAR(*e));
- (void) snprintf(name, sizeof(name), "%.*s/%s",
+ /*
+ * Make up the path by concatenating directory name and
+ * .htpasswd file name.
+ */
+ (void) _shttpd_snprintf(name, sizeof(name), "%.*s/%s",
(int) (e - p), p, HTPASSWD);
}
- if ((fd = my_open(name, O_RDONLY, 0)) == -1) {
+ if ((fd = _shttpd_open(name, O_RDONLY, 0)) == -1) {
DBG(("open_auth_file: open(%s)", name));
} else if ((fp = fdopen(fd, "r")) == NULL) {
DBG(("open_auth_file: fdopen(%s)", name));
@@ -202,7 +212,6 @@
return (fp);
}
-#endif
/*
* Parse the line from htpasswd file. Line should be in form of
@@ -237,149 +246,129 @@
static int
authorize_digest(struct conn *c, FILE *fp)
{
- struct vec *auth_vec = &c->ch.auth.v_vec;
- struct vec *user_vec = &c->ch.user.v_vec;
- struct vec user, domain, ha1;
- struct digest digest;
- int ok = 0;
- char line[256];
+ struct vec *auth_vec = &c->ch.auth.v_vec;
+ struct vec *user_vec = &c->ch.user.v_vec;
+ struct vec user, domain, ha1;
+ struct digest digest;
+ int ok = 0;
+ char line[256];
- parse_authorization_header(auth_vec, &digest);
- *user_vec = digest.user;
+ parse_authorization_header(auth_vec, &digest);
+ *user_vec = digest.user;
- while (fgets(line, sizeof(line), fp) != NULL) {
+ while (fgets(line, sizeof(line), fp) != NULL) {
- if (!parse_htpasswd_line(line, &user, &domain, &ha1))
- continue;
+ if (!parse_htpasswd_line(line, &user, &domain, &ha1))
+ continue;
- DBG(("[%.*s] [%.*s] [%.*s]", user.len, user.ptr,
- domain.len, domain.ptr, ha1.len, ha1.ptr));
+ DBG(("[%.*s] [%.*s] [%.*s]", user.len, user.ptr,
+ domain.len, domain.ptr, ha1.len, ha1.ptr));
- if (vcmp(user_vec, &user) && !memcmp(c->ctx->auth_realm,
- domain.ptr, domain.len)) {
- ok = check_password(c->method, &ha1, &digest);
- break;
- }
- }
+ if (vcmp(user_vec, &user) && !memcmp(c->ctx->options[OPT_AUTH_REALM],
+ domain.ptr, domain.len)) {
+ ok = check_password(c->method, &ha1, &digest);
+ break;
+ }
+ }
- return (ok);
+ return (ok);
}
int
-check_authorization(struct conn *c, const char *path)
+_shttpd_check_authorization(struct conn *c, const char *path)
{
- FILE *fp = NULL;
- int authorized = 0;
- struct vec *auth_vec = &c->ch.auth.v_vec;
+ FILE *fp = NULL;
+ int authorized = 0;
+ struct vec *auth_vec = &c->ch.auth.v_vec;
-#ifdef EMBEDDED
- struct llhead *lp;
- struct uri_auth *auth;
- int digest = 0, basic = 0;
-#ifdef SHTTPD_GSS
- int kerberos = 0;
-#endif
+ struct llhead *lp;
+ struct uri_auth *auth;
+ int digest = 0, basic = 0;
- basic_auth_callback cb = NULL;
- char *p, *pp;
- /* Check, is this URL protected by shttpd_protect_url() */
-#ifdef SHTTPD_GSS
- /* already authenticated */
- if(c->gss_ctx != GSS_C_NO_CONTEXT)
- return 1;
-#endif
+ basic_auth_callback cb = NULL;
+ char *p, *pp;
- LL_FOREACH(&c->ctx->uri_auths, lp) {
- auth = LL_ENTRY(lp, struct uri_auth, link);
- if (!strncmp(c->uri, auth->uri, strlen(c->uri))) {
-#ifdef SHTTPD_GSS
- if (!strncasecmp(auth_vec->ptr, "Kerberos ", 9))
- {
- kerberos = 1;
- }
-#endif
- if (auth->type == DIGEST_AUTH &&
- auth_vec->len > 20 &&
- !strncasecmp(auth_vec->ptr, "Digest ", 7)) {
- fp = fopen(auth->file_name, "r");
- digest = 1;
- }
- if (auth->type == BASIC_AUTH &&
- auth_vec->len > 10 &&
- !strncasecmp(auth_vec->ptr, "Basic ", 6)) {
- cb = (int (*)(char *, char *)) auth->callback.v_func;
- basic = 1;
- }
- break;
- }
- }
- if (lp == &c->ctx->uri_auths) //not a protected uri
- return 1;
-#ifdef SHTTPD_GSS
- if(kerberos == 1)
- {
- do_gss(c);
- return 2;
- }
-#endif
- if (digest == 1) {
- if (fp != NULL) {
- authorized = authorize_digest(c, fp);
- (void) fclose(fp);
- return (authorized);
- } else
- return (0);
+ /* Check, is this URL protected by shttpd_protect_url() */
+
+ LL_FOREACH(&c->ctx->uri_auths, lp) {
+ auth = LL_ENTRY(lp, struct uri_auth, link);
+ if (!strncmp(c->uri, auth->uri, strlen(c->uri))) {
+ if (auth->type == DIGEST_AUTH &&
+ auth_vec->len > 20 &&
+ !strncasecmp(auth_vec->ptr, "Digest ", 7)) {
+ fp = fopen(auth->file_name, "r");
+ digest = 1;
+ }
+ if (auth->type == BASIC_AUTH &&
+ auth_vec->len > 10 &&
+ !strncasecmp(auth_vec->ptr, "Basic ", 6)) {
+ cb = (int (*)(char *, char *)) auth->callback.v_func;
+ basic = 1;
+ }
+ break;
+ }
+ }
+ if (lp == &c->ctx->uri_auths) { //not a protected uri
+ return 1;
}
- if (basic == 1) {
- char buf[4096];
- int l;
-
- p = (char *) auth_vec->ptr + 5;
- while ((*p == ' ') || (*p == '\t')) {
- p++;
- }
- pp = p;
- while ((*p != ' ') && (*p != '\t') && (*p != '\r')
- && (*p != '\n') && (*p != 0)) {
- p++;
- }
-
- if (pp == p) {
- return 0;
- }
- *p = 0;
-
- l = ws_base64_decode(pp, p - pp, buf, 4095);
- if (l <= 0) {
- return 0;
- }
-
- buf[l] = 0;
- p = buf;
- pp = p;
- p = strchr(p, ':');
- if (p == NULL) {
- return 0;
- }
- *p++ = 0;
- authorized = cb(pp, p);
- } else {
+ if (digest == 1) {
return 0;
- }
+ if (fp != NULL) {
+ authorized = authorize_digest(c, fp);
+ (void) fclose(fp);
+ return (authorized);
+ } else
+ return (0);
+ }
-#endif /* EMBEDDED */
+ if (basic == 1) {
+ char buf[4096];
+ int l;
+
+ p = (char *) auth_vec->ptr + 5;
+ while ((*p == ' ') || (*p == '\t')) {
+ p++;
+ }
+ pp = p;
+ while ((*p != ' ') && (*p != '\t') && (*p != '\r')
+ && (*p != '\n') && (*p != 0)) {
+ p++;
+ }
+
+ if (pp == p) {
+ return 0;
+ }
+ *p = 0;
+
+ l = ws_base64_decode(pp, p - pp, buf, 4095);
+ if (l <= 0) {
+ return 0;
+ }
+
+ buf[l] = 0;
+ p = buf;
+ pp = p;
+ p = strchr(p, ':');
+ if (p == NULL) {
+ return 0;
+ }
+ *p++ = 0;
+ authorized = cb(pp, p);
+ } else {
+ return 0;
+ }
return (authorized);
}
int
-is_authorized_for_put(struct conn *c)
+_shttpd_is_authorized_for_put(struct conn *c)
{
FILE *fp;
int ret = 0;
- if ((fp = fopen(c->ctx->put_auth_file, "r")) != NULL) {
+ if ((fp = fopen(c->ctx->options[OPT_AUTH_PUT], "r")) != NULL) {
ret = authorize_digest(c, fp);
(void) fclose(fp);
}
@@ -387,73 +376,48 @@
return (ret);
}
-int
-send_authorization_request(struct conn *c)
+
+void
+_shttpd_send_authorization_request(struct conn *c)
{
-#define BUFSIZE 512
- char buf[BUFSIZE];
- char *bufptr = buf;
- int n;
- size_t remaining = BUFSIZE;
- int b = 0, d = 0;
+ char buf[512];
+ int n = 0;
+ int b = 0, d = 0;
- struct llhead *lp;
- struct uri_auth *auth;
+ struct llhead *lp;
+ struct uri_auth *auth;
- n = snprintf(bufptr, remaining, "Unauthorized\r\n");
- bufptr += n;
- remaining -= n;
- LL_FOREACH(&c->ctx->uri_auths, lp) {
- auth = LL_ENTRY(lp, struct uri_auth, link);
- if (auth->type == DIGEST_AUTH && d == 0) {
- if (b) {
- n = snprintf(bufptr, remaining, "\r\n");
- if (n >= remaining) {
- return -1;
+ n = snprintf(buf, sizeof(buf), "Unauthorized\r\n");
+ LL_FOREACH(&c->ctx->uri_auths, lp) {
+ auth = LL_ENTRY(lp, struct uri_auth, link);
+ if (auth->type == DIGEST_AUTH && d == 0) {
+ if (b ) {
+ n += snprintf(buf +n, sizeof(buf) - n, "\r\n");
}
- bufptr += n;
- remaining -= n;
- }
- n = snprintf(bufptr, remaining,
- "WWW-Authenticate: Digest qop=\"auth\", realm=\"%s\", "
- "nonce=\"%lu\"", c->ctx->auth_realm, (unsigned long) current_time);
- if (n >= remaining) {
- return -1;
- }
- bufptr += n;
- remaining -= n;
- d = 1;
- }
- if (auth->type == BASIC_AUTH && b == 0) {
- if (d) {
- n = snprintf(bufptr, remaining, "\r\n");
- if (n >= remaining) {
- return -1;
+ n += snprintf(buf +n, sizeof(buf) - n,
+ "WWW-Authenticate: Digest qop=\"auth\", realm=\"%s\", "
+ "nonce=\"%lu\"", c->ctx->options[OPT_AUTH_REALM], (unsigned long) _shttpd_current_time);
+ d = 1;
+ }
+ if (auth->type == BASIC_AUTH && b == 0) {
+ if (d) {
+ n += snprintf(buf +n, sizeof(buf) - n, "\r\n");
}
- bufptr += n;
- remaining -= n;
- }
- n = snprintf(bufptr, remaining,
- "WWW-Authenticate: Basic realm=\"%s\"", c->ctx->auth_realm);
- if (n >= remaining) {
- return -1;
- }
- bufptr += n;
- remaining -= n;
- b = 1;
- }
- }
+ n += snprintf(buf +n, sizeof(buf) - n,
+ "WWW-Authenticate: Basic realm=\"%s\"", c->ctx->options[OPT_AUTH_REALM]);
+ b = 1;
+ }
+ }
- send_server_error(c, 401, buf);
-#undef BUFSIZE
- return 0;
+ _shttpd_send_server_error(c, 401, buf);
}
+
/*
* Edit the passwords file.
*/
int
-edit_passwords(const char *fname, const char *domain,
+_shttpd_edit_passwords(const char *fname, const char *domain,
const char *user, const char *pass)
{
int ret = EXIT_SUCCESS, found = 0;
@@ -461,7 +425,7 @@
char line[512], tmp[FILENAME_MAX], ha1[32];
FILE *fp = NULL, *fp2 = NULL;
- (void) snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
+ (void) _shttpd_snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
/* Create the file if does not exist */
if ((fp = fopen(fname, "a+")))
@@ -469,9 +433,11 @@
/* Open the given file and temporary file */
if ((fp = fopen(fname, "r")) == NULL)
- elog(E_FATAL, 0, "Cannot open %s: %s", fname, strerror(errno));
+ _shttpd_elog(E_FATAL, NULL,
+ "Cannot open %s: %s", fname, strerror(errno));
else if ((fp2 = fopen(tmp, "w+")) == NULL)
- elog(E_FATAL, 0, "Cannot open %s: %s", tmp, strerror(errno));
+ _shttpd_elog(E_FATAL, NULL,
+ "Cannot open %s: %s", tmp, strerror(errno));
p.ptr = pass;
p.len = strlen(pass);
@@ -512,8 +478,8 @@
(void) fclose(fp2);
/* Put the temp file in place of real file */
- (void) remove(fname);
- (void) rename(tmp, fname);
+ (void) _shttpd_remove(fname);
+ (void) _shttpd_rename(tmp, fname);
return (ret);
}
diff --git a/src/server/shttpd/cgi.c b/src/server/shttpd/cgi.c
index 2e22f28..f9623dc 100644
--- a/src/server/shttpd/cgi.c
+++ b/src/server/shttpd/cgi.c
@@ -8,7 +8,7 @@
* this stuff is worth it, you can buy me a beer in return.
*/
-#include "shttpd_defs.h"
+#include "defs.h"
#if !defined(NO_CGI)
struct env_block {
@@ -18,94 +18,6 @@
int nvars; /* Number of variables */
};
-/*
- * UNIX socketpair() implementation. Why? Because Windows does not have it.
- * Return 0 on success, -1 on error.
- */
-static int
-my_socketpair(struct conn *c, int sp[2])
-{
-
-
-#ifdef ENABLE_IPV6
- struct sockaddr_in6 sa;
-#else
- struct sockaddr_in sa;
-#endif
- int sock = -1, ret = -1;
- socklen_t len = sizeof(sa);
-
- (void) memset(&sa, 0, sizeof(sa));
-#ifdef ENABLE_IPV6
- sa.sin6_family = AF_INET6;
- sa.sin6_addr = in6addr_loopback;
- sa.sin6_port = htons(0);
-
-#else
- sa.sin_family = AF_INET;
- sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- sa.sin_port = htons(0);
-#endif
-
-#ifdef ENABLE_IPV6
- if (!wsmand_options_get_use_ipv6()
- || (sock = socket(AF_INET6, SOCK_STREAM, 0)) == -1) {
- if(!wsmand_options_get_use_ipv4()
- || (sock = socket(AF_INET,SOCK_STREAM,0)) == -1)
- elog(E_LOG, c, "mysocketpair: socket(): %d", ERRNO);
- }
-
-#else
- if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
- elog(E_LOG, c, "mysocketpair: socket(): %d", ERRNO);
- }
-
-#endif
- else if (bind(sock, (struct sockaddr *) &sa, len) != 0) {
- elog(E_LOG, c, "mysocketpair: bind(): %d", ERRNO);
- (void) closesocket(sock);
- } else if (listen(sock, 1) != 0) {
- elog(E_LOG, c, "mysocketpair: listen(): %d", ERRNO);
- (void) closesocket(sock);
- } else if (getsockname(sock, (struct sockaddr *) &sa, &len) != 0) {
- elog(E_LOG, c, "mysocketpair: getsockname(): %d", ERRNO);
- (void) closesocket(sock);
- }
-
-#ifdef ENABLE_IPV6
- else if ((sp[0] = socket(AF_INET6, SOCK_STREAM, 6)) == -1) {
- elog(E_LOG, c, "mysocketpair: socket(): %d", ERRNO);
- (void) closesocket(sock);
- }
-#else
- else if ((sp[0] = socket(AF_INET, SOCK_STREAM, 6)) == -1) {
- elog(E_LOG, c, "mysocketpair: socket(): %d", ERRNO);
- (void) closesocket(sock);
- }
-#endif
-
- else if (connect(sp[0], (struct sockaddr *) &sa, len) != 0) {
- elog(E_LOG, c, "mysocketpair: connect(): %d", ERRNO);
- (void) closesocket(sock);
- (void) closesocket(sp[0]);
- } else if ((sp[1] = accept(sock,(struct sockaddr *) &sa, &len)) == -1) {
- elog(E_LOG, c, "mysocketpair: accept(): %d", ERRNO);
- (void) closesocket(sock);
- (void) closesocket(sp[0]);
- } else {
- /* Success */
- ret = 0;
- (void) closesocket(sock);
- }
-
-#ifndef _WIN32
- (void) fcntl(sp[0], F_SETFD, FD_CLOEXEC);
- (void) fcntl(sp[1], F_SETFD, FD_CLOEXEC);
-#endif /* _WIN32*/
-
- return (ret);
-}
-
static void
addenv(struct env_block *block, const char *fmt, ...)
{
@@ -136,13 +48,13 @@
/* Find where this header ends. Remember where value starts */
for (p = s, v = NULL; p < e && *p != '\n'; p++)
- if (v == NULL && *p == ':')
+ if (v == NULL && *p == ':')
v = p;
/* 2 null terminators and "HTTP_" */
space = (sizeof(b->buf) - b->len) - (2 + 5);
assert(space >= 0);
-
+
/* Copy header if enough space in the environment block */
if (v > s && p > v + 2 && space > p - s) {
@@ -181,42 +93,35 @@
struct env_block *blk)
{
const struct headers *h = &c->ch;
- const char *s;
+ const char *s, *fname, *root = c->ctx->options[OPT_ROOT];
size_t len;
blk->len = blk->nvars = 0;
+ /* SCRIPT_FILENAME */
+ fname = prog;
+ if ((s = strrchr(prog, '/')))
+ fname = s + 1;
+
/* Prepare the environment block */
addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1");
addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1");
addenv(blk, "%s", "REDIRECT_STATUS=200"); /* PHP */
addenv(blk, "SERVER_PORT=%d", c->loc_port);
- addenv(blk, "SERVER_NAME=%s", c->ctx->auth_realm);
- addenv(blk, "SERVER_ROOT=%s", c->ctx->document_root);
- addenv(blk, "DOCUMENT_ROOT=%s", c->ctx->document_root);
- addenv(blk, "REQUEST_METHOD=%s", known_http_methods[c->method].ptr);
-#ifdef ENABLE_IPV6
- if (wsmand_options_get_use_ipv6()) {
- char str[INET6_ADDRSTRLEN];
- inet_ntop( AF_INET6,&c->sa.u.sin.sin6_addr, str, sizeof(str));
- addenv(blk, "REMOTE_ADDR=%s", str);
- addenv(blk, "REMOTE_PORT=%hu", ntohs(c->sa.u.sin.sin6_port));
- }
- else {
-#endif
- addenv(blk, "REMOTE_ADDR=%s", inet_ntoa(c->sa.u.sin.sin_addr));
- addenv(blk, "REMOTE_PORT=%hu", ntohs(c->sa.u.sin.sin_port));
-#ifdef ENABLE_IPV6
- }
-#endif
-
+ addenv(blk, "SERVER_NAME=%s", c->ctx->options[OPT_AUTH_REALM]);
+ addenv(blk, "SERVER_ROOT=%s", root);
+ addenv(blk, "DOCUMENT_ROOT=%s", root);
+ addenv(blk, "REQUEST_METHOD=%s",
+ _shttpd_known_http_methods[c->method].ptr);
+ addenv(blk, "REMOTE_ADDR=%s", inet_ntoa(c->sa.u.sin.sin_addr));
+ addenv(blk, "REMOTE_PORT=%hu", ntohs(c->sa.u.sin.sin_port));
addenv(blk, "REQUEST_URI=%s", c->uri);
- addenv(blk, "SCRIPT_NAME=%s", prog + strlen(c->ctx->document_root));
- addenv(blk, "SCRIPT_FILENAME=%s", prog); /* PHP */
+ addenv(blk, "SCRIPT_NAME=%s", prog + strlen(root));
+ addenv(blk, "SCRIPT_FILENAME=%s", fname); /* PHP */
addenv(blk, "PATH_TRANSLATED=%s", prog);
if (h->ct.v_vec.len > 0)
- addenv(blk, "CONTENT_TYPE=%.*s",
+ addenv(blk, "CONTENT_TYPE=%.*s",
h->ct.v_vec.len, h->ct.v_vec.ptr);
if (c->query != NULL)
@@ -251,7 +156,7 @@
}
/* Add user-specified variables */
- s = c->ctx->cgi_vars;
+ s = c->ctx->options[OPT_CGI_ENVIRONMENT];
FOR_EACH_WORD_IN_LIST(s, len)
addenv(blk, "%.*s", len, s);
@@ -276,7 +181,7 @@
}
int
-run_cgi(struct conn *c, const char *prog)
+_shttpd_run_cgi(struct conn *c, const char *prog)
{
struct env_block blk;
char dir[FILENAME_MAX], *p;
@@ -286,16 +191,17 @@
pair[0] = pair[1] = -1;
/* CGI must be executed in its own directory */
- (void) snprintf(dir, sizeof(dir), "%s", prog);
+ (void) _shttpd_snprintf(dir, sizeof(dir), "%s", prog);
for (p = dir + strlen(dir) - 1; p > dir; p--)
if (*p == '/') {
*p++ = '\0';
break;
}
-
- if (my_socketpair(c, pair) != 0) {
+
+ if (shttpd_socketpair(pair) != 0) {
ret = -1;
- } else if (spawn_process(c, prog, blk.buf, blk.vars, pair[1], dir)) {
+ } else if (_shttpd_spawn_process(c,
+ prog, blk.buf, blk.vars, pair[1], dir)) {
ret = -1;
(void) closesocket(pair[0]);
(void) closesocket(pair[1]);
@@ -308,13 +214,13 @@
}
void
-do_cgi(struct conn *c)
+_shttpd_do_cgi(struct conn *c)
{
DBG(("running CGI: [%s]", c->uri));
assert(c->loc.io.size > CGI_REPLY_LEN);
memcpy(c->loc.io.buf, CGI_REPLY, CGI_REPLY_LEN);
c->loc.io.head = c->loc.io.tail = c->loc.io.total = CGI_REPLY_LEN;
- c->loc.io_class = &io_cgi;
+ c->loc.io_class = &_shttpd_io_cgi;
c->loc.flags = FLAG_R;
if (c->method == METHOD_POST)
c->loc.flags |= FLAG_W;
diff --git a/src/server/shttpd/compat_rtems.c b/src/server/shttpd/compat_rtems.c
index 7ccbf38..2a01a07 100644
--- a/src/server/shttpd/compat_rtems.c
+++ b/src/server/shttpd/compat_rtems.c
@@ -18,7 +18,7 @@
* INCLUDED MODULES
**********************************************************************/
#include <rtems.h>
-#include "shttpd_defs.h"
+#include "defs.h"
#define MAX_WEB_BASE_PATH_LENGTH 256
#define MIN_SHTTPD_STACK (8*1024)
diff --git a/src/server/shttpd/compat_unix.c b/src/server/shttpd/compat_unix.c
index 00e00e3..a0d5070 100644
--- a/src/server/shttpd/compat_unix.c
+++ b/src/server/shttpd/compat_unix.c
@@ -8,52 +8,52 @@
* this stuff is worth it, you can buy me a beer in return.
*/
-#include "shttpd_defs.h"
+#include "defs.h"
-void
-set_close_on_exec(int fd)
+void
+_shttpd_set_close_on_exec(int fd)
{
(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
}
int
-my_stat(const char *path, struct stat *stp)
+_shttpd_stat(const char *path, struct stat *stp)
{
return (stat(path, stp));
}
int
-my_open(const char *path, int flags, int mode)
+_shttpd_open(const char *path, int flags, int mode)
{
return (open(path, flags, mode));
}
int
-my_remove(const char *path)
+_shttpd_remove(const char *path)
{
return (remove(path));
}
int
-my_rename(const char *path1, const char *path2)
+_shttpd_rename(const char *path1, const char *path2)
{
return (rename(path1, path2));
}
int
-my_mkdir(const char *path, int mode)
+_shttpd_mkdir(const char *path, int mode)
{
return (mkdir(path, mode));
}
char *
-my_getcwd(char *buffer, int maxlen)
+_shttpd_getcwd(char *buffer, int maxlen)
{
return (getcwd(buffer, maxlen));
}
int
-set_non_blocking_mode(int fd)
+_shttpd_set_non_blocking_mode(int fd)
{
int ret = -1;
int flags;
@@ -71,22 +71,24 @@
#ifndef NO_CGI
int
-spawn_process(struct conn *c, const char *prog, char *envblk,
+_shttpd_spawn_process(struct conn *c, const char *prog, char *envblk,
char *envp[], int sock, const char *dir)
{
- int ret;
- pid_t pid;
+ int ret;
+ pid_t pid;
+ const char *p, *interp = c->ctx->options[OPT_CGI_INTERPRETER];
envblk = NULL; /* unused */
if ((pid = vfork()) == -1) {
ret = -1;
- elog(E_LOG, c, "redirect: fork: %s", strerror(errno));
+ _shttpd_elog(E_LOG, c, "redirect: fork: %s", strerror(errno));
} else if (pid == 0) {
/* Child */
+
(void) chdir(dir);
(void) dup2(sock, 0);
(void) dup2(sock, 1);
@@ -96,19 +98,22 @@
if (c->ctx->error_log)
(void) dup2(fileno(c->ctx->error_log), 2);
+ if ((p = strrchr(prog, '/')) != NULL)
+ p++;
+ else
+ p = prog;
+
/* Execute CGI program */
- if (c->ctx->cgi_interpreter == NULL) {
- (void) execle(prog, prog, NULL, envp);
- elog(E_FATAL, c, "redirect: exec(%s)", prog);
+ if (interp == NULL) {
+ (void) execle(p, p, NULL, envp);
+ _shttpd_elog(E_FATAL, c, "redirect: exec(%s)", prog);
} else {
- (void) execle(c->ctx->cgi_interpreter,
- c->ctx->cgi_interpreter, prog, NULL, envp);
- elog(E_FATAL, c, "redirect: exec(%s %s)",
- c->ctx->cgi_interpreter, prog);
+ (void) execle(interp, interp, p, NULL, envp);
+ _shttpd_elog(E_FATAL, c, "redirect: exec(%s %s)",
+ interp, prog);
}
/* UNREACHED */
- ret = -1;
exit(EXIT_FAILURE);
} else {
diff --git a/src/server/shttpd/compat_unix.h b/src/server/shttpd/compat_unix.h
index aa498ef..53c7f03 100644
--- a/src/server/shttpd/compat_unix.h
+++ b/src/server/shttpd/compat_unix.h
@@ -20,16 +20,16 @@
#include <unistd.h>
#include <dirent.h>
#include <dlfcn.h>
-#ifndef SSL_LIB
+
+#if !defined(NO_THREADS)
+#include "pthread.h"
+#define _beginthread(a, b, c) do { pthread_t tid; \
+ pthread_create(&tid, NULL, (void *(*)(void *))a, c); } while (0)
+#endif /* !NO_THREADS */
+
#define SSL_LIB "libssl.so"
-#endif
#define DIRSEP '/'
#define IS_DIRSEP_CHAR(c) ((c) == '/')
#define O_BINARY 0
#define closesocket(a) close(a)
#define ERRNO errno
-#define NO_GUI
-
-#define InitializeCriticalSection(x) /* FIXME UNIX version is not MT safe */
-#define EnterCriticalSection(x)
-#define LeaveCriticalSection(x)
diff --git a/src/server/shttpd/compat_win32.c b/src/server/shttpd/compat_win32.c
index 9b11cc7..36717f8 100644
--- a/src/server/shttpd/compat_win32.c
+++ b/src/server/shttpd/compat_win32.c
@@ -8,523 +8,11 @@
* this stuff is worth it, you can buy me a beer in return.
*/
-#include "shttpd_defs.h"
+#include "defs.h"
-static const char *config_file = CONFIG;
-
-#if !defined(NO_GUI)
-
-static HICON hIcon; /* SHTTPD icon handle */
-HWND hLog; /* Log window */
-
-/*
- * Dialog box control IDs
- */
-#define ID_GROUP 100
-#define ID_SAVE 101
-#define ID_STATUS 102
-#define ID_STATIC 103
-#define ID_SETTINGS 104
-#define ID_QUIT 105
-#define ID_TRAYICON 106
-#define ID_TIMER 107
-#define ID_ICON 108
-#define ID_ADVANCED 109
-#define ID_SHOWLOG 110
-#define ID_LOG 111
-
-#define ID_USER 200
-#define ID_DELTA 1000
-
-static void
-run_server(void *param)
-{
- struct shttpd_ctx *ctx = param;
-
- open_listening_ports(ctx);
-
- while (WaitForSingleObject(ctx->ev[0], 0) != WAIT_OBJECT_0)
- shttpd_poll(ctx, 1000);
-
- SetEvent(ctx->ev[1]);
- shttpd_fini(ctx);
-}
-
-/*
- * Save the configuration back into config file
- */
-static void
-save_config(HWND hDlg, FILE *fp)
-{
- const struct opt *opt;
- char text[FILENAME_MAX];
- int id;
-
- if (fp == NULL)
- elog(E_FATAL, NULL, "save_config: cannot open %s", config_file);
-
- for (opt = options; opt->name != NULL; opt++) {
- id = ID_USER + (opt - options); /* Control ID */
-
- /* Do not save if the text is the same as default */
-
- if (opt->flags & OPT_BOOL)
- (void) fprintf(fp, "%s\t%d\n",
- opt->name, IsDlgButtonChecked(hDlg, id));
- else if (GetDlgItemText(hDlg, id, text, sizeof(text)) != 0 &&
- (opt->def == NULL || strcmp(text, opt->def) != 0))
- (void) fprintf(fp, "%s\t%s\n", opt->name, text);
- }
-
- (void) fclose(fp);
-}
-
-static void
-set_control_values(HWND hDlg, const struct shttpd_ctx *ctx)
-{
- const struct opt *opt;
- const union variant *v;
- char buf[FILENAME_MAX];
- int id;
-
- for (opt = options; opt->name != NULL; opt++) {
- id = ID_USER + (opt - options);
- v = (union variant *) ((char *) ctx + opt->ofs);
- if (opt->flags & OPT_BOOL) {
- CheckDlgButton(hDlg, id,
- v->v_int ? BST_CHECKED : BST_UNCHECKED);
- } else if (opt->flags & OPT_INT) {
- snprintf(buf, sizeof(buf), "%d", v->v_int);
- SetDlgItemText(hDlg, id, buf);
- } else {
- SetDlgItemText(hDlg, id, v->v_str);
- }
- }
-
-}
-
-static BOOL CALLBACK
-DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- static struct shttpd_ctx *ctx, **pctx;
- HANDLE ev;
- const struct opt *opt;
- DWORD tid;
- int id, up;
- char text[256];
-
- switch (msg) {
-
- case WM_CLOSE:
- KillTimer(hDlg, ID_TIMER);
- DestroyWindow(hDlg);
- break;
-
- case WM_COMMAND:
- switch (LOWORD(wParam)) {
- case ID_SAVE:
- EnableWindow(GetDlgItem(hDlg, ID_SAVE), FALSE);
- save_config(hDlg, fopen(config_file, "w+"));
- ev = ctx->ev[1];
- SetEvent(ctx->ev[0]);
- WaitForSingleObject(ev, INFINITE);
- *pctx = ctx = init_from_argc_argv(config_file, 0, NULL);
- open_listening_ports(ctx);
- _beginthread(run_server, 0, ctx);
- EnableWindow(GetDlgItem(hDlg, ID_SAVE), TRUE);
-
- break;
- }
-
- id = ID_USER + ID_DELTA;
- for (opt = options; opt->name != NULL; opt++, id++)
- if (LOWORD(wParam) == id) {
- OPENFILENAME of;
- BROWSEINFO bi;
- char path[FILENAME_MAX] = "";
-
- memset(&of, 0, sizeof(of));
- of.lStructSize = sizeof(of);
- of.hwndOwner = (HWND) hDlg;
- of.lpstrFile = path;
- of.nMaxFile = sizeof(path);
- of.lpstrInitialDir = ctx->document_root;
- of.Flags = OFN_CREATEPROMPT | OFN_NOCHANGEDIR;
-
- memset(&bi, 0, sizeof(bi));
- bi.hwndOwner = (HWND) hDlg;
- bi.lpszTitle = "Choose WWW root directory:";
- bi.ulFlags = BIF_RETURNONLYFSDIRS;
-
- if (opt->flags & OPT_DIR)
- SHGetPathFromIDList(
- SHBrowseForFolder(&bi), path);
- else
- GetOpenFileName(&of);
-
- if (path[0] != '\0')
- SetWindowText(GetDlgItem(hDlg,
- id - ID_DELTA), path);
- }
-
- break;
-
- case WM_INITDIALOG:
- pctx = (struct shttpd_ctx **) lParam;
- ctx = *pctx;
- SendMessage(hDlg,WM_SETICON,(WPARAM)ICON_SMALL,(LPARAM)hIcon);
- SendMessage(hDlg,WM_SETICON,(WPARAM)ICON_BIG,(LPARAM)hIcon);
- SetWindowText(hDlg, "SHTTPD settings");
- SetFocus(GetDlgItem(hDlg, ID_SAVE));
- set_control_values(hDlg, ctx);
- break;
- default:
- break;
- }
-
- return FALSE;
-}
-
-static void *
-align(void *ptr, DWORD alig)
-{
- ULONG ul = (ULONG) ptr;
-
- ul += alig;
- ul &= ~alig;
-
- return ((void *) ul);
-}
-
-
-static void
-add_control(unsigned char **mem, DLGTEMPLATE *dia, WORD type, DWORD id,
- DWORD style, WORD x, WORD y, WORD cx, WORD cy, const char *caption)
-{
- DLGITEMTEMPLATE *tp;
- LPWORD p;
-
- dia->cdit++;
-
- *mem = align(*mem, 3);
- tp = (DLGITEMTEMPLATE *) *mem;
-
- tp->id = (WORD)id;
- tp->style = style;
- tp->dwExtendedStyle = 0;
- tp->x = x;
- tp->y = y;
- tp->cx = cx;
- tp->cy = cy;
-
- p = align(*mem + sizeof(*tp), 1);
- *p++ = 0xffff;
- *p++ = type;
-
- while (*caption != '\0')
- *p++ = (WCHAR) *caption++;
- *p++ = 0;
- p = align(p, 1);
-
- *p++ = 0;
- *mem = (unsigned char *) p;
-}
-
-static void
-show_settings_dialog(struct shttpd_ctx **ctxp)
-{
-#define HEIGHT 15
-#define WIDTH 400
-#define LABEL_WIDTH 70
-
- unsigned char mem[4096], *p;
- DWORD style;
- DLGTEMPLATE *dia = (DLGTEMPLATE *) mem;
- WORD cl, x, y, width, nelems = 0;
- const struct opt *opt;
- static int guard;
-
- static struct {
- DLGTEMPLATE template; /* 18 bytes */
- WORD menu, class;
- wchar_t caption[1];
- WORD fontsiz;
- wchar_t fontface[7];
- } dialog_header = {{WS_CAPTION | WS_POPUP | WS_SYSMENU | WS_VISIBLE |
- DS_SETFONT | WS_DLGFRAME, WS_EX_TOOLWINDOW,
- 0, 200, 200, WIDTH, 0}, 0, 0, L"", 8, L"Tahoma"};
-
- if (guard == 0)
- guard++;
- else
- return;
-
- (void) memset(mem, 0, sizeof(mem));
- (void) memcpy(mem, &dialog_header, sizeof(dialog_header));
- p = mem + sizeof(dialog_header);
-
- for (opt = options; opt->name != NULL; opt++) {
-
- style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
- x = 10 + (WIDTH / 2) * (nelems % 2);
- y = (nelems/2 + 1) * HEIGHT + 5;
- width = WIDTH / 2 - 20 - LABEL_WIDTH;
- if (opt->flags & OPT_INT) {
- style |= ES_NUMBER;
- cl = 0x81;
- style |= WS_BORDER | ES_AUTOHSCROLL;
- } else if (opt->flags & OPT_BOOL) {
- cl = 0x80;
- style |= BS_AUTOCHECKBOX;
- } else if (opt->flags & (OPT_DIR | OPT_FILE)) {
- style |= WS_BORDER | ES_AUTOHSCROLL;
- width -= 20;
- cl = 0x81;
- add_control(&p, dia, 0x80,
- ID_USER + ID_DELTA + (opt - options),
- WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
- (WORD) (x + width + LABEL_WIDTH + 5),
- y, 15, 12, "...");
- } else {
- cl = 0x81;
- style |= WS_BORDER | ES_AUTOHSCROLL;
- }
- add_control(&p, dia, 0x82, ID_STATIC, WS_VISIBLE | WS_CHILD,
- x, y, LABEL_WIDTH, HEIGHT, opt->desc);
- add_control(&p, dia, cl, ID_USER + (opt - options), style,
- (WORD) (x + LABEL_WIDTH), y, width, 12, "");
- nelems++;
- }
-
- y = (WORD) (((nelems + 1)/2 + 1) * HEIGHT + 5);
- add_control(&p, dia, 0x80, ID_GROUP, WS_CHILD | WS_VISIBLE |
- BS_GROUPBOX, 5, 5, WIDTH - 10, y, "Settings");
- y += 10;
- add_control(&p, dia, 0x80, ID_SAVE,
- WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
- WIDTH - 70, y, 65, 12, "Save Settings");
-#if 0
- add_control(&p, dia, 0x80, ID_ADVANCED,
- WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
- WIDTH - 190, y, 110, 12, "Show Advanced Settings >>");
-#endif
- add_control(&p, dia, 0x82, ID_STATIC,
- WS_CHILD | WS_VISIBLE | WS_DISABLED,
- 5, y, 180, 12,"SHTTPD v." VERSION
- " (http://shttpd.sourceforge.net)");
-
- dia->cy = ((nelems + 1)/2 + 1) * HEIGHT + 30;
- DialogBoxIndirectParam(NULL, dia, NULL, DlgProc, (LPARAM) ctxp);
- guard--;
-}
-
-static BOOL CALLBACK
-LogProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- static struct shttpd_ctx *ctx;
- static HWND hStatus;
- HWND hEdit;
- RECT rect, rect2, rect3, rect4;
- int len, up, widths[] = {120, 220, 330, 460, -1};
- char text[256], buf[1024 * 64];
-
- switch (msg) {
-
- case WM_CLOSE:
- KillTimer(hDlg, ID_TIMER);
- DestroyWindow(hDlg);
- break;
-
- case WM_APP:
- hEdit = GetDlgItem(hDlg, ID_LOG);
- len = GetWindowText(hEdit, buf, sizeof(buf));
- if (len > sizeof(buf) * 4 / 5)
- len = sizeof(buf) * 4 / 5;
- snprintf(buf + len, sizeof(buf) - len,
- "%s\r\n", (char *) lParam);
- SetWindowText(hEdit, buf);
- SendMessage(hEdit, WM_VSCROLL, SB_BOTTOM, 0);
- break;
-
- case WM_TIMER:
- /* Print statistics on a status bar */
- up = current_time - ctx->start_time;
- (void) snprintf(text, sizeof(text),
- " Up: %3d h %2d min %2d sec",
- up / 3600, up / 60 % 60, up % 60);
- SendMessage(hStatus, SB_SETTEXT, 0, (LPARAM) text);
- (void) snprintf(text, sizeof(text),
- " Requests: %u", ctx->nrequests);
- SendMessage(hStatus, SB_SETTEXT, 1, (LPARAM) text);
- (void) snprintf(text, sizeof(text),
- " Sent: %4.2f Mb", (double) ctx->out / 1048576);
- SendMessage(hStatus, SB_SETTEXT, 2, (LPARAM) text);
- (void) snprintf(text, sizeof(text),
- " Received: %4.2f Mb", (double) ctx->in / 1048576);
- SendMessage(hStatus, SB_SETTEXT, 3, (LPARAM) text);
- break;
-
- case WM_INITDIALOG:
- ctx = (struct shttpd_ctx *) lParam;
- SendMessage(hDlg,WM_SETICON,(WPARAM)ICON_SMALL,(LPARAM)hIcon);
- SendMessage(hDlg,WM_SETICON,(WPARAM)ICON_BIG,(LPARAM)hIcon);
- hStatus = CreateStatusWindow(WS_CHILD | WS_VISIBLE,
- "", hDlg, ID_STATUS);
- SendMessage(hStatus, SB_SETPARTS, 5, (LPARAM) widths);
- SendMessage(hStatus, SB_SETTEXT, 4, (LPARAM) " Running");
- SetWindowText(hDlg, "SHTTPD web server log");
- SetTimer(hDlg, ID_TIMER, 1000, NULL);
- GetWindowRect(GetDesktopWindow(), &rect3);
- GetWindowRect(hDlg, &rect4);
- GetClientRect(hDlg, &rect);
- GetClientRect(hStatus, &rect2);
- SetWindowPos(GetDlgItem(hDlg, ID_LOG), 0,
- 0, 0, rect.right, rect.bottom - rect2.bottom, 0);
- SetWindowPos(hDlg, HWND_TOPMOST,
- rect3.right - (rect4.right - rect4.left),
- rect3.bottom - (rect4.bottom - rect4.top) - 30,
- 0, 0, SWP_NOSIZE);
- SetFocus(hStatus);
- SendMessage(hDlg, WM_TIMER, 0, 0);
- hLog = hDlg;
- break;
- default:
- break;
- }
-
-
- return (FALSE);
-}
-
-static void
-show_log_window(struct shttpd_ctx *ctx)
-{
- unsigned char mem[4096], *p;
- DWORD style;
- DLGTEMPLATE *dia = (DLGTEMPLATE *) mem;
- WORD cl, x, y, width, nelems = 0;
-
- static struct {
- DLGTEMPLATE template; /* 18 bytes */
- WORD menu, class;
- wchar_t caption[1];
- WORD fontsiz;
- wchar_t fontface[7];
- } dialog_header = {{WS_CAPTION | WS_POPUP | WS_VISIBLE | WS_SYSMENU |
- DS_SETFONT | WS_DLGFRAME, WS_EX_TOOLWINDOW,
- 0, 200, 200, 400, 100}, 0, 0, L"", 8, L"Tahoma"};
-
- if (hLog != NULL)
- return;
-
- (void) memset(mem, 0, sizeof(mem));
- (void) memcpy(mem, &dialog_header, sizeof(dialog_header));
- p = mem + sizeof(dialog_header);
-
- add_control(&p, dia, 0x81, ID_LOG, WS_CHILD | WS_VISIBLE |
- WS_BORDER | WS_VSCROLL | ES_MULTILINE | ES_AUTOVSCROLL |
- ES_READONLY, 5, 5, WIDTH - 10, 60, "");
-
- DialogBoxIndirectParam(NULL, dia, NULL, LogProc, (LPARAM) ctx);
-
- hLog = NULL;
-}
-
-static LRESULT CALLBACK
-WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- static NOTIFYICONDATA ni;
- static struct shttpd_ctx *ctx;
- DWORD tid; /* Thread ID */
- HMENU hMenu;
- POINT pt;
-
- switch (msg) {
- case WM_CREATE:
- ctx = ((CREATESTRUCT *) lParam)->lpCreateParams;
- memset(&ni, 0, sizeof(ni));
- ni.cbSize = sizeof(ni);
- ni.uID = ID_TRAYICON;
- ni.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
- ni.hIcon = hIcon;
- ni.hWnd = hWnd;
- snprintf(ni.szTip, sizeof(ni.szTip), "SHTTPD web server");
- ni.uCallbackMessage = WM_USER;
- Shell_NotifyIcon(NIM_ADD, &ni);
- ctx->ev[0] = CreateEvent(0, TRUE, FALSE, 0);
- ctx->ev[1] = CreateEvent(0, TRUE, FALSE, 0);
- _beginthread(run_server, 0, ctx);
- break;
- case WM_CLOSE:
- Shell_NotifyIcon(NIM_DELETE, &ni);
- PostQuitMessage(0);
- break;
- case WM_COMMAND:
- switch (LOWORD(wParam)) {
- case ID_SETTINGS:
- show_settings_dialog(&ctx);
- break;
- case ID_QUIT:
- SendMessage(hWnd, WM_CLOSE, wParam, lParam);
- PostQuitMessage(0);
- break;
- case ID_SHOWLOG:
- show_log_window(ctx);
- break;
- }
- break;
- case WM_USER:
- switch (lParam) {
- case WM_RBUTTONUP:
- case WM_LBUTTONUP:
- case WM_LBUTTONDBLCLK:
- hMenu = CreatePopupMenu();
- AppendMenu(hMenu, 0, ID_SETTINGS, "Settings");
- AppendMenu(hMenu, 0, ID_SHOWLOG, "Show Log");
- AppendMenu(hMenu, 0, ID_QUIT, "Exit SHTTPD");
- GetCursorPos(&pt);
- TrackPopupMenu(hMenu, 0, pt.x, pt.y, 0, hWnd, NULL);
- DestroyMenu(hMenu);
- break;
- }
- break;
- }
-
- return (DefWindowProc(hWnd, msg, wParam, lParam));
-}
-
-int WINAPI
-WinMain(HINSTANCE h, HINSTANCE prev, char *cmdline, int show)
-{
- struct shttpd_ctx *ctx;
- WNDCLASS cls;
- HWND hWnd;
- MSG msg;
-
- ctx = init_from_argc_argv(config_file, 0, NULL);
- (void) memset(&cls, 0, sizeof(cls));
-
- hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(ID_ICON));
- if (hIcon == NULL)
- hIcon = LoadIcon(NULL, IDI_APPLICATION);
- cls.lpfnWndProc = (WNDPROC) WindowProc;
- cls.hIcon = hIcon;
- cls.lpszClassName = "shttpd v." VERSION;
-
- if (!RegisterClass(&cls))
- elog(E_FATAL, NULL, "RegisterClass: %d", ERRNO);
- else if ((hWnd = CreateWindow(cls.lpszClassName, "",WS_OVERLAPPEDWINDOW,
- 0, 0, 0, 0, NULL, NULL, NULL, ctx)) == NULL)
- elog(E_FATAL, NULL, "CreateWindow: %d", ERRNO);
-
- while (GetMessage(&msg, (HWND) NULL, 0, 0)) {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
-
- return (0);
-}
-#endif /* NO_GUI */
+static SERVICE_STATUS ss;
+static SERVICE_STATUS_HANDLE hStatus;
+static SERVICE_DESCRIPTION service_descr = {"Web server"};
static void
fix_directory_separators(char *path)
@@ -533,32 +21,68 @@
if (*path == '/')
*path = '\\';
if (*path == '\\')
- while (path[1] == '\\' || path[1] == '/')
+ while (path[1] == '\\' || path[1] == '/')
(void) memmove(path + 1,
path + 2, strlen(path + 2) + 1);
}
}
+static int
+protect_against_code_disclosure(const wchar_t *path)
+{
+ WIN32_FIND_DATAW data;
+ HANDLE handle;
+ const wchar_t *p;
+
+ /*
+ * Protect against CGI code disclosure under Windows.
+ * This is very nasty hole. Windows happily opens files with
+ * some garbage in the end of file name. So fopen("a.cgi ", "r")
+ * actually opens "a.cgi", and does not return an error! And since
+ * "a.cgi " does not have valid CGI extension, this leads to
+ * the CGI code disclosure.
+ * To protect, here we delete all fishy characters from the
+ * end of file name.
+ */
+
+ if ((handle = FindFirstFileW(path, &data)) == INVALID_HANDLE_VALUE)
+ return (FALSE);
+
+ FindClose(handle);
+
+ for (p = path + wcslen(path); p > path && p[-1] != L'\\';)
+ p--;
+
+ if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
+ wcscmp(data.cFileName, p) != 0)
+ return (FALSE);
+
+ return (TRUE);
+}
+
int
-my_open(const char *path, int flags, int mode)
+_shttpd_open(const char *path, int flags, int mode)
{
char buf[FILENAME_MAX];
wchar_t wbuf[FILENAME_MAX];
- my_strlcpy(buf, path, sizeof(buf));
+ _shttpd_strlcpy(buf, path, sizeof(buf));
fix_directory_separators(buf);
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
+ if (protect_against_code_disclosure(wbuf) == FALSE)
+ return (-1);
+
return (_wopen(wbuf, flags));
}
int
-my_stat(const char *path, struct stat *stp)
+_shttpd_stat(const char *path, struct stat *stp)
{
char buf[FILENAME_MAX], *p;
wchar_t wbuf[FILENAME_MAX];
- my_strlcpy(buf, path, sizeof(buf));
+ _shttpd_strlcpy(buf, path, sizeof(buf));
fix_directory_separators(buf);
p = buf + strlen(buf) - 1;
@@ -571,12 +95,12 @@
}
int
-my_remove(const char *path)
+_shttpd_remove(const char *path)
{
char buf[FILENAME_MAX];
wchar_t wbuf[FILENAME_MAX];
- my_strlcpy(buf, path, sizeof(buf));
+ _shttpd_strlcpy(buf, path, sizeof(buf));
fix_directory_separators(buf);
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
@@ -585,15 +109,15 @@
}
int
-my_rename(const char *path1, const char *path2)
+_shttpd_rename(const char *path1, const char *path2)
{
char buf1[FILENAME_MAX];
char buf2[FILENAME_MAX];
wchar_t wbuf1[FILENAME_MAX];
wchar_t wbuf2[FILENAME_MAX];
- my_strlcpy(buf1, path1, sizeof(buf1));
- my_strlcpy(buf2, path2, sizeof(buf2));
+ _shttpd_strlcpy(buf1, path1, sizeof(buf1));
+ _shttpd_strlcpy(buf2, path2, sizeof(buf2));
fix_directory_separators(buf1);
fix_directory_separators(buf2);
@@ -604,12 +128,12 @@
}
int
-my_mkdir(const char *path, int mode)
+_shttpd_mkdir(const char *path, int mode)
{
char buf[FILENAME_MAX];
wchar_t wbuf[FILENAME_MAX];
- my_strlcpy(buf, path, sizeof(buf));
+ _shttpd_strlcpy(buf, path, sizeof(buf));
fix_directory_separators(buf);
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
@@ -640,7 +164,7 @@
}
char *
-my_getcwd(char *buffer, int maxlen)
+_shttpd_getcwd(char *buffer, int maxlen)
{
char *result = NULL;
wchar_t *wbuffer, *wresult;
@@ -686,7 +210,7 @@
} else if ((dir = malloc(sizeof(*dir))) == NULL) {
errno = ENOMEM;
} else {
- snprintf(path, sizeof(path), "%s/*", name);
+ _shttpd_snprintf(path, sizeof(path), "%s/*", name);
fix_directory_separators(path);
MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, sizeof(wpath));
dir->handle = FindFirstFileW(wpath, &dir->info);
@@ -714,7 +238,7 @@
free(dir);
}
- if (result == -1)
+ if (result == -1)
errno = EBADF;
return (result);
@@ -742,7 +266,7 @@
}
int
-set_non_blocking_mode(int fd)
+_shttpd_set_non_blocking_mode(int fd)
{
unsigned long on = 1;
@@ -750,7 +274,7 @@
}
void
-set_close_on_exec(int fd)
+_shttpd_set_close_on_exec(int fd)
{
fd = 0; /* Do nothing. There is no FD_CLOEXEC on Windows */
}
@@ -763,6 +287,30 @@
big_int_t content_len;
};
+
+enum ready_mode_t {IS_READY_FOR_READ, IS_READY_FOR_WRITE};
+
+/*
+ * Wait until given socket is in ready state. Always return TRUE.
+ */
+static int
+is_socket_ready(int sock, enum ready_mode_t mode)
+{
+ fd_set read_set, write_set;
+
+ FD_ZERO(&read_set);
+ FD_ZERO(&write_set);
+
+ if (mode == IS_READY_FOR_READ)
+ FD_SET(sock, &read_set);
+ else
+ FD_SET(sock, &write_set);
+
+ select(sock + 1, &read_set, &write_set, NULL, NULL);
+
+ return (TRUE);
+}
+
/*
* Thread function that reads POST data from the socket pair
* and writes it to the CGI process.
@@ -778,14 +326,19 @@
size_t max_recv;
max_recv = min(sizeof(buf), tp->content_len - total);
- while (!stop && max_recv > 0 && (n = recv(tp->s, buf, max_recv, 0)) > 0) {
+ while (!stop &&
+ max_recv > 0 &&
+ is_socket_ready(tp->s, IS_READY_FOR_READ) &&
+ (n = recv(tp->s, buf, max_recv, 0)) > 0) {
+ if (n == -1 && ERRNO == EWOULDBLOCK)
+ continue;
for (sent = 0; !stop && sent < n; sent += k)
if (!WriteFile(tp->hPipe, buf + sent, n - sent, &k, 0))
stop++;
total += n;
max_recv = min(sizeof(buf), tp->content_len - total);
}
-
+
CloseHandle(tp->hPipe); /* Suppose we have POSTed everything */
free(tp);
}
@@ -804,12 +357,19 @@
while (!stop && ReadFile(tp->hPipe, buf, sizeof(buf), &n, NULL)) {
ntotal += n;
- for (sent = 0; !stop && sent < n; sent += k)
- if ((k = send(tp->s, buf + sent, n - sent, 0)) <= 0)
+ for (sent = 0; !stop && sent < n; sent += k) {
+ if (is_socket_ready(tp->s, IS_READY_FOR_WRITE) &&
+ (k = send(tp->s, buf + sent, n - sent, 0)) <= 0) {
+ if (k == -1 && ERRNO == EWOULDBLOCK) {
+ k = 0;
+ continue;
+ }
stop++;
+ }
+ }
}
CloseHandle(tp->hPipe);
-
+
/*
* Windows is a piece of crap. When this thread closes its end
* of the socket pair, the other end (get_cgi() function) may loose
@@ -846,14 +406,14 @@
}
int
-spawn_process(struct conn *c, const char *prog, char *envblk,
+_shttpd_spawn_process(struct conn *c, const char *prog, char *envblk,
char *envp[], int sock, const char *dir)
{
- HANDLE a[2], b[2], h[2], me;
- DWORD flags;
- char *p, cmdline[FILENAME_MAX], line[FILENAME_MAX];
- FILE *fp;
- STARTUPINFOA si;
+ HANDLE a[2], b[2], h[2], me;
+ DWORD flags;
+ char *p, *interp, cmdline[FILENAME_MAX], line[FILENAME_MAX];
+ FILE *fp;
+ STARTUPINFOA si;
PROCESS_INFORMATION pi;
me = GetCurrentProcess();
@@ -864,7 +424,7 @@
CreatePipe(&b[0], &b[1], NULL, 0);
DuplicateHandle(me, a[0], me, &h[0], 0, TRUE, flags);
DuplicateHandle(me, b[1], me, &h[1], 0, TRUE, flags);
-
+
(void) memset(&si, 0, sizeof(si));
(void) memset(&pi, 0, sizeof(pi));
@@ -872,11 +432,12 @@
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
- si.hStdOutput = si.hStdError = h[1];
+ si.hStdOutput = h[1];
si.hStdInput = h[0];
/* If CGI file is a script, try to read the interpreter line */
- if (c->ctx->cgi_interpreter == NULL) {
+ interp = c->ctx->options[OPT_CGI_INTERPRETER];
+ if (interp == NULL) {
if ((fp = fopen(prog, "r")) != NULL) {
(void) fgets(line, sizeof(line), fp);
if (memcmp(line, "#!", 2) != 0)
@@ -887,14 +448,17 @@
*p = '\0';
(void) fclose(fp);
}
- (void) snprintf(cmdline, sizeof(cmdline), "%s%s%s",
+ interp = line + 2;
+ (void) _shttpd_snprintf(cmdline, sizeof(cmdline), "%s%s%s",
line + 2, line[2] == '\0' ? "" : " ", prog);
- } else {
- (void) snprintf(cmdline, sizeof(cmdline), "%s %s",
- c->ctx->cgi_interpreter, prog);
}
- (void) snprintf(line, sizeof(line), "%s", dir);
+ if ((p = strrchr(prog, '/')) != NULL)
+ prog = p + 1;
+
+ (void) _shttpd_snprintf(cmdline, sizeof(cmdline), "%s %s", interp, prog);
+
+ (void) _shttpd_snprintf(line, sizeof(line), "%s", dir);
fix_directory_separators(line);
fix_directory_separators(cmdline);
@@ -907,7 +471,8 @@
if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE,
CREATE_NEW_PROCESS_GROUP, envblk, line, &si, &pi) == 0) {
- elog(E_LOG, c,"redirect: CreateProcess(%s): %d",cmdline,ERRNO);
+ _shttpd_elog(E_LOG, c,
+ "redirect: CreateProcess(%s): %d", cmdline, ERRNO);
return (-1);
} else {
CloseHandle(h[0]);
@@ -920,3 +485,203 @@
}
#endif /* !NO_CGI */
+
+#define ID_TRAYICON 100
+#define ID_QUIT 101
+static NOTIFYICONDATA ni;
+
+static LRESULT CALLBACK
+WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ POINT pt;
+ HMENU hMenu;
+
+ switch (msg) {
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case ID_QUIT:
+ exit(EXIT_SUCCESS);
+ break;
+ }
+ break;
+ case WM_USER:
+ switch (lParam) {
+ case WM_RBUTTONUP:
+ case WM_LBUTTONUP:
+ case WM_LBUTTONDBLCLK:
+ hMenu = CreatePopupMenu();
+ AppendMenu(hMenu, 0, ID_QUIT, "Exit SHTTPD");
+ GetCursorPos(&pt);
+ TrackPopupMenu(hMenu, 0, pt.x, pt.y, 0, hWnd, NULL);
+ DestroyMenu(hMenu);
+ break;
+ }
+ break;
+ }
+
+ return (DefWindowProc(hWnd, msg, wParam, lParam));
+}
+
+static void
+systray(void *arg)
+{
+ WNDCLASS cls;
+ HWND hWnd;
+ MSG msg;
+
+ (void) memset(&cls, 0, sizeof(cls));
+
+ cls.lpfnWndProc = (WNDPROC) WindowProc;
+ cls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+ cls.lpszClassName = "shttpd v." SHTTPD_VERSION;
+
+ if (!RegisterClass(&cls))
+ _shttpd_elog(E_FATAL, NULL, "RegisterClass: %d", ERRNO);
+ else if ((hWnd = CreateWindow(cls.lpszClassName, "",
+ WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, NULL, NULL, NULL, arg)) == NULL)
+ _shttpd_elog(E_FATAL, NULL, "CreateWindow: %d", ERRNO);
+ ShowWindow(hWnd, SW_HIDE);
+
+ ni.cbSize = sizeof(ni);
+ ni.uID = ID_TRAYICON;
+ ni.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
+ ni.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+ ni.hWnd = hWnd;
+ _shttpd_snprintf(ni.szTip, sizeof(ni.szTip), "SHTTPD web server");
+ ni.uCallbackMessage = WM_USER;
+ Shell_NotifyIcon(NIM_ADD, &ni);
+
+ while (GetMessage(&msg, hWnd, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+}
+
+int
+_shttpd_set_systray(struct shttpd_ctx *ctx, const char *opt)
+{
+ HWND hWnd;
+ char title[512];
+ static WNDPROC oldproc;
+
+ if (!_shttpd_is_true(opt))
+ return (TRUE);
+
+ FreeConsole();
+ GetConsoleTitle(title, sizeof(title));
+ hWnd = FindWindow(NULL, title);
+ ShowWindow(hWnd, SW_HIDE);
+ _beginthread(systray, 0, hWnd);
+
+ return (TRUE);
+}
+
+int
+_shttpd_set_nt_service(struct shttpd_ctx *ctx, const char *action)
+{
+ SC_HANDLE hSCM, hService;
+ char path[FILENAME_MAX], key[128];
+ HKEY hKey;
+ DWORD dwData;
+
+
+ if (!strcmp(action, "install")) {
+ if ((hSCM = OpenSCManager(NULL, NULL,
+ SC_MANAGER_ALL_ACCESS)) == NULL)
+ _shttpd_elog(E_FATAL, NULL, "Error opening SCM (%d)", ERRNO);
+
+ GetModuleFileName(NULL, path, sizeof(path));
+
+ hService = CreateService(hSCM, SERVICE_NAME, SERVICE_NAME,
+ SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
+ SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, path,
+ NULL, NULL, NULL, NULL, NULL);
+
+ if (!hService)
+ _shttpd_elog(E_FATAL, NULL,
+ "Error installing service (%d)", ERRNO);
+
+ ChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION,
+ &service_descr);
+ _shttpd_elog(E_FATAL, NULL, "Service successfully installed");
+
+
+ } else if (!strcmp(action, "uninstall")) {
+
+ if ((hSCM = OpenSCManager(NULL, NULL,
+ SC_MANAGER_ALL_ACCESS)) == NULL) {
+ _shttpd_elog(E_FATAL, NULL, "Error opening SCM (%d)", ERRNO);
+ } else if ((hService = OpenService(hSCM,
+ SERVICE_NAME, DELETE)) == NULL) {
+ _shttpd_elog(E_FATAL, NULL,
+ "Error opening service (%d)", ERRNO);
+ } else if (!DeleteService(hService)) {
+ _shttpd_elog(E_FATAL, NULL,
+ "Error deleting service (%d)", ERRNO);
+ } else {
+ _shttpd_elog(E_FATAL, NULL, "Service deleted");
+ }
+
+ } else {
+ _shttpd_elog(E_FATAL, NULL, "Use -service <install|uninstall>");
+ }
+
+ /* NOTREACHED */
+ return (TRUE);
+}
+
+static void WINAPI
+ControlHandler(DWORD code)
+{
+ if (code == SERVICE_CONTROL_STOP || code == SERVICE_CONTROL_SHUTDOWN) {
+ ss.dwWin32ExitCode = 0;
+ ss.dwCurrentState = SERVICE_STOPPED;
+ }
+
+ SetServiceStatus(hStatus, &ss);
+}
+
+static void WINAPI
+ServiceMain(int argc, char *argv[])
+{
+ char path[MAX_PATH], *p, *av[] = {"shttpd_service", path, NULL};
+ struct shttpd_ctx *ctx;
+
+ ss.dwServiceType = SERVICE_WIN32;
+ ss.dwCurrentState = SERVICE_RUNNING;
+ ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
+
+ hStatus = RegisterServiceCtrlHandler(SERVICE_NAME, ControlHandler);
+ SetServiceStatus(hStatus, &ss);
+
+ GetModuleFileName(NULL, path, sizeof(path));
+
+ if ((p = strrchr(path, DIRSEP)) != NULL)
+ *++p = '\0';
+
+ strcat(path, CONFIG_FILE); /* woo ! */
+
+ ctx = shttpd_init(NELEMS(av) - 1, av);
+ if ((ctx = shttpd_init(NELEMS(av) - 1, av)) == NULL)
+ _shttpd_elog(E_FATAL, NULL, "Cannot initialize SHTTP context");
+
+ while (ss.dwCurrentState == SERVICE_RUNNING)
+ shttpd_poll(ctx, INT_MAX);
+ shttpd_fini(ctx);
+
+ ss.dwCurrentState = SERVICE_STOPPED;
+ ss.dwWin32ExitCode = -1;
+ SetServiceStatus(hStatus, &ss);
+}
+
+void
+try_to_run_as_nt_service(void)
+{
+ static SERVICE_TABLE_ENTRY service_table[] = {
+ {SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
+ {NULL, NULL}
+ };
+
+ if (StartServiceCtrlDispatcher(service_table))
+ exit(EXIT_SUCCESS);
+}
diff --git a/src/server/shttpd/compat_win32.h b/src/server/shttpd/compat_win32.h
index a5c1dce..99ba99d 100644
--- a/src/server/shttpd/compat_win32.h
+++ b/src/server/shttpd/compat_win32.h
@@ -20,18 +20,6 @@
#ifndef _WIN32_WCE
-#ifdef _MSC_VER /* pragmas not valid on MinGW */
-#pragma comment(lib,"ws2_32")
-#pragma comment(lib,"user32")
-#pragma comment(lib,"comctl32")
-#pragma comment(lib,"comdlg32")
-#pragma comment(lib,"shell32")
-#ifdef NO_GUI
-#pragma comment(linker,"/subsystem:console")
-#else
-#pragma comment(linker,"/subsystem:windows")
-#endif /* NO_GUI */
-#endif /* _MSC_VER */
#include <process.h>
#include <direct.h>
#include <io.h>
@@ -51,7 +39,7 @@
#define ERRNO GetLastError()
#define NO_SOCKLEN_T
-#define SSL_LIB L"libssl32.dll"
+#define SSL_LIB L"ssleay32.dll"
#define DIRSEP '\\'
#define IS_DIRSEP_CHAR(c) ((c) == '/' || (c) == '\\')
#define O_NONBLOCK 0
diff --git a/src/server/shttpd/compat_wince.c b/src/server/shttpd/compat_wince.c
index 36702f6..edf8da2 100644
--- a/src/server/shttpd/compat_wince.c
+++ b/src/server/shttpd/compat_wince.c
@@ -860,7 +860,7 @@
4-digit years, the result is zero-padded and exactly two characters;
but for other years, there may a negative sign or more digits. In
this way, `<<%C%y>>' is equivalent to `<<%Y>>'. [tm_year]
-
+
o %d
The day of the month, formatted with two digits (from `<<01>>' to
`<<31>>'). [tm_mday]
@@ -892,7 +892,7 @@
2nd, or 3rd falls on a Sunday, that day and earlier belong to the last
week of the previous year; and if December 29th, 30th, or 31st falls
on Monday, that day and later belong to week 1 of the next year. For
-consistency with %Y, it always has at least four characters.
+consistency with %Y, it always has at least four characters.
Example: "%G" for Saturday 2nd January 1999 gives "1998", and for
Tuesday 30th December 1997 gives "1998". [tm_year, tm_wday, tm_yday]
diff --git a/src/server/shttpd/compat_wince.h b/src/server/shttpd/compat_wince.h
index 651ec50..02f6557 100644
--- a/src/server/shttpd/compat_wince.h
+++ b/src/server/shttpd/compat_wince.h
@@ -97,7 +97,7 @@
#define S_IFDIR 0040000
#define S_IFREG 0100000
#define S_IEXEC 0000100
-#define S_IWRITE 0000200
+#define S_IWRITE 0000200
#define S_IREAD 0000400
#define _S_IFDIR S_IFDIR /* MSVCRT compatibilit */
diff --git a/src/server/shttpd/config.c b/src/server/shttpd/config.c
deleted file mode 100644
index 67b904d..0000000
--- a/src/server/shttpd/config.c
+++ /dev/null
@@ -1,429 +0,0 @@
-/*
- * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
- * All rights reserved
- *
- * "THE BEER-WARE LICENSE" (Revision 42):
- * Sergey Lyubka wrote this file. As long as you retain this notice you
- * can do whatever you want with this stuff. If we meet some day, and you think
- * this stuff is worth it, you can buy me a beer in return.
- */
-
-#include "shttpd_defs.h"
-#include "wsmand-daemon.h"
-
-/*
- * Configuration parameters setters
- */
-static void
-set_int(struct shttpd_ctx *ctx, void *ptr, const char *string)
-{
- ctx = NULL; /* Unused */
- * (int *) ptr = atoi(string);
-}
-
-static void
-set_str(struct shttpd_ctx *ctx, void *ptr, const char *string)
-{
- ctx = NULL; /* Unused */
- * (char **) ptr = strdup(string);
-}
-
-static void
-set_log_file(struct shttpd_ctx *ctx, void *ptr, const char *string)
-{
- FILE **fp = ptr;
- ctx = NULL;
-
- if ((*fp = fopen(string, "a")) == NULL)
- elog(E_FATAL, NULL, "cannot open log file %s: %s",
- string, strerror(errno));
-}
-
-static int isbyte(int n) { return (n >= 0 && n <= 255); }
-
-static void
-set_acl(struct shttpd_ctx *ctx, void *ptr, const char *s)
-{
- struct llhead *head = ptr;
- struct acl *acl;
- char flag;
- int len, a, b, c, d, n, mask;
-
- ctx = NULL;
-
- FOR_EACH_WORD_IN_LIST(s, len) {
-
- mask = 32;
-
- if (sscanf(s, "%c%d.%d.%d.%d%n",&flag,&a,&b,&c,&d,&n) != 5) {
- elog(E_FATAL, NULL, "[%s]: subnet must be "
- "[+|-]x.x.x.x[/x]", s);
- } else if (flag != '+' && flag != '-') {
- elog(E_FATAL, NULL, "flag must be + or -: [%s]", s);
- } else if (!isbyte(a)||!isbyte(b)||!isbyte(c)||!isbyte(d)) {
- elog(E_FATAL, NULL, "bad ip address: [%s]", s);
- } else if ((acl = malloc(sizeof(*acl))) == NULL) {
- elog(E_FATAL, NULL, "%s", "cannot malloc subnet");
- } else if (sscanf(s + n, "/%d", &mask) == 0) {
- /* Do nothing, no mask specified */
- } else if (mask < 0 || mask > 32) {
- elog(E_FATAL, NULL, "bad subnet mask: %d [%s]", n, s);
- }
-
- acl->ip = (a << 24) | (b << 16) | (c << 8) | d;
- acl->mask = mask ? 0xffffffffU << (32 - mask) : 0;
- acl->flag = flag;
- LL_TAIL(head, &acl->link);
- }
-}
-
-#ifndef NO_SSL
-/*
- * Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
- */
-static void
-set_ssl(struct shttpd_ctx *ctx, void *arg, const char *pem)
-{
- SSL_CTX *CTX;
- void *lib;
- struct ssl_func *fp;
- char *ssl_disabled_protocols = wsmand_options_get_ssl_disabled_protocols();
-
- arg = NULL; /* Unused */
-
- /* Load SSL library dynamically */
- if ((lib = dlopen(SSL_LIB, RTLD_LAZY)) == NULL) {
- elog(E_FATAL, NULL, "set_ssl: cannot load %s", SSL_LIB);
- ctx->ssl_ctx = NULL;
- return;
- }
-
- for (fp = ssl_sw; fp->name != NULL; fp++) {
- if ((fp->ptr.v_void = dlsym(lib, fp->name)) == NULL) {
- elog(E_FATAL, NULL,"set_ssl: cannot find %s", fp->name);
- ctx->ssl_ctx = NULL;
- return;
- }
- }
-
- /* Initialize SSL crap */
- static int ssl_library_initialized = 0;
- if(!ssl_library_initialized) {
- debug("Initialize SSL");
- SSL_library_init();
- ssl_library_initialized = 1;
- }
- if ((CTX = SSL_CTX_new(SSLv23_server_method())) == NULL) {
- elog(E_FATAL, NULL, "SSL_CTX_new error");
- }
- else if (wsmand_options_get_ssl_cert_file() && SSL_CTX_use_certificate_file(CTX, wsmand_options_get_ssl_cert_file(),SSL_FILETYPE_PEM) == 0) {
- elog(E_FATAL, NULL, "cannot open %s : %s", pem, strerror(errno));
- SSL_CTX_free(CTX);
- CTX = NULL;
- }
- else if (wsmand_options_get_ssl_key_file() && SSL_CTX_use_PrivateKey_file(CTX, wsmand_options_get_ssl_key_file(), SSL_FILETYPE_PEM) == 0) {
- elog(E_FATAL, NULL, "cannot open %s : %s", pem, strerror(errno));
- SSL_CTX_free(CTX);
- CTX = NULL;
- }
- while (ssl_disabled_protocols) {
- struct ctx_opts_t {
- char *name;
- long opt;
- } protocols[] = {
- { "SSLv2", SSL_OP_NO_SSLv2 },
- { "SSLv3", SSL_OP_NO_SSLv3 },
- { "TLSv1", SSL_OP_NO_TLSv1 },
-# if OPENSSL_VERSION_NUMBER >= 0x10001000L
- { "TLSv1_1", SSL_OP_NO_TLSv1_1 },
- { "TLSv1_2", SSL_OP_NO_TLSv1_2 },
-# endif
- { NULL, 0 }
- };
- char *blank_ptr;
- int idx;
- if (*ssl_disabled_protocols == 0)
- break;
- blank_ptr = strchr(ssl_disabled_protocols, ' ');
- if (blank_ptr == NULL)
- blank_ptr = ssl_disabled_protocols + strlen(ssl_disabled_protocols);
- for (idx = 0; protocols[idx].name ; ++idx) {
- if (strncasecmp(protocols[idx].name, ssl_disabled_protocols, blank_ptr-ssl_disabled_protocols) == 0) {
- debug("SSL: disable %s protocol", protocols[idx].name);
- SSL_CTX_ctrl(CTX, SSL_CTRL_OPTIONS, protocols[idx].opt, NULL);
- break;
- }
- }
- if (*blank_ptr == 0)
- break;
- ssl_disabled_protocols = blank_ptr + 1;
- }
-
- ctx->ssl_ctx = CTX;
-}
-#endif /* NO_SSL */
-
-static void
-set_mime(struct shttpd_ctx *ctx, void *arg, const char *string)
-{
- arg = NULL;
- set_mime_types(ctx, string);
-}
-
-#define OFS(x) offsetof(struct shttpd_ctx, x)
-#define BOOL_OPT "0|1"
-const struct opt options[] = {
- {'d', "document_root", "Web root directory", set_str,
- OFS(document_root), "directory", NULL, OPT_DIR},
- {'i', "index_files", "Index files", set_str, OFS(index_files),
- "file_list", INDEX_FILES, OPT_ADVANCED},
- {'p', "listen_ports", "Listening ports", set_str,
- OFS(ports), "ports", LISTENING_PORTS, OPT_ADVANCED},
- {'D', "list_directories", "Directory listing", set_int,
- OFS(dirlist), BOOL_OPT, "1", OPT_BOOL | OPT_ADVANCED},
-#ifndef NO_CGI
- {'c', "cgi_extensions", "CGI extensions", set_str,
- OFS(cgi_extensions), "ext_list", CGI_EXT, OPT_ADVANCED},
- {'C', "cgi_interpreter", "CGI interpreter", set_str,
- OFS(cgi_interpreter), "file", NULL, OPT_FILE | OPT_ADVANCED},
- {'V', "cgi_envvar", "CGI envir variables", set_str,
- OFS(cgi_vars), "X=Y,....", NULL, OPT_ADVANCED},
-#endif /* NO_CGI */
-#if !defined(NO_SSI)
- {'S', "ssi_extensions", "SSI extensions", set_str,
- OFS(ssi_extensions), "ext_list", SSI_EXT, OPT_ADVANCED},
-#endif /* NO_SSI */
- {'N', "auth_realm", "Authentication realm", set_str,
- OFS(auth_realm), "auth_realm", REALM, OPT_ADVANCED},
- {'l', "access_log", "Access log file", set_log_file,
- OFS(access_log), "file", NULL, OPT_FILE | OPT_ADVANCED},
- {'e', "error_log", "Error log file", set_log_file,
- OFS(error_log), "file", NULL, OPT_FILE | OPT_ADVANCED},
- {'m', "mime_types", "Mime types file", set_mime,
- OFS(mime_file), "file", NULL, OPT_FILE | OPT_ADVANCED},
- {'P', "global_htpasswd", "Global passwords file", set_str,
- OFS(global_passwd_file), "file", NULL, OPT_FILE | OPT_ADVANCED},
-#ifndef NO_SSL
- {'s', "ssl_certificate", "SSL certificate file", set_ssl,
- OFS(ssl_ctx), "pem_file", NULL, OPT_FILE | OPT_ADVANCED},
-#endif /* NO_SSL */
- {'U', "put_auth", "PUT,DELETE auth file",set_str,
- OFS(put_auth_file), "file", NULL, OPT_FILE | OPT_ADVANCED},
- {'a', "aliases", "Aliases", set_str,
- OFS(aliases), "X=Y,...", NULL, OPT_ADVANCED},
- {'b', "io_buf_size", "IO buffer size", set_int, OFS(io_buf_size),
- "bytes", DFLT_IO_SIZ, OPT_INT | OPT_ADVANCED},
- {'x', "acl", "Allow/deny IP addresses/subnets", set_acl,
- OFS(acl), "acl_list", NULL, OPT_ADVANCED},
-#ifdef _WIN32
- {'B', "auto_start", "Autostart with Windows", set_int,
- OFS(auto_start), BOOL_OPT, "1", OPT_BOOL},
-#else
- {'I', "inetd_mode", "Inetd mode", set_int,
- OFS(inetd_mode), BOOL_OPT, NULL, OPT_BOOL },
- {'u', "runtime_uid", "Run as user", set_str,
- OFS(uid), "user_name", NULL, 0 },
-#endif /* _WIN32 */
- {0, NULL, NULL, NULL, 0, NULL, NULL, 0 }
-};
-
-static const struct opt *
-find_option(int sw, const char *name)
-{
- const struct opt *opt;
-
- for (opt = options; opt->sw != 0; opt++)
- if (sw == opt->sw || (name && strcmp(opt->name, name) == 0))
- return (opt);
-
- return (NULL);
-}
-
-static void
-set_option(const struct opt *opt, const char *val, char **tmpvars)
-{
- tmpvars += opt - options;
-
- if (*tmpvars != NULL)
- free(*tmpvars);
-
- *tmpvars = strdup(val);
-}
-
-/*
- * Initialize shttpd context
- */
-static void
-initialize_context(struct shttpd_ctx *ctx, const char *config_file,
- int argc, char *argv[], char **tmpvars)
-{
- char line[FILENAME_MAX], root[FILENAME_MAX],
- var[sizeof(line)], val[sizeof(line)];
- const char *arg;
- size_t i;
- const struct opt *opt;
- FILE *fp;
- struct tm *tm;
-
- current_time = time(NULL);
- tm = localtime(¤t_time);
- tz_offset = 0;
-#if 0
- tm->tm_gmtoff - 3600 * (tm->tm_isdst > 0 ? 1 : 0);
-#endif
-
- (void) memset(ctx, 0, sizeof(*ctx));
-
- ctx->start_time = current_time;
- InitializeCriticalSection(&ctx->mutex);
-
- LL_INIT(&ctx->connections);
- LL_INIT(&ctx->mime_types);
- LL_INIT(&ctx->registered_uris);
- LL_INIT(&ctx->uri_auths);
- LL_INIT(&ctx->error_handlers);
- LL_INIT(&ctx->acl);
-
-#if !defined(NO_SSI)
- LL_INIT(&ctx->ssi_funcs);
-#endif /* NO_SSI */
-
- /* First pass: set the defaults */
- for (opt = options; opt->sw != 0; opt++)
- if (tmpvars[opt - options] == NULL && opt->def != NULL)
- tmpvars[opt - options] = strdup(opt->def);
-
- /* Second pass: load config file */
- if (config_file != NULL && (fp = fopen(config_file, "r")) != NULL) {
- DBG(("init_ctx: config file %s", config_file));
-
- /* Loop through the lines in config file */
- while (fgets(line, sizeof(line), fp) != NULL) {
-
- /* Skip comments and empty lines */
- if (line[0] == '#' || line[0] == '\n')
- continue;
-
- /* Trim trailing newline character */
- line[strlen(line) - 1] = '\0';
-
- if (sscanf(line, "%s %[^#\n]", var, val) != 2)
- elog(E_FATAL,0,"init_ctx: bad line: [%s]",line);
-
- if ((opt = find_option(0, var)) == NULL)
- elog(E_FATAL, NULL,
- "set_option: unknown variable [%s]", var);
- set_option(opt, val, tmpvars);
- }
- (void) fclose(fp);
- }
-
- /* Third pass: process command line args */
- for (i = 1; i < (size_t) argc && argv[i][0] == '-'; i++)
- if ((opt = find_option(argv[i][1], NULL)) != NULL) {
- arg = argv[i][2] ? &argv[i][2] : argv[++i];
-
- if (arg == NULL)
- usage(argv[0]);
-
- set_option(opt, arg, tmpvars);
- } else {
- usage(argv[0]);
- }
-
- /* Call setters functions now */
- for (i = 0; i < NELEMS(options); i++)
- if (tmpvars[i] != NULL) {
- options[i].setter(ctx,
- ((char *) ctx) + options[i].ofs, tmpvars[i]);
- free(tmpvars[i]);
- }
-
- /* If document_root is not set, set it to current directory */
- if (ctx->document_root == NULL) {
- (void) my_getcwd(root, sizeof(root));
- ctx->document_root = strdup(root);
- }
-
-#ifdef _WIN32
- {WSADATA data; WSAStartup(MAKEWORD(2,2), &data);}
-#endif /* _WIN32 */
-
- DBG(("init_ctx: initialized context %p", (void *) ctx));
-}
-
-/*
- * Show usage string and exit.
- */
-void
-usage(const char *prog)
-{
- const struct opt *opt;
-
- (void) fprintf(stderr,
- "SHTTPD version %s (c) Sergey Lyubka\n"
- "usage: %s [OPTIONS] [config_file]\n"
- "Note: config line keyword for every option is in the "
- "round brackets\n", VERSION, prog);
-
-#if !defined(NO_AUTH)
- (void) fprintf(stderr, "-A <htpasswd_file> <realm> <user> <passwd>\n");
-#endif /* NO_AUTH */
-
- for (opt = options; opt->name != NULL; opt++)
- (void) fprintf(stderr, "-%c <%s>\t\t%s (%s)\n",
- opt->sw, opt->arg, opt->desc, opt->name);
-
- exit(EXIT_FAILURE);
-}
-
-struct shttpd_ctx *
-init_from_argc_argv(const char *config_file, int argc, char *argv[])
-{
- struct shttpd_ctx *ctx;
- char *tmpvars[NELEMS(options)];
- size_t i;
-
- /* Initialize all temporary holders to NULL */
- for (i = 0; i < NELEMS(tmpvars); i++)
- tmpvars[i] = NULL;
-
- if ((ctx = malloc(sizeof(*ctx))) != NULL)
- initialize_context(ctx, config_file, argc, argv, tmpvars);
-
- return (ctx);
-}
-
-struct shttpd_ctx *
-shttpd_init(const char *config_file, ...)
-{
- struct shttpd_ctx *ctx;
- va_list ap;
- const char *opt_name, *opt_value;
- char *tmpvars[NELEMS(options)];
- const struct opt *opt;
- size_t i;
-
- elog(E_LOG, NULL, "Initializing http server");
- /* Initialize all temporary holders to NULL */
- for (i = 0; i < NELEMS(tmpvars); i++)
- tmpvars[i] = NULL;
-
- if ((ctx = malloc(sizeof(*ctx))) != NULL) {
-
- va_start(ap, config_file);
- while ((opt_name = va_arg(ap, const char *)) != NULL) {
- opt_value = va_arg(ap, const char *);
-
- if ((opt = find_option(0, opt_name)) == NULL)
- elog(E_FATAL, NULL, "shttpd_init: "
- "unknown variable [%s]", opt_name);
- set_option(opt, opt_value, tmpvars);
- }
- va_end(ap);
-
- initialize_context(ctx, config_file, 0, NULL, tmpvars);
- }
-
- return (ctx);
-}
diff --git a/src/server/shttpd/defs.h b/src/server/shttpd/defs.h
new file mode 100644
index 0000000..f2d4adf
--- /dev/null
+++ b/src/server/shttpd/defs.h
@@ -0,0 +1,410 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#ifndef DEFS_HEADER_DEFINED
+#define DEFS_HEADER_DEFINED
+
+#include "wsman_config.h"
+
+#include "std_includes.h"
+#include "llist.h"
+#include "io.h"
+#include "md5.h"
+#include "u/libu.h"
+#include "config.h"
+#include "shttpd.h"
+
+#define NELEMS(ar) (sizeof(ar) / sizeof(ar[0]))
+
+#ifdef _DEBUG
+#define DBG(x) do { printf x ; putchar('\n'); fflush(stdout); } while (0)
+#else
+#define DBG(x)
+#endif /* DEBUG */
+
+/*
+ * Darwin prior to 7.0 and Win32 do not have socklen_t
+ */
+#ifdef NO_SOCKLEN_T
+typedef int socklen_t;
+#endif /* NO_SOCKLEN_T */
+
+/*
+ * For parsing. This guy represents a substring.
+ */
+struct vec {
+ const char *ptr;
+ int len;
+};
+
+#if !defined(FALSE)
+enum {FALSE, TRUE};
+#endif /* !FALSE */
+
+enum {BASIC_AUTH, DIGEST_AUTH};
+enum {METHOD_GET, METHOD_POST, METHOD_PUT, METHOD_DELETE, METHOD_HEAD};
+enum {HDR_DATE, HDR_INT, HDR_STRING}; /* HTTP header types */
+enum {E_FATAL = 1, E_LOG = 2}; /* Flags for elog() function */
+typedef unsigned long big_int_t; /* Type for Content-Length */
+
+/*
+ * Unified socket address
+ */
+struct usa {
+ socklen_t len;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ } u;
+};
+
+/*
+ * This thing is aimed to hold values of any type.
+ * Used to store parsed headers' values.
+ */
+union variant {
+ char *v_str;
+ int v_int;
+ big_int_t v_big_int;
+ time_t v_time;
+ void (*v_func)(void);
+ void *v_void;
+ struct vec v_vec;
+};
+
+/*
+ * This is used only in embedded configuration. This structure holds a
+ * registered URI, associated callback function with callback data.
+ * For non-embedded compilation shttpd_callback_t is not defined, so
+ * we use union variant to keep the compiler silent.
+ */
+struct registered_uri {
+ struct llhead link;
+ const char *uri;
+ union variant callback;
+ void *callback_data;
+};
+
+struct uri_auth {
+ struct llhead link;
+ const char *uri;
+ const char *file_name;
+ int type;
+ size_t uri_len;
+ union variant callback;
+ void *callback_data;
+};
+
+/*
+ * User may want to handle certain errors. This structure holds the
+ * handlers for corresponding error codes.
+ */
+struct error_handler {
+ struct llhead link;
+ int code;
+ union variant callback;
+ void *callback_data;
+};
+
+struct http_header {
+ int len; /* Header name length */
+ int type; /* Header type */
+ size_t offset; /* Value placeholder */
+ const char *name; /* Header name */
+};
+
+/*
+ * This guy holds parsed HTTP headers
+ */
+struct headers {
+ union variant cl; /* Content-Length: */
+ union variant ct; /* Content-Type: */
+ union variant connection; /* Connection: */
+ union variant ims; /* If-Modified-Since: */
+ union variant user; /* Remote user name */
+ union variant auth; /* Authorization */
+ union variant useragent; /* User-Agent: */
+ union variant referer; /* Referer: */
+ union variant cookie; /* Cookie: */
+ union variant location; /* Location: */
+ union variant range; /* Range: */
+ union variant status; /* Status: */
+ union variant transenc; /* Transfer-Encoding: */
+};
+
+/* Must go after union variant definition */
+#include "ssl.h"
+
+/*
+ * The communication channel
+ */
+union channel {
+ int fd; /* Regular static file */
+ int sock; /* Connected socket */
+ struct {
+ int sock; /* XXX important. must be first */
+ SSL *ssl; /* shttpd_poll() assumes that */
+ } ssl; /* SSL-ed socket */
+ struct {
+ DIR *dirp;
+ char *path;
+ } dir; /* Opened directory */
+ struct {
+ void *state; /* For keeping state */
+ union variant func; /* User callback function */
+ void *data; /* User defined parameters */
+ } emb; /* Embedded, user callback */
+};
+
+struct stream;
+
+/*
+ * IO class descriptor (file, directory, socket, SSL, CGI, etc)
+ * These classes are defined in io_*.c files.
+ */
+struct io_class {
+ const char *name;
+ int (*read)(struct stream *, void *buf, size_t len);
+ int (*write)(struct stream *, const void *buf, size_t len);
+ void (*close)(struct stream *);
+};
+
+/*
+ * Data exchange stream. It is backed by some communication channel:
+ * opened file, socket, etc. The 'read' and 'write' methods are
+ * determined by a communication channel.
+ */
+struct stream {
+ struct conn *conn;
+ union channel chan; /* Descriptor */
+ struct io io; /* IO buffer */
+ const struct io_class *io_class; /* IO class */
+ int headers_len;
+ big_int_t content_len;
+ unsigned int flags;
+#define FLAG_HEADERS_PARSED 1
+#define FLAG_SSL_ACCEPTED 2
+#define FLAG_R 4 /* Can read in general */
+#define FLAG_W 8 /* Can write in general */
+#define FLAG_CLOSED 16
+#define FLAG_DONT_CLOSE 32
+#define FLAG_ALWAYS_READY 64 /* File, dir, user_func */
+#define FLAG_SUSPEND 128
+};
+
+struct worker {
+ struct llhead link;
+ int num_conns; /* Num of active connections */
+ int exit_flag; /* Ditto - exit flag */
+ int ctl[2]; /* Control socket pair */
+ struct shttpd_ctx *ctx; /* Context reference */
+ struct llhead connections; /* List of connections */
+};
+
+struct conn {
+ struct llhead link; /* Connections chain */
+ struct worker *worker; /* Worker this conn belongs to */
+ struct shttpd_ctx *ctx; /* Context this conn belongs to */
+ struct usa sa; /* Remote socket address */
+ time_t birth_time; /* Creation time */
+ time_t expire_time; /* Expiration time */
+
+ int loc_port; /* Local port */
+ int status; /* Reply status code */
+ int method; /* Request method */
+ char *uri; /* Decoded URI */
+ unsigned long major_version; /* Major HTTP version number */
+ unsigned long minor_version; /* Minor HTTP version number */
+ char *request; /* Request line */
+ char *headers; /* Request headers */
+ char *query; /* QUERY_STRING part of the URI */
+ char *path_info; /* PATH_INFO thing */
+ struct vec mime_type; /* Mime type */
+
+ struct headers ch; /* Parsed client headers */
+
+ struct stream loc; /* Local stream */
+ struct stream rem; /* Remote stream */
+
+#if !defined(NO_SSI)
+ void *ssi; /* SSI descriptor */
+#endif /* NO_SSI */
+};
+
+enum {
+ OPT_ROOT, OPT_INDEX_FILES, OPT_PORTS, OPT_DIR_LIST,
+ OPT_CGI_EXTENSIONS, OPT_CGI_INTERPRETER, OPT_CGI_ENVIRONMENT,
+ OPT_SSI_EXTENSIONS, OPT_AUTH_REALM, OPT_AUTH_GPASSWD,
+ OPT_AUTH_PUT, OPT_ACCESS_LOG, OPT_ERROR_LOG, OPT_MIME_TYPES,
+ OPT_SSL_CERTIFICATE, OPT_ALIASES, OPT_ACL, OPT_INETD, OPT_UID,
+ OPT_CFG_URI, OPT_PROTECT, OPT_SERVICE, OPT_HIDE, OPT_THREADS,
+ NUM_OPTIONS
+};
+
+/*
+ * SHTTPD context
+ */
+struct shttpd_ctx {
+ SSL_CTX *ssl_ctx; /* SSL context */
+
+ struct llhead registered_uris;/* User urls */
+ struct llhead uri_auths; /* User auth files */
+ struct llhead error_handlers; /* Embedded error handlers */
+ struct llhead acl; /* Access control list */
+ struct llhead ssi_funcs; /* SSI callback functions */
+ struct llhead listeners; /* Listening sockets */
+ struct llhead workers; /* Worker workers */
+
+ FILE *access_log; /* Access log stream */
+ FILE *error_log; /* Error log stream */
+
+ char *options[NUM_OPTIONS]; /* Configurable options */
+#if defined(__rtems__)
+ rtems_id mutex;
+#endif /* _WIN32 */
+};
+
+struct listener {
+ struct llhead link;
+ struct shttpd_ctx *ctx; /* Context that socket belongs */
+ int sock; /* Listening socket */
+ int is_ssl; /* Should be SSL-ed */
+};
+
+/* Types of messages that could be sent over the control socket */
+enum {CTL_PASS_SOCKET, CTL_WAKEUP};
+
+/*
+ * In SHTTPD, list of values are represented as comma or space separated
+ * string. For example, list of CGI extensions can be represented as
+ * ".cgi,.php,.pl", or ".cgi .php .pl". The macro that follows allows to
+ * loop through the individual values in that list.
+ *
+ * A "const char *" pointer and size_t variable must be passed to the macro.
+ * Spaces or commas can be used as delimiters (macro DELIM_CHARS).
+ *
+ * In every iteration of the loop, "s" points to the current value, and
+ * "len" specifies its length. The code inside loop must not change
+ * "s" and "len" parameters.
+ */
+#define FOR_EACH_WORD_IN_LIST(s,len) \
+ for (; s != NULL && (len = strcspn(s, DELIM_CHARS)) != 0; \
+ s += len, s+= strspn(s, DELIM_CHARS))
+
+/*
+ * IPv4 ACL entry. Specifies subnet with deny/allow flag
+ */
+struct acl {
+ struct llhead link;
+ uint32_t ip; /* IP, in network byte order */
+ uint32_t mask; /* Also in network byte order */
+ int flag; /* Either '+' or '-' */
+};
+
+/*
+ * shttpd.c
+ */
+extern time_t _shttpd_current_time; /* Current UTC time */
+extern int _shttpd_tz_offset; /* Offset from GMT time zone */
+extern const struct vec _shttpd_known_http_methods[];
+
+extern void _shttpd_stop_stream(struct stream *stream);
+extern int _shttpd_url_decode(const char *, int, char *dst, int);
+extern void _shttpd_send_server_error(struct conn *, int, const char *);
+extern int _shttpd_get_headers_len(const char *buf, size_t buflen);
+extern void _shttpd_parse_headers(const char *s, int, struct headers *);
+extern int _shttpd_is_true(const char *str);
+extern int _shttpd_socketpair(int pair[2]);
+extern void _shttpd_get_mime_type(struct shttpd_ctx *,
+ const char *, int, struct vec *);
+
+#define IS_TRUE(ctx, opt) _shttpd_is_true((ctx)->options[opt])
+
+/*
+ * config.c
+ */
+extern void _shttpd_usage(const char *prog);
+
+/*
+ * log.c
+ */
+extern void _shttpd_elog(int flags, struct conn *c, const char *fmt, ...);
+extern void _shttpd_log_access(FILE *fp, const struct conn *c);
+
+/*
+ * string.c
+ */
+extern void _shttpd_strlcpy(register char *, register const char *, size_t);
+extern int _shttpd_strncasecmp(register const char *,
+ register const char *, size_t);
+extern char *_shttpd_strndup(const char *ptr, size_t len);
+extern char *_shttpd_strdup(const char *str);
+extern int _shttpd_snprintf(char *buf, size_t len, const char *fmt, ...);
+extern int _shttpd_match_extension(const char *path, const char *ext_list);
+
+/*
+ * compat_*.c
+ */
+extern void _shttpd_set_close_on_exec(int fd);
+extern int _shttpd_set_non_blocking_mode(int fd);
+extern int _shttpd_stat(const char *, struct stat *stp);
+extern int _shttpd_open(const char *, int flags, int mode);
+extern int _shttpd_remove(const char *);
+extern int _shttpd_rename(const char *, const char *);
+extern int _shttpd_mkdir(const char *, int);
+extern char * _shttpd_getcwd(char *, int);
+extern int _shttpd_spawn_process(struct conn *c, const char *prog,
+ char *envblk, char *envp[], int sock, const char *dir);
+
+extern int _shttpd_set_nt_service(struct shttpd_ctx *, const char *);
+extern int _shttpd_set_systray(struct shttpd_ctx *, const char *);
+extern void _shttpd_try_to_run_as_nt_service(void);
+
+/*
+ * io_*.c
+ */
+extern const struct io_class _shttpd_io_file;
+extern const struct io_class _shttpd_io_socket;
+extern const struct io_class _shttpd_io_ssl;
+extern const struct io_class _shttpd_io_cgi;
+extern const struct io_class _shttpd_io_dir;
+extern const struct io_class _shttpd_io_embedded;
+extern const struct io_class _shttpd_io_ssi;
+
+extern int _shttpd_put_dir(const char *path);
+extern void _shttpd_get_dir(struct conn *c);
+extern void _shttpd_get_file(struct conn *c, struct stat *stp);
+extern void _shttpd_ssl_handshake(struct stream *stream);
+extern void _shttpd_setup_embedded_stream(struct conn *,
+ union variant, void *);
+extern struct registered_uri *_shttpd_is_registered_uri(struct shttpd_ctx *,
+ const char *uri);
+extern void _shttpd_do_ssi(struct conn *);
+extern void _shttpd_ssi_func_destructor(struct llhead *lp);
+
+/*
+ * auth.c
+ */
+extern int _shttpd_check_authorization(struct conn *c, const char *path);
+extern int _shttpd_is_authorized_for_put(struct conn *c);
+extern void _shttpd_send_authorization_request(struct conn *c);
+extern int _shttpd_edit_passwords(const char *fname, const char *domain,
+ const char *user, const char *pass);
+
+/*
+ * cgi.c
+ */
+extern int _shttpd_run_cgi(struct conn *c, const char *prog);
+extern void _shttpd_do_cgi(struct conn *c);
+
+#define CGI_REPLY "HTTP/1.1 OK\r\n"
+#define CGI_REPLY_LEN (sizeof(CGI_REPLY) - 1)
+
+#endif /* DEFS_HEADER_DEFINED */
diff --git a/src/server/shttpd/io.h b/src/server/shttpd/io.h
index 5439917..d774cc2 100644
--- a/src/server/shttpd/io.h
+++ b/src/server/shttpd/io.h
@@ -14,14 +14,6 @@
#include <assert.h>
#include <stddef.h>
-#ifdef __GNUC__
-#define __INLINE__ __inline__
-#elif _WIN32
-#define __INLINE__ __inline
-#elif defined (__SUNPRO_C) || defined (__SUNPRO_CC)
-#define __INLINE__ inline
-#endif
-
/*
* I/O buffer descriptor
*/
@@ -33,7 +25,7 @@
size_t total; /* Total bytes read */
};
-static __INLINE__ void
+static __inline void
io_clear(struct io *io)
{
assert(io->buf != NULL);
@@ -41,7 +33,7 @@
io->total = io->tail = io->head = 0;
}
-static __INLINE__ char *
+static __inline char *
io_space(struct io *io)
{
assert(io->buf != NULL);
@@ -50,7 +42,7 @@
return (io->buf + io->head);
}
-static __INLINE__ char *
+static __inline char *
io_data(struct io *io)
{
assert(io->buf != NULL);
@@ -59,7 +51,7 @@
return (io->buf + io->tail);
}
-static __INLINE__ size_t
+static __inline size_t
io_space_len(const struct io *io)
{
assert(io->buf != NULL);
@@ -68,7 +60,7 @@
return (io->size - io->head);
}
-static __INLINE__ size_t
+static __inline size_t
io_data_len(const struct io *io)
{
assert(io->buf != NULL);
@@ -78,7 +70,7 @@
return (io->head - io->tail);
}
-static __INLINE__ void
+static __inline void
io_inc_tail(struct io *io, size_t n)
{
assert(io->buf != NULL);
@@ -91,7 +83,7 @@
io->head = io->tail = 0;
}
-static __INLINE__ void
+static __inline void
io_inc_head(struct io *io, size_t n)
{
assert(io->buf != NULL);
diff --git a/src/server/shttpd/io_cgi.c b/src/server/shttpd/io_cgi.c
new file mode 100644
index 0000000..b41c600
--- /dev/null
+++ b/src/server/shttpd/io_cgi.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#include "defs.h"
+
+static int
+write_cgi(struct stream *stream, const void *buf, size_t len)
+{
+ assert(stream->chan.sock != -1);
+ assert(stream->flags & FLAG_W);
+
+ return (send(stream->chan.sock, buf, len, 0));
+}
+
+static int
+read_cgi(struct stream *stream, void *buf, size_t len)
+{
+ struct headers parsed;
+ char status[4];
+ int n;
+
+ assert(stream->chan.sock != -1);
+ assert(stream->flags & FLAG_R);
+
+ stream->flags &= ~FLAG_DONT_CLOSE;
+
+ n = recv(stream->chan.sock, buf, len, 0);
+
+ if (stream->flags & FLAG_HEADERS_PARSED)
+ return (n);
+
+ if (n <= 0 && ERRNO != EWOULDBLOCK) {
+ _shttpd_send_server_error(stream->conn, 500,
+ "Error running CGI");
+ return (n);
+ }
+
+ /*
+ * CGI script may output Status: and Location: headers, which
+ * may alter the status code. Buffer in headers, parse
+ * them, send correct status code and then forward all data
+ * from CGI script back to the remote end.
+ * Reply line was alredy appended to the IO buffer in
+ * decide_what_to_do(), with blank status code.
+ */
+
+ stream->flags |= FLAG_DONT_CLOSE;
+ io_inc_head(&stream->io, n);
+
+ stream->headers_len = _shttpd_get_headers_len(stream->io.buf,
+ stream->io.head);
+ if (stream->headers_len < 0) {
+ stream->flags &= ~FLAG_DONT_CLOSE;
+ _shttpd_send_server_error(stream->conn, 500,
+ "Bad headers sent");
+ _shttpd_elog(E_LOG, stream->conn,
+ "CGI script sent invalid headers: "
+ "[%.*s]", stream->io.head - CGI_REPLY_LEN,
+ stream->io.buf + CGI_REPLY_LEN);
+ return (0);
+ }
+
+ /*
+ * If we did not received full headers yet, we must not send any
+ * data read from the CGI back to the client. Suspend sending by
+ * setting tail = head, which tells that there is no data in IO buffer
+ */
+
+ if (stream->headers_len == 0) {
+ stream->io.tail = stream->io.head;
+ return (0);
+ }
+
+ /* Received all headers. Set status code for the connection. */
+ (void) memset(&parsed, 0, sizeof(parsed));
+ _shttpd_parse_headers(stream->io.buf, stream->headers_len, &parsed);
+ stream->content_len = parsed.cl.v_big_int;
+ stream->conn->status = (int) parsed.status.v_big_int;
+
+ /* If script outputs 'Location:' header, set status code to 302 */
+ if (parsed.location.v_vec.len > 0)
+ stream->conn->status = 302;
+
+ /*
+ * If script did not output neither 'Location:' nor 'Status' headers,
+ * set the default status code 200, which means 'success'.
+ */
+ if (stream->conn->status == 0)
+ stream->conn->status = 200;
+
+ /* Append the status line to the beginning of the output */
+ (void) _shttpd_snprintf(status,
+ sizeof(status), "%3d", stream->conn->status);
+ (void) memcpy(stream->io.buf + 9, status, 3);
+ DBG(("read_cgi: content len %lu status %s",
+ stream->content_len, status));
+
+ /* Next time, pass output directly back to the client */
+ assert((big_int_t) stream->headers_len <= stream->io.total);
+ stream->io.total -= stream->headers_len;
+ stream->io.tail = 0;
+ stream->flags |= FLAG_HEADERS_PARSED;
+
+ /* Return 0 because we've already shifted the head */
+ return (0);
+}
+
+static void
+close_cgi(struct stream *stream)
+{
+ assert(stream->chan.sock != -1);
+ (void) closesocket(stream->chan.sock);
+}
+
+const struct io_class _shttpd_io_cgi = {
+ "cgi",
+ read_cgi,
+ write_cgi,
+ close_cgi
+};
diff --git a/src/server/shttpd/io_dir.c b/src/server/shttpd/io_dir.c
index d8ed661..7366398 100644
--- a/src/server/shttpd/io_dir.c
+++ b/src/server/shttpd/io_dir.c
@@ -8,7 +8,7 @@
* this stuff is worth it, you can buy me a beer in return.
*/
-#include "shttpd_defs.h"
+#include "defs.h"
/*
* For a given PUT path, create all intermediate subdirectories
@@ -16,7 +16,7 @@
* or -1 on error, 1 if OK.
*/
int
-put_dir(const char *path)
+_shttpd_put_dir(const char *path)
{
char buf[FILENAME_MAX];
const char *s, *p;
@@ -30,7 +30,8 @@
buf[len] = '\0';
/* Try to create intermediate directory */
- if (my_stat(buf, &st) == -1 && my_mkdir(buf, 0755) != 0)
+ if (_shttpd_stat(buf, &st) == -1 &&
+ _shttpd_mkdir(buf, 0755) != 0)
return (-1);
/* Is path itself a directory ? */
@@ -44,6 +45,8 @@
static int
read_dir(struct stream *stream, void *buf, size_t len)
{
+ static const char footer[] = "</table></body></html>\n";
+
struct dirent *dp = NULL;
char file[FILENAME_MAX], line[FILENAME_MAX + 512],
size[64], mod[64];
@@ -59,10 +62,8 @@
if (len < sizeof(line))
break;
- if ((dp = readdir(stream->chan.dir.dirp)) == NULL) {
- stream->flags |= FLAG_CLOSED;
+ if ((dp = readdir(stream->chan.dir.dirp)) == NULL)
break;
- }
DBG(("read_dir: %s", dp->d_name));
/* Do not show current dir and passwords file */
@@ -70,26 +71,27 @@
strcmp(dp->d_name, HTPASSWD) == 0)
continue;
- (void) snprintf(file, sizeof(file),
+ (void) _shttpd_snprintf(file, sizeof(file),
"%s%s%s", stream->chan.dir.path, slash, dp->d_name);
- (void) my_stat(file, &st);
+ (void) _shttpd_stat(file, &st);
if (S_ISDIR(st.st_mode)) {
- snprintf(size,sizeof(size),"%s","<DIR>");
+ _shttpd_snprintf(size,sizeof(size),"%s","<DIR>");
} else {
if (st.st_size < 1024)
- (void) snprintf(size, sizeof(size),
+ (void) _shttpd_snprintf(size, sizeof(size),
"%lu", (unsigned long) st.st_size);
else if (st.st_size < 1024 * 1024)
- (void) snprintf(size, sizeof(size), "%luk",
+ (void) _shttpd_snprintf(size,
+ sizeof(size), "%luk",
(unsigned long) (st.st_size >> 10) + 1);
else
- (void) snprintf(size, sizeof(size),
+ (void) _shttpd_snprintf(size, sizeof(size),
"%.1fM", (float) st.st_size / 1048576);
}
(void) strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M",
localtime(&st.st_mtime));
- n = snprintf(line, sizeof(line),
+ n = _shttpd_snprintf(line, sizeof(line),
"<tr><td><a href=\"%s%s%s\">%s%s</a></td>"
"<td> %s</td><td> %s</td></tr>\n",
c->uri, slash, dp->d_name, dp->d_name,
@@ -100,6 +102,13 @@
len -= n;
} while (dp != NULL);
+ /* Append proper HTML footer for the page */
+ if (dp == NULL && len >= sizeof(footer)) {
+ (void) memcpy(buf, footer, sizeof(footer));
+ nwritten += sizeof(footer);
+ stream->flags |= FLAG_CLOSED;
+ }
+
return (nwritten);
}
@@ -113,14 +122,15 @@
}
void
-get_dir(struct conn *c)
+_shttpd_get_dir(struct conn *c)
{
if ((c->loc.chan.dir.dirp = opendir(c->loc.chan.dir.path)) == NULL) {
(void) free(c->loc.chan.dir.path);
- send_server_error(c, 500, "Cannot open directory");
+ _shttpd_send_server_error(c, 500, "Cannot open directory");
} else {
- c->loc.io.head = snprintf(c->loc.io.buf, c->loc.io.size,
+ c->loc.io.head = _shttpd_snprintf(c->loc.io.buf, c->loc.io.size,
"HTTP/1.1 200 OK\r\n"
+ "Connection: close\r\n"
"Content-Type: text/html; charset=utf-8\r\n\r\n"
"<html><head><title>Index of %s</title>"
"<style>th {text-align: left;}</style></head>"
@@ -130,12 +140,12 @@
c->uri, c->uri);
io_clear(&c->rem.io);
c->status = 200;
- c->loc.io_class = &io_dir;
+ c->loc.io_class = &_shttpd_io_dir;
c->loc.flags |= FLAG_R | FLAG_ALWAYS_READY;
}
}
-const struct io_class io_dir = {
+const struct io_class _shttpd_io_dir = {
"dir",
read_dir,
NULL,
diff --git a/src/server/shttpd/io_emb.c b/src/server/shttpd/io_emb.c
index ee40a8b..e2d7f69 100644
--- a/src/server/shttpd/io_emb.c
+++ b/src/server/shttpd/io_emb.c
@@ -8,17 +8,12 @@
* this stuff is worth it, you can buy me a beer in return.
*/
-#include "shttpd_defs.h"
+#include "defs.h"
-#ifdef ENABLE_IPV6
-extern int wsmand_options_get_use_ipv6(void);
-#endif
-
-#if defined(EMBEDDED)
const char *
shttpd_version(void)
{
- return (VERSION);
+ return (SHTTPD_VERSION);
}
static void
@@ -33,13 +28,11 @@
arg->in.len = io_data_len(&c->rem.io);
arg->in.num_bytes = 0;
- if (io_data_len(&c->rem.io) >= c->rem.io.size) {
+ if (io_data_len(&c->rem.io) >= c->rem.io.size)
arg->flags |= SHTTPD_POST_BUFFER_FULL;
- }
- if (c->rem.content_len > 0 && c->rem.io.total < c->rem.content_len) {
- arg->flags |= SHTTPD_MORE_POST_DATA;
- }
+ if (c->rem.content_len > 0 && c->rem.io.total < c->rem.content_len)
+ arg->flags |= SHTTPD_MORE_POST_DATA;
func(arg);
@@ -56,15 +49,12 @@
*/
if (arg->flags & SHTTPD_END_OF_OUTPUT)
- {
- c->loc.flags |= FLAG_RESPONSE_COMPLETE;
c->loc.flags &= ~FLAG_DONT_CLOSE;
-}
else
-{
-c->loc.flags &= ~FLAG_RESPONSE_COMPLETE;
c->loc.flags |= FLAG_DONT_CLOSE;
-}
+
+ if (arg->flags & SHTTPD_SUSPEND)
+ c->loc.flags |= FLAG_SUSPEND;
}
static int
@@ -103,14 +93,10 @@
size_t
shttpd_printf(struct shttpd_arg *arg, const char *fmt, ...)
{
- struct conn *c = arg->priv;
- struct io *io = &c->loc.io;
char *buf = arg->out.buf + arg->out.num_bytes;
int buflen = arg->out.len - arg->out.num_bytes, len = 0;
va_list ap;
- assert(buf <= io->buf + io->size);
-
if (buflen > 0) {
va_start(ap, fmt);
len = vsnprintf(buf, buflen, fmt, ap);
@@ -138,7 +124,7 @@
while (p < e) {
if ((s = strchr(p, '\n')) != NULL)
s[s[-1] == '\r' ? -1 : 0] = '\0';
- if (strncasecmp(header_name, p, len) == 0)
+ if (_shttpd_strncasecmp(header_name, p, len) == 0)
return (p + len + 2);
p += strlen(p) + 1;
@@ -154,7 +140,7 @@
struct vec *vec;
if (strcmp(env_name, "REQUEST_METHOD") == 0) {
- return (known_http_methods[c->method].ptr);
+ return (_shttpd_known_http_methods[c->method].ptr);
} else if (strcmp(env_name, "REQUEST_URI") == 0) {
return (c->uri);
} else if (strcmp(env_name, "QUERY_STRING") == 0) {
@@ -166,25 +152,15 @@
return (vec->ptr);
}
} else if (strcmp(env_name, "REMOTE_ADDR") == 0) {
-#ifdef ENABLE_IPV6
- if (wsmand_options_get_use_ipv6()) {
- static char str[INET6_ADDRSTRLEN];
- inet_ntop( AF_INET6,&c->sa.u.sin6.sin6_addr, str, sizeof(str));
- return (const char*)str;
- }
- else {
-#endif
- return (inet_ntoa(c->sa.u.sin.sin_addr));/* FIXME NOT MT safe */
-#ifdef ENABLE_IPV6
- }
-#endif
+ return (inet_ntoa(c->sa.u.sin.sin_addr));/* FIXME NOT MT safe */
}
return (NULL);
}
void
-shttpd_get_http_version(struct shttpd_arg *arg, unsigned long *major, unsigned long *minor)
+shttpd_get_http_version(struct shttpd_arg *arg,
+ unsigned long *major, unsigned long *minor)
{
struct conn *c = arg->priv;
@@ -199,47 +175,39 @@
struct registered_uri *e;
if ((e = malloc(sizeof(*e))) != NULL) {
- e->uri = strdup(uri);
+ e->uri = _shttpd_strdup(uri);
e->callback.v_func = (void (*)(void)) callback;
e->callback_data = data;
LL_TAIL(&ctx->registered_uris, &e->link);
}
}
-#if 0
-struct shttpd_ctx *
-shttpd_init2(const char *config_file, char *names[], char *values[], size_t n)
-{
- size_t i;
-
- for (i = 0; i < n; i++)
- set_option(names[i], values[i]);
-
- return (init_ctx(config_file, 0, NULL));
-}
-#endif
-
void
shttpd_protect_uri(struct shttpd_ctx *ctx, const char *uri, const char *file,
- basic_auth_callback cb, int type)
+ basic_auth_callback cb, int type)
{
- struct uri_auth *auth;
+ struct uri_auth *auth;
- if ((auth = malloc(sizeof(*auth))) != NULL) {
- auth->uri = strdup(uri);
- if (file)
- auth->file_name = strdup(file);
- else
- auth->file_name = NULL;
- if (cb)
- auth->callback.v_func = (void (*)(void)) cb;
- auth->uri_len = strlen(uri);
- if (type == BASIC_AUTH || type == DIGEST_AUTH)
- auth->type = type;
- else
- auth->type = DIGEST_AUTH;
- LL_ADD(&ctx->uri_auths, &auth->link);
- }
+ if ((auth = malloc(sizeof(*auth))) != NULL) {
+ auth->uri = strdup(uri);
+ if (file) {
+ auth->file_name = strdup(file);
+ }
+ else {
+ auth->file_name = NULL;
+ }
+ if (cb) {
+ auth->callback.v_func = (void (*)(void)) cb;
+ }
+ auth->uri_len = strlen(uri);
+ if (type == BASIC_AUTH || type == DIGEST_AUTH) {
+ auth->type = type;
+ }
+ else {
+ auth->type = DIGEST_AUTH;
+ }
+ LL_ADD(&ctx->uri_auths, &auth->link);
+ }
}
int
@@ -256,7 +224,7 @@
for (p = buf; p + var_len < e; p++)
if ((p == buf || p[-1] == '&') &&
p[var_len] == '=' &&
- !strncasecmp(var, p, var_len)) {
+ !_shttpd_strncasecmp(var, p, var_len)) {
/* Point 'p' to var value, 's' to the end of value */
p += var_len + 1;
@@ -264,7 +232,7 @@
s = e;
/* URL-decode value. Return result length */
- return (url_decode(p, s - p, value, value_len));
+ return (_shttpd_url_decode(p, s - p, value, value_len));
}
return (-1);
@@ -289,7 +257,7 @@
}
struct registered_uri *
-is_registered_uri(struct shttpd_ctx *ctx, const char *uri)
+_shttpd_is_registered_uri(struct shttpd_ctx *ctx, const char *uri)
{
struct llhead *lp;
struct registered_uri *reg_uri;
@@ -304,12 +272,12 @@
}
void
-setup_embedded_stream(struct conn *c, union variant func, void *data)
+_shttpd_setup_embedded_stream(struct conn *c, union variant func, void *data)
{
c->loc.chan.emb.state = NULL;
c->loc.chan.emb.func = func;
c->loc.chan.emb.data = data;
- c->loc.io_class = &io_embedded;
+ c->loc.io_class = &_shttpd_io_embedded;
c->loc.flags |= FLAG_R | FLAG_W |FLAG_ALWAYS_READY;
}
@@ -327,11 +295,25 @@
}
}
-const struct io_class io_embedded = {
+void
+shttpd_wakeup(const void *priv)
+{
+ const struct conn *conn = priv;
+ char buf[sizeof(int) + sizeof(void *)];
+ int cmd = CTL_WAKEUP;
+
+#if 0
+ conn->flags &= ~SHTTPD_SUSPEND;
+#endif
+ (void) memcpy(buf, &cmd, sizeof(cmd));
+ (void) memcpy(buf + sizeof(cmd), conn, sizeof(conn));
+
+ (void) send(conn->worker->ctl[1], buf, sizeof(buf), 0);
+}
+
+const struct io_class _shttpd_io_embedded = {
"embedded",
do_embedded,
(int (*)(struct stream *, const void *, size_t)) do_embedded,
close_embedded
};
-
-#endif /* EMBEDDED */
diff --git a/src/server/shttpd/io_file.c b/src/server/shttpd/io_file.c
index fefb2b6..75e879b 100644
--- a/src/server/shttpd/io_file.c
+++ b/src/server/shttpd/io_file.c
@@ -8,7 +8,7 @@
* this stuff is worth it, you can buy me a beer in return.
*/
-#include "shttpd_defs.h"
+#include "defs.h"
static int
write_file(struct stream *stream, const void *buf, size_t len)
@@ -20,16 +20,16 @@
assert(fd != -1);
n = write(fd, buf, len);
- DBG(("put_file(%p, %d): %d bytes", (void *) stream, len, n));
+ DBG(("put_file(%p, %d): %d bytes", (void *) stream, (int) len, n));
- if (n <= 0 || (rem->io.total >= (big_int_t) rem->headers_len)) {
+ if (n <= 0 || (rem->io.total >= (big_int_t) rem->content_len)) {
(void) fstat(fd, &st);
stream->io.head = stream->headers_len =
- snprintf(stream->io.buf,
+ _shttpd_snprintf(stream->io.buf,
stream->io.size, "HTTP/1.1 %d OK\r\n"
"Content-Length: %lu\r\nConnection: close\r\n\r\n",
stream->conn->status, st.st_size);
- stop_stream(stream);
+ _shttpd_stop_stream(stream);
}
return (n);
@@ -90,16 +90,17 @@
}
void
-get_file(struct conn *c, struct stat *stp)
+_shttpd_get_file(struct conn *c, struct stat *stp)
{
char date[64], lm[64], etag[64], range[64] = "";
- int n, status = 200;
+ size_t n, status = 200;
unsigned long r1, r2;
const char *fmt = "%a, %d %b %Y %H:%M:%S GMT", *msg = "OK";
big_int_t cl; /* Content-Length */
- if (c->mime_type == NULL)
- c->mime_type = get_mime_type(c->ctx, c->uri, strlen(c->uri));
+ if (c->mime_type.len == 0)
+ _shttpd_get_mime_type(c->ctx, c->uri,
+ strlen(c->uri), &c->mime_type);
cl = (big_int_t) stp->st_size;
/* If Range: header specified, act accordingly */
@@ -108,16 +109,17 @@
status = 206;
(void) lseek(c->loc.chan.fd, r1, SEEK_SET);
cl = n == 2 ? r2 - r1 + 1: cl - r1;
- (void) snprintf(range, sizeof(range),
+ (void) _shttpd_snprintf(range, sizeof(range),
"Content-Range: bytes %lu-%lu/%lu\r\n",
r1, r1 + cl - 1, (unsigned long) stp->st_size);
msg = "Partial Content";
}
/* Prepare Etag, Date, Last-Modified headers */
- (void) strftime(date, sizeof(date), fmt, localtime(¤t_time));
+ (void) strftime(date, sizeof(date),
+ fmt, localtime(&_shttpd_current_time));
(void) strftime(lm, sizeof(lm), fmt, localtime(&stp->st_mtime));
- (void) snprintf(etag, sizeof(etag), "%lx.%lx",
+ (void) _shttpd_snprintf(etag, sizeof(etag), "%lx.%lx",
(unsigned long) stp->st_mtime, (unsigned long) stp->st_size);
/*
@@ -125,28 +127,29 @@
* member in io. We want 'total' to be equal to the content size,
* and exclude the headers length from it.
*/
- c->loc.io.head = c->loc.headers_len = snprintf(c->loc.io.buf,
+ c->loc.io.head = c->loc.headers_len = _shttpd_snprintf(c->loc.io.buf,
c->loc.io.size,
"HTTP/1.1 %d %s\r\n"
"Date: %s\r\n"
"Last-Modified: %s\r\n"
"Etag: \"%s\"\r\n"
- "Content-Type: %s\r\n"
+ "Content-Type: %.*s\r\n"
"Content-Length: %lu\r\n"
- "Connection: close\r\n"
+ "Accept-Ranges: bytes\r\n"
"%s\r\n",
- status, msg, date, lm, etag, c->mime_type, cl, range);
+ status, msg, date, lm, etag,
+ c->mime_type.len, c->mime_type.ptr, cl, range);
c->status = status;
c->loc.content_len = cl;
- c->loc.io_class = &io_file;
+ c->loc.io_class = &_shttpd_io_file;
c->loc.flags |= FLAG_R | FLAG_ALWAYS_READY;
if (c->method == METHOD_HEAD)
- stop_stream(&c->loc);
+ _shttpd_stop_stream(&c->loc);
}
-const struct io_class io_file = {
+const struct io_class _shttpd_io_file = {
"file",
read_file,
write_file,
diff --git a/src/server/shttpd/io_socket.c b/src/server/shttpd/io_socket.c
index 3e183e6..20bf207 100644
--- a/src/server/shttpd/io_socket.c
+++ b/src/server/shttpd/io_socket.c
@@ -8,7 +8,7 @@
* this stuff is worth it, you can buy me a beer in return.
*/
-#include "shttpd_defs.h"
+#include "defs.h"
static int
read_socket(struct stream *stream, void *buf, size_t len)
@@ -28,11 +28,10 @@
close_socket(struct stream *stream)
{
assert(stream->chan.sock != -1);
- shutdown(stream->chan.sock,SHUT_RDWR);
(void) closesocket(stream->chan.sock);
}
-const struct io_class io_socket = {
+const struct io_class _shttpd_io_socket = {
"socket",
read_socket,
write_socket,
diff --git a/src/server/shttpd/io_ssi.c b/src/server/shttpd/io_ssi.c
new file mode 100644
index 0000000..750b059
--- /dev/null
+++ b/src/server/shttpd/io_ssi.c
@@ -0,0 +1,488 @@
+/*
+ * Copyright (c) 2006,2007 Steven Johnson <sjohnson@sakuraindustries.com>
+ * Copyright (c) 2007 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#include "defs.h"
+
+#if !defined(NO_SSI)
+
+#define CMDBUFSIZ 512 /* SSI command buffer size */
+#define NEST_MAX 6 /* Maximum nesting level */
+
+struct ssi_func {
+ struct llhead link;
+ void *user_data;
+ char *name;
+ shttpd_callback_t func;
+};
+
+struct ssi_inc {
+ int state; /* Buffering state */
+ int cond; /* Conditional state */
+ FILE *fp; /* Icluded file stream */
+ char buf[CMDBUFSIZ]; /* SSI command buffer */
+ size_t nbuf; /* Bytes in a command buffer */
+ FILE *pipe; /* #exec stream */
+ struct ssi_func func; /* #call function */
+};
+
+struct ssi {
+ struct conn *conn; /* Connection we belong to */
+ int nest; /* Current nesting level */
+ struct ssi_inc incs[NEST_MAX]; /* Nested includes */
+};
+
+enum { SSI_PASS, SSI_BUF, SSI_EXEC, SSI_CALL };
+enum { SSI_GO, SSI_STOP }; /* Conditional states */
+
+static const struct vec st = {"<!--#", 5};
+
+void
+shttpd_register_ssi_func(struct shttpd_ctx *ctx, const char *name,
+ shttpd_callback_t func, void *user_data)
+{
+ struct ssi_func *e;
+
+ if ((e = malloc(sizeof(*e))) != NULL) {
+ e->name = _shttpd_strdup(name);
+ e->func = func;
+ e->user_data = user_data;
+ LL_TAIL(&ctx->ssi_funcs, &e->link);
+ }
+}
+
+void
+_shttpd_ssi_func_destructor(struct llhead *lp)
+{
+ struct ssi_func *e = LL_ENTRY(lp, struct ssi_func, link);
+
+ free(e->name);
+ free(e);
+}
+
+static const struct ssi_func *
+find_ssi_func(struct ssi *ssi, const char *name)
+{
+ struct ssi_func *e;
+ struct llhead *lp;
+
+ LL_FOREACH(&ssi->conn->ctx->ssi_funcs, lp) {
+ e = LL_ENTRY(lp, struct ssi_func, link);
+ if (!strcmp(name, e->name))
+ return (e);
+ }
+
+ return (NULL);
+}
+
+static void
+call(struct ssi *ssi, const char *name,
+ struct shttpd_arg *arg, char *buf, int len)
+{
+ const struct ssi_func *ssi_func;
+
+ (void) memset(arg, 0, sizeof(*arg));
+
+ /*
+ * SSI function may be called with parameters. These parameters
+ * are passed as arg->in.buf, arg->in.len vector.
+ */
+ arg->in.buf = strchr(name, ' ');
+ if (arg->in.buf != NULL) {
+ *arg->in.buf++ = '\0';
+ arg->in.len = strlen(arg->in.buf);
+ }
+
+ if ((ssi_func = find_ssi_func(ssi, name)) != NULL) {
+ arg->priv = ssi->conn;
+ arg->user_data = ssi_func->user_data;
+ arg->out.buf = buf;
+ arg->out.len = len;
+ ssi_func->func(arg);
+ }
+}
+
+static int
+evaluate(struct ssi *ssi, const char *name)
+{
+ struct shttpd_arg arg;
+
+ call(ssi, name, &arg, NULL, 0);
+
+ return (arg.flags & SHTTPD_SSI_EVAL_TRUE);
+}
+
+static void
+pass(struct ssi_inc *inc, void *buf, int *n)
+{
+ if (inc->cond == SSI_GO) {
+ (void) memcpy(buf, inc->buf, inc->nbuf);
+ (*n) += inc->nbuf;
+ }
+ inc->nbuf = 0;
+ inc->state = SSI_PASS;
+}
+
+static int
+get_path(struct conn *conn, const char *src,
+ int src_len, char *dst, int dst_len)
+{
+ static struct vec accepted[] = {
+ {"\"", 1}, /* Relative to webserver CWD */
+ {"file=\"", 6}, /* Relative to current URI */
+ {"virtual=\"", 9}, /* Relative to document root */
+ {NULL, 0},
+ };
+ struct vec *vec;
+ const char *p, *root = conn->ctx->options[OPT_ROOT];
+ int len;
+
+ for (vec = accepted; vec->len > 0; vec++)
+ if (src_len > vec->len && !memcmp(src, vec->ptr, vec->len)) {
+ src += vec->len;
+ src_len -= vec->len;
+ if ((p = memchr(src, '"', src_len)) == NULL)
+ break;
+ if (vec->len == 6) {
+ len = _shttpd_snprintf(dst, dst_len, "%s%c%s",
+ root, DIRSEP, conn->uri);
+ while (len > 0 && dst[len] != '/')
+ len--;
+ dst += len;
+ dst_len -= len;
+ } else if (vec->len == 9) {
+ len = _shttpd_snprintf(dst, dst_len, "%s%c",
+ root, DIRSEP);
+ dst += len;
+ dst_len -= len;
+ }
+ _shttpd_url_decode(src, p - src, dst, dst_len);
+ return (1);
+ }
+
+ return (0);
+}
+
+static void
+do_include(struct ssi *ssi)
+{
+ struct ssi_inc *inc = ssi->incs + ssi->nest;
+ char buf[FILENAME_MAX];
+ FILE *fp;
+
+ assert(inc->nbuf >= 13);
+
+ if (inc->cond == SSI_STOP) {
+ /* Do nothing - conditional FALSE */
+ } else if (ssi->nest >= (int) NELEMS(ssi->incs) - 1) {
+ _shttpd_elog(E_LOG, ssi->conn,
+ "ssi: #include: maximum nested level reached");
+ } else if (!get_path(ssi->conn,
+ inc->buf + 13, inc->nbuf - 13, buf, sizeof(buf))) {
+ _shttpd_elog(E_LOG, ssi->conn, "ssi: bad #include: [%.*s]",
+ inc->nbuf, inc->buf);
+ } else if ((fp = fopen(buf, "r")) == NULL) {
+ _shttpd_elog(E_LOG, ssi->conn,
+ "ssi: fopen(%s): %s", buf, strerror(errno));
+ } else {
+ ssi->nest++;
+ ssi->incs[ssi->nest].fp = fp;
+ ssi->incs[ssi->nest].nbuf = 0;
+ ssi->incs[ssi->nest].cond = SSI_GO;
+ }
+}
+
+static char *
+trim_spaces(struct ssi_inc *inc)
+{
+ char *p = inc->buf + inc->nbuf - 2;
+
+ /* Trim spaces from the right */
+ *p-- = '\0';
+ while (isspace(* (unsigned char *) p))
+ *p-- = '\0';
+
+ /* Shift pointer to the start of attributes */
+ for (p = inc->buf; !isspace(* (unsigned char *) p); p++);
+ while (*p && isspace(* (unsigned char *) p)) p++;
+
+ return (p);
+}
+
+static void
+do_if(struct ssi *ssi)
+{
+ struct ssi_inc *inc = ssi->incs + ssi->nest;
+ char *name = trim_spaces(inc);
+
+ inc->cond = evaluate(ssi, name) ? SSI_GO : SSI_STOP;
+}
+
+static void
+do_elif(struct ssi *ssi)
+{
+ struct ssi_inc *inc = ssi->incs + ssi->nest;
+ char *name = trim_spaces(inc);
+
+ if (inc->cond == SSI_STOP && evaluate(ssi, name))
+ inc->cond = SSI_GO;
+ else
+ inc->cond = SSI_STOP;
+}
+static void
+do_endif(struct ssi *ssi)
+{
+ ssi->incs[ssi->nest].cond = SSI_GO;
+}
+
+static void
+do_else(struct ssi *ssi)
+{
+ struct ssi_inc *inc = ssi->incs + ssi->nest;
+
+ inc->cond = inc->cond == SSI_GO ? SSI_STOP : SSI_GO;
+}
+
+static void
+do_call2(struct ssi *ssi, char *buf, int len, int *n)
+{
+ struct ssi_inc *inc = ssi->incs + ssi->nest;
+ struct shttpd_arg arg;
+
+ call(ssi, inc->buf, &arg, buf, len);
+ (*n) += arg.out.num_bytes;
+ if (arg.flags & SHTTPD_END_OF_OUTPUT)
+ inc->state = SSI_PASS;
+}
+
+static void
+do_call(struct ssi *ssi, char *buf, int len, int *n)
+{
+ struct ssi_inc *inc = ssi->incs + ssi->nest;
+ char *name = trim_spaces(inc);
+
+ if (inc->cond == SSI_GO) {
+ (void) memmove(inc->buf, name, strlen(name) + 1);
+ inc->state = SSI_CALL;
+ do_call2(ssi, buf, len, n);
+ }
+}
+
+static void
+do_exec2(struct ssi *ssi, char *buf, int len, int *n)
+{
+ struct ssi_inc *inc = ssi->incs + ssi->nest;
+ int i, ch;
+
+ for (i = 0; i < len; i++) {
+ if ((ch = fgetc(inc->pipe)) == EOF) {
+ inc->state = SSI_PASS;
+ (void) pclose(inc->pipe);
+ inc->pipe = NULL;
+ break;
+ }
+ *buf++ = ch;
+ (*n)++;
+ }
+}
+
+static void
+do_exec(struct ssi *ssi, char *buf, int len, int *n)
+{
+ struct ssi_inc *inc = ssi->incs + ssi->nest;
+ char cmd[sizeof(inc->buf)], *e, *p;
+
+ p = trim_spaces(inc);
+
+ if (inc->cond == SSI_STOP) {
+ /* Do nothing - conditional FALSE */
+ } else if (*p != '"' || (e = strchr(p + 1, '"')) == NULL) {
+ _shttpd_elog(E_LOG, ssi->conn, "ssi: bad exec(%s)", p);
+ } else if (!_shttpd_url_decode(p + 1, e - p - 1, cmd, sizeof(cmd))) {
+ _shttpd_elog(E_LOG, ssi->conn,
+ "ssi: cannot url_decode: exec(%s)", p);
+ } else if ((inc->pipe = popen(cmd, "r")) == NULL) {
+ _shttpd_elog(E_LOG, ssi->conn, "ssi: popen(%s)", cmd);
+ } else {
+ inc->state = SSI_EXEC;
+ do_exec2(ssi, buf, len, n);
+ }
+}
+
+static const struct ssi_cmd {
+ struct vec vec;
+ void (*func)();
+} known_ssi_commands [] = {
+ {{"include ", 8}, do_include },
+ {{"if ", 3}, do_if },
+ {{"elif ", 5}, do_elif },
+ {{"else", 4}, do_else },
+ {{"endif", 5}, do_endif },
+ {{"call ", 5}, do_call },
+ {{"exec ", 5}, do_exec },
+ {{NULL, 0}, NULL }
+};
+
+static void
+do_command(struct ssi *ssi, char *buf, size_t len, int *n)
+{
+ struct ssi_inc *inc = ssi->incs + ssi->nest;
+ const struct ssi_cmd *cmd;
+
+ assert(len > 0);
+ assert(inc->nbuf <= len);
+ inc->state = SSI_PASS;
+
+ for (cmd = known_ssi_commands; cmd->func != NULL; cmd++)
+ if (inc->nbuf > (size_t) st.len + cmd->vec.len &&
+ !memcmp(inc->buf + st.len, cmd->vec.ptr, cmd->vec.len)) {
+ cmd->func(ssi, buf, len, n);
+ break;
+ }
+
+ if (cmd->func == NULL)
+ pass(inc, buf, n);
+
+ inc->nbuf = 0;
+}
+
+static int
+read_ssi(struct stream *stream, void *vbuf, size_t len)
+{
+ struct ssi *ssi = stream->conn->ssi;
+ struct ssi_inc *inc = ssi->incs + ssi->nest;
+ char *buf = vbuf;
+ int ch = EOF, n = 0;
+
+again:
+
+ if (inc->state == SSI_CALL)
+ do_call2(ssi, buf, len, &n);
+ else if (inc->state == SSI_EXEC)
+ do_exec2(ssi, buf, len, &n);
+
+ while (n + inc->nbuf < len && (ch = fgetc(inc->fp)) != EOF)
+
+ switch (inc->state) {
+
+ case SSI_PASS:
+ if (ch == '<') {
+ inc->nbuf = 0;
+ inc->buf[inc->nbuf++] = ch;
+ inc->state = SSI_BUF;
+ } else if (inc->cond == SSI_GO) {
+ buf[n++] = ch;
+ }
+ break;
+
+ /*
+ * We are buffering whole SSI command, until closing "-->".
+ * That means that when do_command() is called, we can rely
+ * on that full command with arguments is buffered in and
+ * there is no need for streaming.
+ * Restrictions:
+ * 1. The command must fit in CMDBUFSIZ
+ * 2. HTML comments inside the command ? Not sure about this.
+ */
+ case SSI_BUF:
+ if (inc->nbuf >= sizeof(inc->buf) - 1) {
+ pass(inc, buf + n, &n);
+ } else if (ch == '>' &&
+ !memcmp(inc->buf + inc->nbuf - 2, "--", 2)) {
+ do_command(ssi, buf + n, len - n, &n);
+ inc = ssi->incs + ssi->nest;
+ } else {
+ inc->buf[inc->nbuf++] = ch;
+
+ /* If not SSI tag, pass it */
+ if (inc->nbuf <= (size_t) st.len &&
+ memcmp(inc->buf, st.ptr, inc->nbuf) != 0)
+ pass(inc, buf + n, &n);
+ }
+ break;
+
+ case SSI_EXEC:
+ case SSI_CALL:
+ break;
+
+ default:
+ /* Never happens */
+ abort();
+ break;
+ }
+
+ if (ssi->nest > 0 && n + inc->nbuf < len && ch == EOF) {
+ (void) fclose(inc->fp);
+ inc->fp = NULL;
+ ssi->nest--;
+ inc--;
+ goto again;
+ }
+
+ return (n);
+}
+
+static void
+close_ssi(struct stream *stream)
+{
+ struct ssi *ssi = stream->conn->ssi;
+ size_t i;
+
+ for (i = 0; i < NELEMS(ssi->incs); i++) {
+ if (ssi->incs[i].fp != NULL)
+ (void) fclose(ssi->incs[i].fp);
+ if (ssi->incs[i].pipe != NULL)
+ (void) pclose(ssi->incs[i].pipe);
+ }
+
+ free(ssi);
+}
+
+void
+_shttpd_do_ssi(struct conn *c)
+{
+ char date[64];
+ struct ssi *ssi;
+
+ (void) strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S GMT",
+ localtime(&_shttpd_current_time));
+
+ c->loc.io.head = c->loc.headers_len = _shttpd_snprintf(c->loc.io.buf,
+ c->loc.io.size,
+ "HTTP/1.1 200 OK\r\n"
+ "Date: %s\r\n"
+ "Content-Type: text/html\r\n"
+ "Connection: close\r\n\r\n",
+ date);
+
+ c->status = 200;
+ c->loc.io_class = &_shttpd_io_ssi;
+ c->loc.flags |= FLAG_R | FLAG_ALWAYS_READY;
+
+ if (c->method == METHOD_HEAD) {
+ _shttpd_stop_stream(&c->loc);
+ } else if ((ssi = calloc(1, sizeof(struct ssi))) == NULL) {
+ _shttpd_send_server_error(c, 500,
+ "Cannot allocate SSI descriptor");
+ } else {
+ ssi->incs[0].fp = fdopen(c->loc.chan.fd, "r");
+ ssi->conn = c;
+ c->ssi = ssi;
+ }
+}
+
+const struct io_class _shttpd_io_ssi = {
+ "ssi",
+ read_ssi,
+ NULL,
+ close_ssi
+};
+
+#endif /* !NO_SSI */
diff --git a/src/server/shttpd/io_ssl.c b/src/server/shttpd/io_ssl.c
index 293d0b5..6de0db2 100644
--- a/src/server/shttpd/io_ssl.c
+++ b/src/server/shttpd/io_ssl.c
@@ -8,7 +8,7 @@
* this stuff is worth it, you can buy me a beer in return.
*/
-#include "shttpd_defs.h"
+#include "defs.h"
#if !defined(NO_SSL)
struct ssl_func ssl_sw[] = {
@@ -25,42 +25,37 @@
{"SSL_library_init", {0}},
{"SSL_CTX_use_PrivateKey_file", {0}},
{"SSL_CTX_use_certificate_file",{0}},
- {"SSL_CTX_free", {0}},
- {"SSL_pending", {0}},
- {"SSL_CTX_use_certificate_chain_file",{0}},
- {"SSL_CTX_ctrl", {0}},
{NULL, {0}}
};
void
-ssl_handshake(struct stream *stream)
+_shttpd_ssl_handshake(struct stream *stream)
{
int n;
- if ((n = SSL_accept(stream->chan.ssl.ssl)) == 0) {
+ if ((n = SSL_accept(stream->chan.ssl.ssl)) == 1) {
+ DBG(("handshake: SSL accepted"));
+ stream->flags |= FLAG_SSL_ACCEPTED;
+ } else {
n = SSL_get_error(stream->chan.ssl.ssl, n);
if (n != SSL_ERROR_WANT_READ && n != SSL_ERROR_WANT_WRITE)
stream->flags |= FLAG_CLOSED;
- elog(E_LOG, stream->conn, "SSL_accept error %d", n);
- } else {
- DBG(("handshake: SSL accepted"));
- stream->flags |= FLAG_SSL_ACCEPTED;
+ DBG(("SSL_accept error %d", n));
}
}
static int
read_ssl(struct stream *stream, void *buf, size_t len)
{
- int nread = 0;
+ int nread = -1;
assert(stream->chan.ssl.ssl != NULL);
if (!(stream->flags & FLAG_SSL_ACCEPTED))
- ssl_handshake(stream);
+ _shttpd_ssl_handshake(stream);
if (stream->flags & FLAG_SSL_ACCEPTED)
nread = SSL_read(stream->chan.ssl.ssl, buf, len);
- ERR_print_errors_fp(stderr);
return (nread);
}
@@ -68,11 +63,8 @@
static int
write_ssl(struct stream *stream, const void *buf, size_t len)
{
- int nwrite = 0;
assert(stream->chan.ssl.ssl != NULL);
- nwrite = SSL_write(stream->chan.ssl.ssl, buf, len);
- ERR_print_errors_fp(stderr);
- return nwrite;
+ return (SSL_write(stream->chan.ssl.ssl, buf, len));
}
static void
@@ -80,12 +72,11 @@
{
assert(stream->chan.ssl.sock != -1);
assert(stream->chan.ssl.ssl != NULL);
- shutdown(stream->chan.ssl.sock,SHUT_RDWR);
(void) closesocket(stream->chan.ssl.sock);
SSL_free(stream->chan.ssl.ssl);
}
-const struct io_class io_ssl = {
+const struct io_class _shttpd_io_ssl = {
"ssl",
read_ssl,
write_ssl,
diff --git a/src/server/shttpd/log.c b/src/server/shttpd/log.c
index 422eb6d..8c506f4 100644
--- a/src/server/shttpd/log.c
+++ b/src/server/shttpd/log.c
@@ -8,13 +8,13 @@
* this stuff is worth it, you can buy me a beer in return.
*/
-#include "shttpd_defs.h"
+#include "defs.h"
/*
* Log function
*/
void
-elog(int flags, struct conn *c, const char *fmt, ...)
+_shttpd_elog(int flags, struct conn *c, const char *fmt, ...)
{
char date[64], buf[URI_MAX];
int len;
@@ -22,7 +22,7 @@
va_list ap;
/* Print to stderr */
- if (c == NULL || c->ctx->inetd_mode == 0) {
+ if (c == NULL || !IS_TRUE(c->ctx, OPT_INETD)) {
va_start(ap, fmt);
(void) vfprintf(stderr, fmt, ap);
(void) fputc('\n', stderr);
@@ -30,9 +30,9 @@
}
strftime(date, sizeof(date), "%a %b %d %H:%M:%S %Y",
- localtime(¤t_time));
+ localtime(&_shttpd_current_time));
- len = snprintf(buf, sizeof(buf),
+ len = _shttpd_snprintf(buf, sizeof(buf),
"[%s] [error] [client %s] \"%s\" ",
date, c ? inet_ntoa(c->sa.u.sin.sin_addr) : "-",
c && c->request ? c->request : "-");
@@ -48,21 +48,12 @@
(void) fflush(fp);
}
-#if defined(_WIN32) && !defined(NO_GUI)
- {
- extern HWND hLog;
-
- if (hLog != NULL)
- SendMessage(hLog, WM_APP, 0, (LPARAM) buf);
- }
-#endif /* _WIN32 */
-
if (flags & E_FATAL)
exit(EXIT_FAILURE);
}
-#if 0
+
void
-log_access(FILE *fp, const struct conn *c)
+_shttpd_log_access(FILE *fp, const struct conn *c)
{
static const struct vec dash = {"-", 1};
@@ -85,12 +76,12 @@
}
(void) strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S",
- localtime(¤t_time));
+ localtime(&c->birth_time));
- (void) snprintf(buf, sizeof(buf),
+ (void) _shttpd_snprintf(buf, sizeof(buf),
"%s - %.*s [%s %+05d] \"%s\" %d %lu %s%.*s%s %s%.*s%s",
inet_ntoa(c->sa.u.sin.sin_addr), user->len, user->ptr,
- date, tz_offset, c->request ? c->request : "-",
+ date, _shttpd_tz_offset, c->request ? c->request : "-",
c->status, (unsigned long) c->loc.io.total,
q1, referer->len, referer->ptr, q1,
q2, user_agent->len, user_agent->ptr, q2);
@@ -99,14 +90,4 @@
(void) fprintf(fp, "%s\n", buf);
(void) fflush(fp);
}
-
-#if defined(_WIN32) && !defined(NO_GUI)
- {
- extern HWND hLog;
-
- if (hLog != NULL)
- SendMessage(hLog, WM_APP, 0, (LPARAM) buf);
- }
-#endif /* _WIN32 */
}
-#endif
diff --git a/src/server/shttpd/md5.c b/src/server/shttpd/md5.c
index 9a3df6c..4e9c470 100644
--- a/src/server/shttpd/md5.c
+++ b/src/server/shttpd/md5.c
@@ -15,7 +15,7 @@
* will fill a supplied 16-byte array with the digest.
*/
-#include "shttpd_defs.h"
+#include "defs.h"
#ifndef HAVE_MD5
#if __BYTE_ORDER == 1234
@@ -201,7 +201,7 @@
}
/*
- * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
* 1 0* (64-bit count of bits processed, MSB-first)
*/
void
diff --git a/src/server/shttpd/mime_type.c b/src/server/shttpd/mime_type.c
deleted file mode 100644
index 31ce380..0000000
--- a/src/server/shttpd/mime_type.c
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
- * All rights reserved
- *
- * "THE BEER-WARE LICENSE" (Revision 42):
- * Sergey Lyubka wrote this file. As long as you retain this notice you
- * can do whatever you want with this stuff. If we meet some day, and you think
- * this stuff is worth it, you can buy me a beer in return.
- */
-
-#include "shttpd_defs.h"
-
-static const struct mime_type default_mime_types[] = {
- {"html", 4, "text/html" },
- {"htm", 3, "text/html" },
- {"txt", 3, "text/plain" },
- {"css", 3, "text/css" },
- {"ico", 3, "image/x-icon" },
- {"gif", 3, "image/gif" },
- {"jpg", 3, "image/jpeg" },
- {"jpeg", 4, "image/jpeg" },
- {"png", 3, "image/png" },
- {"svg", 3, "image/svg+xml" },
- {"torrent", 7, "application/x-bittorrent" },
- {"wav", 3, "audio/x-wav" },
- {"mp3", 3, "audio/x-mp3" },
- {"mid", 3, "audio/mid" },
- {"m3u", 3, "audio/x-mpegurl" },
- {"ram", 3, "audio/x-pn-realaudio" },
- {"ra", 2, "audio/x-pn-realaudio" },
- {"doc", 3, "application/msword", },
- {"exe", 3, "application/octet-stream" },
- {"zip", 3, "application/x-zip-compressed" },
- {"xls", 3, "application/excel" },
- {"tgz", 3, "application/x-tar-gz" },
- {"tar.gz", 6, "application/x-tar-gz" },
- {"tar", 3, "application/x-tar" },
- {"gz", 2, "application/x-gunzip" },
- {"arj", 3, "application/x-arj-compressed" },
- {"rar", 3, "application/x-arj-compressed" },
- {"rtf", 3, "application/rtf" },
- {"pdf", 3, "application/pdf" },
- {"mpg", 3, "video/mpeg" },
- {"mpeg", 4, "video/mpeg" },
- {"asf", 3, "video/x-ms-asf" },
- {"avi", 3, "video/x-msvideo" },
- {"bmp", 3, "image/bmp" },
- {NULL, 0, NULL }
-};
-
-const char *
-get_mime_type(struct shttpd_ctx *ctx, const char *uri, int len)
-{
- struct llhead *lp;
- const struct mime_type *mt;
- struct mime_type_link *mtl;
- const char *s;
-
- /* Firt, loop through the custom mime types if any */
- LL_FOREACH(&ctx->mime_types, lp) {
- mtl = LL_ENTRY(lp, struct mime_type_link, link);
- s = uri + len - mtl->ext_len;
- if (s > uri && s[-1] == '.' &&
- !strncasecmp(mtl->ext, s, mtl->ext_len))
- return (mtl->mime);
- }
-
- /* If no luck, try built-in mime types */
- for (mt = default_mime_types; mt->ext != NULL; mt++) {
- s = uri + len - mt->ext_len;
- if (s > uri && s[-1] == '.' &&
- !strncasecmp(mt->ext, s, mt->ext_len))
- return (mt->mime);
- }
-
- /* Oops. This extension is unknown to us. Fallback to text/plain */
- return ("text/plain");
-}
-
-void
-set_mime_types(struct shttpd_ctx *ctx, const char *path)
-{
- FILE *fp;
- char line[512], ext[sizeof(line)], mime[sizeof(line)], *s;
-
- if ((fp = fopen(path, "r")) == NULL)
- elog(E_FATAL, NULL, "set_mime_types: fopen(%s): %s",
- path, strerror(errno));
-
- while (fgets(line, sizeof(line), fp) != NULL) {
- /* Skip empty lines */
- if (line[0] == '#' || line[0] == '\n')
- continue;
- if (sscanf(line, "%s", mime)) {
- s = line + strlen(mime);
- while (*s && *s != '\n' && sscanf(s, "%s", ext)) {
- shttpd_add_mime_type(ctx, ext, mime);
- s += strlen(mime);
- }
- }
- }
-
- (void) fclose(fp);
-}
diff --git a/src/server/shttpd/shttpd.c b/src/server/shttpd/shttpd.c
index 4aedf62..6d8e518 100644
--- a/src/server/shttpd/shttpd.c
+++ b/src/server/shttpd/shttpd.c
@@ -10,39 +10,25 @@
/*
* Small and portable HTTP server, http://shttpd.sourceforge.net
- * $Id: shttpd.c,v 1.18 2008/01/10 11:01:21 drozd Exp $
+ * $Id: shttpd.c,v 1.57 2008/08/23 21:00:38 drozd Exp $
*/
-#include "shttpd_defs.h"
+#include "defs.h"
+#include "wsmand-daemon.h"
-/* from src/server/wsmand-daemon.h */
-extern int wsmand_options_get_use_ipv4(void);
-#ifdef ENABLE_IPV6
-extern int wsmand_options_get_use_ipv6(void);
-extern void wsmand_options_disable_use_ipv6(void);
-#endif
+time_t _shttpd_current_time; /* Current UTC time */
+int _shttpd_tz_offset; /* Time zone offset from UTC */
+int _shttpd_exit_flag; /* Program exit flag */
-time_t current_time; /* Current UTC time */
-int tz_offset; /* Time zone offset from UTC */
-
-static LL_HEAD(listeners); /* List of listening sockets */
-
-const struct vec known_http_methods[] = {
-/* {"GET", 3}, */
+const struct vec _shttpd_known_http_methods[] = {
+ {"GET", 3},
{"POST", 4},
-/* {"PUT", 3},
+ {"PUT", 3},
{"DELETE", 6},
- {"HEAD", 4}, */
+ {"HEAD", 4},
{NULL, 0}
};
-struct listener {
- struct llhead link;
- struct shttpd_ctx *ctx; /* Context that socket belongs */
- int sock; /* Listening socket */
- int is_ssl; /* Should be SSL-ed */
-};
-
/*
* This structure tells how HTTP headers must be parsed.
* Used by parse_headers() function.
@@ -68,7 +54,56 @@
static void process_connection(struct conn *, int, int);
int
-url_decode(const char *src, int src_len, char *dst, int dst_len)
+_shttpd_is_true(const char *str)
+{
+ static const char *trues[] = {"1", "yes", "true", "jawohl", NULL};
+ const char **p;
+
+ for (p = trues; *p != NULL; p++)
+ if (str && !strcmp(str, *p))
+ return (TRUE);
+
+ return (FALSE);
+}
+
+static void
+free_list(struct llhead *head, void (*dtor)(struct llhead *))
+{
+ struct llhead *lp, *tmp;
+
+ LL_FOREACH_SAFE(head, lp, tmp) {
+ LL_DEL(lp);
+ dtor(lp);
+ }
+}
+
+static void
+listener_destructor(struct llhead *lp)
+{
+ struct listener *listener = LL_ENTRY(lp, struct listener, link);
+
+ (void) closesocket(listener->sock);
+ free(listener);
+}
+
+static void
+registered_uri_destructor(struct llhead *lp)
+{
+ struct registered_uri *ruri = LL_ENTRY(lp, struct registered_uri, link);
+
+ free((void *) ruri->uri);
+ free(ruri);
+}
+
+static void
+acl_destructor(struct llhead *lp)
+{
+ struct acl *acl = LL_ENTRY(lp, struct acl, link);
+ free(acl);
+}
+
+int
+_shttpd_url_decode(const char *src, int src_len, char *dst, int dst_len)
{
int i, j, a, b;
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
@@ -96,37 +131,21 @@
return (j);
}
-void
-shttpd_add_mime_type(struct shttpd_ctx *ctx, const char *ext, const char *mime)
-{
- struct mime_type_link *e;
- const char *error_msg = "shttpd_add_mime_type: no memory";
-
- if ((e = malloc(sizeof(*e))) == NULL) {
- elog(E_FATAL, 0, error_msg);
- } else if ((e->ext= strdup(ext)) == NULL) {
- elog(E_FATAL, 0, error_msg);
- } else if ((e->mime = strdup(mime)) == NULL) {
- elog(E_FATAL, 0, error_msg);
- } else {
- e->ext_len = strlen(ext);
- LL_TAIL(&ctx->mime_types, &e->link);
- }
-}
-
-
static const char *
is_alias(struct shttpd_ctx *ctx, const char *uri,
struct vec *a_uri, struct vec *a_path)
{
- const char *p, *s = ctx->aliases;
+ const char *p, *s = ctx->options[OPT_ALIASES];
size_t len;
DBG(("is_alias: aliases [%s]", s == NULL ? "" : s));
FOR_EACH_WORD_IN_LIST(s, len) {
- if ((p = memchr(s, '=', len)) != NULL &&
- memcmp(uri, s, p - s) == 0) {
+
+ if ((p = memchr(s, '=', len)) == NULL || p >= s + len || p == s)
+ continue;
+
+ if (memcmp(uri, s, p - s) == 0) {
a_uri->ptr = s;
a_uri->len = p - s;
a_path->ptr = ++p;
@@ -139,7 +158,7 @@
}
void
-stop_stream(struct stream *stream)
+_shttpd_stop_stream(struct stream *stream)
{
if (stream->io_class != NULL && stream->io_class->close != NULL)
stream->io_class->close(stream);
@@ -151,54 +170,30 @@
DBG(("%d %s stopped. %lu of content data, %d now in a buffer",
stream->conn->rem.chan.sock,
stream->io_class ? stream->io_class->name : "(null)",
- (unsigned long) stream->io.total, io_data_len(&stream->io)));
+ (unsigned long) stream->io.total, (int) io_data_len(&stream->io)));
}
/*
* Setup listening socket on given port, return socket
*/
static int
-open_listening_port(int port)
+shttpd_open_listening_port(int port)
{
- int sock = -1, on = 1;
+ int sock, on = 1;
struct usa sa;
#ifdef _WIN32
{WSADATA data; WSAStartup(MAKEWORD(2,2), &data);}
#endif /* _WIN32 */
+ sa.len = sizeof(sa.u.sin);
+ sa.u.sin.sin_family = AF_INET;
+ sa.u.sin.sin_port = htons((uint16_t) port);
+ sa.u.sin.sin_addr.s_addr = htonl(INADDR_ANY);
-
-#ifdef ENABLE_IPV6
- sa.len = sizeof(sa.u.sin6);
- memset(&sa.u.sin6, 0, sa.len);
- sa.u.sin6.sin6_family = AF_INET6;
- sa.u.sin6.sin6_addr = in6addr_any;
- sa.u.sin6.sin6_port = htons((uint16_t) port);
- sa.u.sin6.sin6_flowinfo = 0;
- sa.u.sin6.sin6_scope_id = 0;
-
- if (!wsmand_options_get_use_ipv6()
- || (sock = socket(AF_INET6, SOCK_STREAM, 6)) == -1) {
- wsmand_options_disable_use_ipv6();
- if (wsmand_options_get_use_ipv4()) {
-#endif
- sa.len = sizeof(sa.u.sin);
- memset(&sa.u.sin, 0, sa.len);
- sa.u.sin.sin_family = AF_INET;
- sa.u.sin.sin_addr.s_addr = htonl(INADDR_ANY);
- sa.u.sin.sin_port = htons((uint16_t) port);
-
- if ((sock = socket(PF_INET, SOCK_STREAM, 6)) == -1)
- goto fail;
-#ifdef ENABLE_IPV6
- }
- else
- goto fail;
- }
-#endif
-
- if (set_non_blocking_mode(sock) != 0)
+ if ((sock = socket(PF_INET, SOCK_STREAM, 6)) == -1)
+ goto fail;
+ if (_shttpd_set_non_blocking_mode(sock) != 0)
goto fail;
if (setsockopt(sock, SOL_SOCKET,
SO_REUSEADDR,(char *) &on, sizeof(on)) != 0)
@@ -216,7 +211,7 @@
fail:
if (sock != -1)
(void) closesocket(sock);
- elog(E_LOG, NULL, "open_listening_port(%d): %s", port, strerror(errno));
+ _shttpd_elog(E_LOG, NULL, "open_listening_port(%d): %s", port, strerror(errno));
return (-1);
}
@@ -224,7 +219,7 @@
* Check whether full request is buffered Return headers length, or 0
*/
int
-get_headers_len(const char *buf, size_t buflen)
+_shttpd_get_headers_len(const char *buf, size_t buflen)
{
const char *s, *e;
int len = 0;
@@ -247,9 +242,8 @@
* Send error message back to a client.
*/
void
-send_server_error(struct conn *c, int status, const char *reason)
+_shttpd_send_server_error(struct conn *c, int status, const char *reason)
{
-#ifdef EMBEDDED
struct llhead *lp;
struct error_handler *e;
@@ -261,18 +255,23 @@
c->loc.io_class->close != NULL)
c->loc.io_class->close(&c->loc);
io_clear(&c->loc.io);
- setup_embedded_stream(c, e->callback, e->callback_data);
+ _shttpd_setup_embedded_stream(c,
+ e->callback, e->callback_data);
return;
}
}
-#endif /* EMBEDDED */
io_clear(&c->loc.io);
- c->loc.headers_len = c->loc.io.head = snprintf(c->loc.io.buf,
- c->loc.io.size, "HTTP/1.1 %d %s\r\nConnection: Close\r\n\r\n%d %s",
- status, reason, status, reason);
+ c->loc.io.head = _shttpd_snprintf(c->loc.io.buf, c->loc.io.size,
+ "HTTP/1.1 %d %s\r\n"
+ "Content-Type: text/plain\r\n"
+ "Content-Length: 12\r\n"
+ "\r\n"
+ "Error: %03d\r\n",
+ status, reason, status);
+ c->loc.content_len = 10;
c->status = status;
- stop_stream(&c->loc);
+ _shttpd_stop_stream(&c->loc);
}
/*
@@ -330,7 +329,7 @@
tm.tm_year += 100;
/* Set Daylight Saving Time field */
- tmp = localtime(¤t_time);
+ tmp = localtime(&_shttpd_current_time);
tm.tm_isdst = tmp->tm_isdst;
return (mktime(&tm));
@@ -351,7 +350,7 @@
}
void
-parse_headers(const char *s, int len, struct headers *parsed)
+_shttpd_parse_headers(const char *s, int len, struct headers *parsed)
{
const struct http_header *h;
union variant *v;
@@ -368,7 +367,7 @@
/* Is this header known to us ? */
for (h = http_headers; h->len != 0; h++)
if (e - s > h->len &&
- !strncasecmp(s, h->name, h->len))
+ !_shttpd_strncasecmp(s, h->name, h->len))
break;
/* If the header is known to us, store its value */
@@ -397,6 +396,86 @@
}
}
+static const struct {
+ const char *extension;
+ int ext_len;
+ const char *mime_type;
+} builtin_mime_types[] = {
+ {"html", 4, "text/html" },
+ {"htm", 3, "text/html" },
+ {"txt", 3, "text/plain" },
+ {"css", 3, "text/css" },
+ {"ico", 3, "image/x-icon" },
+ {"gif", 3, "image/gif" },
+ {"jpg", 3, "image/jpeg" },
+ {"jpeg", 4, "image/jpeg" },
+ {"png", 3, "image/png" },
+ {"svg", 3, "image/svg+xml" },
+ {"torrent", 7, "application/x-bittorrent" },
+ {"wav", 3, "audio/x-wav" },
+ {"mp3", 3, "audio/x-mp3" },
+ {"mid", 3, "audio/mid" },
+ {"m3u", 3, "audio/x-mpegurl" },
+ {"ram", 3, "audio/x-pn-realaudio" },
+ {"ra", 2, "audio/x-pn-realaudio" },
+ {"doc", 3, "application/msword", },
+ {"exe", 3, "application/octet-stream" },
+ {"zip", 3, "application/x-zip-compressed" },
+ {"xls", 3, "application/excel" },
+ {"tgz", 3, "application/x-tar-gz" },
+ {"tar.gz", 6, "application/x-tar-gz" },
+ {"tar", 3, "application/x-tar" },
+ {"gz", 2, "application/x-gunzip" },
+ {"arj", 3, "application/x-arj-compressed" },
+ {"rar", 3, "application/x-arj-compressed" },
+ {"rtf", 3, "application/rtf" },
+ {"pdf", 3, "application/pdf" },
+ {"swf", 3, "application/x-shockwave-flash" },
+ {"mpg", 3, "video/mpeg" },
+ {"mpeg", 4, "video/mpeg" },
+ {"asf", 3, "video/x-ms-asf" },
+ {"avi", 3, "video/x-msvideo" },
+ {"bmp", 3, "image/bmp" },
+ {NULL, 0, NULL }
+};
+
+void
+_shttpd_get_mime_type(struct shttpd_ctx *ctx,
+ const char *uri, int len, struct vec *vec)
+{
+ const char *eq, *p = ctx->options[OPT_MIME_TYPES];
+ int i, n, ext_len;
+
+ /* Firt, loop through the custom mime types if any */
+ FOR_EACH_WORD_IN_LIST(p, n) {
+ if ((eq = memchr(p, '=', n)) == NULL || eq >= p + n || eq == p)
+ continue;
+ ext_len = eq - p;
+ if (len > ext_len && uri[len - ext_len - 1] == '.' &&
+ !_shttpd_strncasecmp(p, &uri[len - ext_len], ext_len)) {
+ vec->ptr = eq + 1;
+ vec->len = p + n - vec->ptr;
+ return;
+ }
+ }
+
+ /* If no luck, try built-in mime types */
+ for (i = 0; builtin_mime_types[i].extension != NULL; i++) {
+ ext_len = builtin_mime_types[i].ext_len;
+ if (len > ext_len && uri[len - ext_len - 1] == '.' &&
+ !_shttpd_strncasecmp(builtin_mime_types[i].extension,
+ &uri[len - ext_len], ext_len)) {
+ vec->ptr = builtin_mime_types[i].mime_type;
+ vec->len = strlen(vec->ptr);
+ return;
+ }
+ }
+
+ /* Oops. This extension is unknown to us. Fallback to text/plain */
+ vec->ptr = "text/plain";
+ vec->len = strlen(vec->ptr);
+}
+
/*
* For given directory path, substitute it to valid index file.
* Return 0 if index file has been found, -1 if not found
@@ -405,14 +484,15 @@
find_index_file(struct conn *c, char *path, size_t maxpath, struct stat *stp)
{
char buf[FILENAME_MAX];
- const char *s = c->ctx->index_files;
- int len;
+ const char *s = c->ctx->options[OPT_INDEX_FILES];
+ size_t len;
FOR_EACH_WORD_IN_LIST(s, len) {
- snprintf(buf, sizeof(buf), "%s%c%.*s",path, DIRSEP, len, s);
- if (my_stat(buf, stp) == 0) {
- my_strlcpy(path, buf, maxpath);
- c->mime_type = get_mime_type(c->ctx, s, len);
+ /* path must end with '/' character */
+ _shttpd_snprintf(buf, sizeof(buf), "%s%.*s", path, len, s);
+ if (_shttpd_stat(buf, stp) == 0) {
+ _shttpd_strlcpy(path, buf, maxpath);
+ _shttpd_get_mime_type(c->ctx, s, len, &c->mime_type);
return (0);
}
}
@@ -430,17 +510,17 @@
{
char *p, *e;
- if (my_stat(path, stp) == 0)
+ if (_shttpd_stat(path, stp) == 0)
return (0);
p = path + strlen(path);
- e = path + strlen(c->ctx->document_root) + 2;
+ e = path + strlen(c->ctx->options[OPT_ROOT]) + 2;
/* Strip directory parts of the path one by one */
for (; p > e; p--)
if (*p == '/') {
*p = '\0';
- if (!my_stat(path, stp) && !S_ISDIR(stp->st_mode)) {
+ if (!_shttpd_stat(path, stp) && !S_ISDIR(stp->st_mode)) {
c->path_info = p + 1;
return (0);
} else {
@@ -451,139 +531,130 @@
return (-1);
}
-
static void
decide_what_to_do(struct conn *c)
{
- char path[URI_MAX], buf[1024];
+ char path[URI_MAX], buf[1024], *root;
struct vec alias_uri, alias_path;
struct stat st;
int rc;
-#ifdef EMBEDDED
struct registered_uri *ruri;
-#endif /* EMBEDDED */
DBG(("decide_what_to_do: [%s]", c->uri));
if ((c->query = strchr(c->uri, '?')) != NULL)
*c->query++ = '\0';
- url_decode(c->uri, strlen(c->uri), c->uri, strlen(c->uri) + 1);
+ _shttpd_url_decode(c->uri, strlen(c->uri), c->uri, strlen(c->uri) + 1);
remove_double_dots(c->uri);
- if (strlen(c->uri) + strlen(c->ctx->document_root) >= sizeof(path)) {
- send_server_error(c, 400, "URI is too long");
+ root = c->ctx->options[OPT_ROOT];
+ if (strlen(c->uri) + strlen(root) >= sizeof(path)) {
+ _shttpd_send_server_error(c, 400, "URI is too long");
return;
}
- (void) snprintf(path, sizeof(path), "%s%s",
- c->ctx->document_root, c->uri);
+ (void) _shttpd_snprintf(path, sizeof(path), "%s%s", root, c->uri);
/* User may use the aliases - check URI for mount point */
if (is_alias(c->ctx, c->uri, &alias_uri, &alias_path) != NULL) {
- (void) snprintf(path, sizeof(path), "%.*s%s",
+ (void) _shttpd_snprintf(path, sizeof(path), "%.*s%s",
alias_path.len, alias_path.ptr, c->uri + alias_uri.len);
DBG(("using alias %.*s -> %.*s", alias_uri.len, alias_uri.ptr,
alias_path.len, alias_path.ptr));
}
#if !defined(NO_AUTH)
- rc = check_authorization(c, path);
- if (rc != 1) {
- if (rc != 2) { /* 2 = multipass auth (GSS)*/
- if (send_authorization_request(c)) {
- fprintf(stderr, "Digest realm overflows buffer\n");
- return;
- }
- }
+ if (_shttpd_check_authorization(c, path) != 1) {
+ _shttpd_send_authorization_request(c);
} else
#endif /* NO_AUTH */
-#ifdef EMBEDDED
- if ((ruri = is_registered_uri(c->ctx, c->uri)) != NULL) {
- setup_embedded_stream(c, ruri->callback, ruri->callback_data);
+ if ((ruri = _shttpd_is_registered_uri(c->ctx, c->uri)) != NULL) {
+ _shttpd_setup_embedded_stream(c,
+ ruri->callback, ruri->callback_data);
} else
-#endif /* EMBEDDED */
if (strstr(path, HTPASSWD)) {
/* Do not allow to view passwords files */
- send_server_error(c, 403, "Forbidden");
+ _shttpd_send_server_error(c, 403, "Forbidden");
} else
#if !defined(NO_AUTH)
if ((c->method == METHOD_PUT || c->method == METHOD_DELETE) &&
- (c->ctx->put_auth_file == NULL || !is_authorized_for_put(c))) {
- if (send_authorization_request(c)) {
- fprintf(stderr, "Digest realm overflows buffer\n");
- return;
- }
+ (c->ctx->options[OPT_AUTH_PUT] == NULL ||
+ !_shttpd_is_authorized_for_put(c))) {
+ _shttpd_send_authorization_request(c);
} else
#endif /* NO_AUTH */
if (c->method == METHOD_PUT) {
- c->status = my_stat(path, &st) == 0 ? 200 : 201;
+ c->status = _shttpd_stat(path, &st) == 0 ? 200 : 201;
if (c->ch.range.v_vec.len > 0) {
- send_server_error(c, 501, "PUT Range Not Implemented");
- } else if ((rc = put_dir(path)) == 0) {
- send_server_error(c, 200, "OK");
+ _shttpd_send_server_error(c, 501,
+ "PUT Range Not Implemented");
+ } else if ((rc = _shttpd_put_dir(path)) == 0) {
+ _shttpd_send_server_error(c, 200, "OK");
} else if (rc == -1) {
- send_server_error(c, 500, "PUT Directory Error");
+ _shttpd_send_server_error(c, 500, "PUT Directory Error");
} else if (c->rem.content_len == 0) {
- send_server_error(c, 411, "Length Required");
- } else if ((c->loc.chan.fd = my_open(path, O_WRONLY | O_BINARY |
+ _shttpd_send_server_error(c, 411, "Length Required");
+ } else if ((c->loc.chan.fd = _shttpd_open(path, O_WRONLY | O_BINARY |
O_CREAT | O_NONBLOCK | O_TRUNC, 0644)) == -1) {
- send_server_error(c, 500, "PUT Error");
+ _shttpd_send_server_error(c, 500, "PUT Error");
} else {
DBG(("PUT file [%s]", c->uri));
- c->loc.io_class = &io_file;
+ c->loc.io_class = &_shttpd_io_file;
c->loc.flags |= FLAG_W | FLAG_ALWAYS_READY ;
}
} else if (c->method == METHOD_DELETE) {
DBG(("DELETE [%s]", c->uri));
- if (my_remove(path) == 0)
- send_server_error(c, 200, "OK");
+ if (_shttpd_remove(path) == 0)
+ _shttpd_send_server_error(c, 200, "OK");
else
- send_server_error(c, 500, "DELETE Error");
+ _shttpd_send_server_error(c, 500, "DELETE Error");
} else if (get_path_info(c, path, &st) != 0) {
- send_server_error(c, 404, "Not Found");
+ _shttpd_send_server_error(c, 404, "Not Found");
} else if (S_ISDIR(st.st_mode) && path[strlen(path) - 1] != '/') {
- (void) snprintf(buf, sizeof(buf),
+ (void) _shttpd_snprintf(buf, sizeof(buf),
"Moved Permanently\r\nLocation: %s/", c->uri);
- send_server_error(c, 301, buf);
+ _shttpd_send_server_error(c, 301, buf);
} else if (S_ISDIR(st.st_mode) &&
find_index_file(c, path, sizeof(path) - 1, &st) == -1 &&
- c->ctx->dirlist == 0) {
- send_server_error(c, 403, "Directory Listing Denied");
- } else if (S_ISDIR(st.st_mode) && c->ctx->dirlist) {
- if ((c->loc.chan.dir.path = strdup(path)) != NULL)
- get_dir(c);
+ !IS_TRUE(c->ctx, OPT_DIR_LIST)) {
+ _shttpd_send_server_error(c, 403, "Directory Listing Denied");
+ } else if (S_ISDIR(st.st_mode) && IS_TRUE(c->ctx, OPT_DIR_LIST)) {
+ if ((c->loc.chan.dir.path = _shttpd_strdup(path)) != NULL)
+ _shttpd_get_dir(c);
else
- send_server_error(c, 500, "GET Directory Error");
- } else if (S_ISDIR(st.st_mode) && c->ctx->dirlist == 0) {
- send_server_error(c, 403, "Directory listing denied");
+ _shttpd_send_server_error(c, 500, "GET Directory Error");
+ } else if (S_ISDIR(st.st_mode) && !IS_TRUE(c->ctx, OPT_DIR_LIST)) {
+ _shttpd_send_server_error(c, 403, "Directory listing denied");
#if !defined(NO_CGI)
- } else if (match_extension(path, c->ctx->cgi_extensions)) {
+ } else if (_shttpd_match_extension(path,
+ c->ctx->options[OPT_CGI_EXTENSIONS])) {
if (c->method != METHOD_POST && c->method != METHOD_GET) {
- send_server_error(c, 501, "Bad method ");
- } else if ((run_cgi(c, path)) == -1) {
- send_server_error(c, 500, "Cannot exec CGI");
+ _shttpd_send_server_error(c, 501, "Bad method ");
+ } else if ((_shttpd_run_cgi(c, path)) == -1) {
+ _shttpd_send_server_error(c, 500, "Cannot exec CGI");
} else {
- do_cgi(c);
+ _shttpd_do_cgi(c);
}
#endif /* NO_CGI */
#if !defined(NO_SSI)
- } else if (match_extension(path, c->ctx->ssi_extensions)) {
- if ((c->loc.chan.fd = my_open(path,
+ } else if (_shttpd_match_extension(path,
+ c->ctx->options[OPT_SSI_EXTENSIONS])) {
+ if ((c->loc.chan.fd = _shttpd_open(path,
O_RDONLY | O_BINARY, 0644)) == -1) {
- send_server_error(c, 500, "SSI open error");
+ _shttpd_send_server_error(c, 500, "SSI open error");
} else {
- do_ssi(c);
+ _shttpd_do_ssi(c);
}
#endif /* NO_CGI */
} else if (c->ch.ims.v_time && st.st_mtime <= c->ch.ims.v_time) {
- send_server_error(c, 304, "Not Modified");
- } else if ((c->loc.chan.fd = my_open(path,
+ _shttpd_send_server_error(c, 304, "Not Modified");
+ } else if ((c->loc.chan.fd = _shttpd_open(path,
O_RDONLY | O_BINARY, 0644)) != -1) {
- get_file(c, &st);
+ _shttpd_get_file(c, &st);
} else {
- send_server_error(c, 500, "Internal Error");
+ _shttpd_send_server_error(c, 500, "Internal Error");
}
}
@@ -592,12 +663,10 @@
{
const struct vec *v;
- assert(c->rem.io.head >= MIN_REQ_LEN);
-
/* Set the request method */
- for (v = known_http_methods; v->ptr != NULL; v++)
+ for (v = _shttpd_known_http_methods; v->ptr != NULL; v++)
if (!memcmp(c->rem.io.buf, v->ptr, v->len)) {
- c->method = v - known_http_methods;
+ c->method = v - _shttpd_known_http_methods;
break;
}
@@ -608,28 +677,32 @@
parse_http_request(struct conn *c)
{
char *s, *e, *p, *start;
- char *end_number;
- int uri_len, req_len;
+ int uri_len, req_len, n;
s = io_data(&c->rem.io);;
req_len = c->rem.headers_len =
- get_headers_len(s, io_data_len(&c->rem.io));
+ _shttpd_get_headers_len(s, io_data_len(&c->rem.io));
- if (req_len == 0 && io_space_len(&c->rem.io) == 0)
- send_server_error(c, 400, "Request is too big");
+ if (req_len == 0 && io_space_len(&c->rem.io) == 0) {
+ io_clear(&c->rem.io);
+ _shttpd_send_server_error(c, 400, "Request is too big");
+ }
- if (req_len == 0)
+ if (req_len == 0) {
return;
- else if (req_len < MIN_REQ_LEN)
- send_server_error(c, 400, "Bad request");
- else if (set_request_method(c))
- send_server_error(c, 501, "Method Not Implemented");
- else if ((c->request = u_strndup(s, req_len)) == NULL)
- send_server_error(c, 500, "Cannot allocate request");
+ } else if (req_len < 16) { /* Minimal: "GET / HTTP/1.0\n\n" */
+ _shttpd_send_server_error(c, 400, "Bad request");
+ } else if (set_request_method(c)) {
+ _shttpd_send_server_error(c, 501, "Method Not Implemented");
+ } else if ((c->request = _shttpd_strndup(s, req_len)) == NULL) {
+ _shttpd_send_server_error(c, 500, "Cannot allocate request");
+ }
if (c->loc.flags & FLAG_CLOSED)
return;
+ io_inc_tail(&c->rem.io, req_len);
+
DBG(("Conn %d: parsing request: [%.*s]", c->rem.chan.sock, req_len, s));
c->rem.flags |= FLAG_HEADERS_PARSED;
@@ -647,338 +720,269 @@
* First, we skip the REQUEST_METHOD and shift to the URI.
*/
for (p = c->request, e = p + req_len; *p != ' ' && p < e; p++);
- while (p < e && *p == ' ') p++;
+ while (p < e && *p == ' ')
+ p++;
/* Now remember where URI starts, and shift to the end of URI */
for (start = p; p < e && !isspace((unsigned char)*p); ) p++;
uri_len = p - start;
+
/* Skip space following the URI */
- while (p < e && *p == ' ') p++;
+ while (p < e && *p == ' ')
+ p++;
/* Now comes the HTTP-Version in the form HTTP/<major>.<minor> */
- if (strncmp(p, "HTTP/", 5) != 0) {
- send_server_error(c, 400, "Bad HTTP version");
- return;
- }
- p += 5;
- /* Parse the HTTP major version number */
- c->major_version = strtoul(p, &end_number, 10);
- if (end_number == p || *end_number != '.') {
- send_server_error(c, 400, "Bad HTTP major version");
- return;
- }
- p = end_number + 1;
- /* Parse the minor version number */
- c->minor_version = strtoul(p, &end_number, 10);
- if (end_number == p || *end_number != '\0') {
- send_server_error(c, 400, "Bad HTTP minor version");
- return;
- }
- /* Version must be <=1.1 */
- if (c->major_version > 1 ||
+ if (sscanf(p, "HTTP/%lu.%lu%n",
+ &c->major_version, &c->minor_version, &n) != 2 || p[n] != '\0') {
+ _shttpd_send_server_error(c, 400, "Bad HTTP version");
+ } else if (c->major_version > 1 ||
(c->major_version == 1 && c->minor_version > 1)) {
- send_server_error(c, 505, "HTTP version not supported");
- return;
- }
-
- if (uri_len <= 0) {
- send_server_error(c, 400, "Bad URI");
+ _shttpd_send_server_error(c, 505, "HTTP version not supported");
+ } else if (uri_len <= 0) {
+ _shttpd_send_server_error(c, 400, "Bad URI");
} else if ((c->uri = malloc(uri_len + 1)) == NULL) {
- send_server_error(c, 500, "Cannot allocate URI");
+ _shttpd_send_server_error(c, 500, "Cannot allocate URI");
} else {
- my_strlcpy(c->uri, (char *) start, uri_len + 1);
- parse_headers(c->headers,
+ _shttpd_strlcpy(c->uri, (char *) start, uri_len + 1);
+ _shttpd_parse_headers(c->headers,
(c->request + req_len) - c->headers, &c->ch);
/* Remove the length of request from total, count only data */
assert(c->rem.io.total >= (big_int_t) req_len);
c->rem.io.total -= req_len;
-
c->rem.content_len = c->ch.cl.v_big_int;
- io_inc_tail(&c->rem.io, req_len);
-
decide_what_to_do(c);
}
}
-void
-shttpd_add_socket(struct shttpd_ctx *ctx, int sock, int is_ssl)
+static void
+add_socket(struct worker *worker, int sock, int is_ssl)
{
- struct conn *c;
- struct usa sa;
- int l = ctx->inetd_mode ? E_FATAL : E_LOG;
+ struct shttpd_ctx *ctx = worker->ctx;
+ struct conn *c;
+ struct usa sa;
+ int l = IS_TRUE(ctx, OPT_INETD) ? E_FATAL : E_LOG;
#if !defined(NO_SSL)
SSL *ssl = NULL;
+#else
+ is_ssl = is_ssl; /* supress warnings */
#endif /* NO_SSL */
sa.len = sizeof(sa.u.sin);
- (void) set_non_blocking_mode(sock);
+ (void) _shttpd_set_non_blocking_mode(sock);
if (getpeername(sock, &sa.u.sa, &sa.len)) {
- elog(l, NULL, "add_socket: %s", strerror(errno));
+ _shttpd_elog(l, NULL, "add_socket: %s", strerror(errno));
#if !defined(NO_SSL)
} else if (is_ssl && (ssl = SSL_new(ctx->ssl_ctx)) == NULL) {
- elog(l, NULL, "add_socket: SSL_new: %s", strerror(ERRNO));
+ _shttpd_elog(l, NULL, "add_socket: SSL_new: %s", strerror(ERRNO));
(void) closesocket(sock);
} else if (is_ssl && SSL_set_fd(ssl, sock) == 0) {
- elog(l, NULL, "add_socket: SSL_set_fd: %s", strerror(ERRNO));
+ _shttpd_elog(l, NULL, "add_socket: SSL_set_fd: %s", strerror(ERRNO));
(void) closesocket(sock);
SSL_free(ssl);
#endif /* NO_SSL */
- } else if ((c = calloc(1, sizeof(*c) + 2 * ctx->io_buf_size)) == NULL) {
+ } else if ((c = calloc(1, sizeof(*c) + 2 * URI_MAX)) == NULL) {
#if !defined(NO_SSL)
if (ssl)
SSL_free(ssl);
#endif /* NO_SSL */
(void) closesocket(sock);
- elog(l, NULL, "add_socket: calloc: %s", strerror(ERRNO));
+ _shttpd_elog(l, NULL, "add_socket: calloc: %s", strerror(ERRNO));
} else {
- ctx->nrequests++;
- c->rem.conn = c->loc.conn = c;
+ c->rem.conn = c->loc.conn = c;
c->ctx = ctx;
+ c->worker = worker;
c->sa = sa;
- c->birth_time = current_time;
- c->expire_time = current_time + EXPIRE_TIME;
+ c->birth_time = _shttpd_current_time;
+ c->expire_time = _shttpd_current_time + EXPIRE_TIME;
(void) getsockname(sock, &sa.u.sa, &sa.len);
-#ifdef ENABLE_IPV6
- if (wsmand_options_get_use_ipv6()) {
- c->loc_port = sa.u.sin6.sin6_port;
- }
- else {
-#endif
- c->loc_port = sa.u.sin.sin_port;
-#ifdef ENABLE_IPV6
- }
-#endif
+ c->loc_port = sa.u.sin.sin_port;
- set_close_on_exec(sock);
+ _shttpd_set_close_on_exec(sock);
c->loc.io_class = NULL;
- c->rem.io_class = &io_socket;
+ c->rem.io_class = &_shttpd_io_socket;
c->rem.chan.sock = sock;
/* Set IO buffers */
c->loc.io.buf = (char *) (c + 1);
- c->rem.io.buf = c->loc.io.buf + ctx->io_buf_size;
- c->loc.io.size = c->rem.io.size = ctx->io_buf_size;
-#ifdef SHTTPD_GSS
- c->gss_ctx = GSS_C_NO_CONTEXT;
-#endif
+ c->rem.io.buf = c->loc.io.buf + URI_MAX;
+ c->loc.io.size = c->rem.io.size = URI_MAX;
+
#if !defined(NO_SSL)
if (is_ssl) {
- c->rem.io_class = &io_ssl;
+ c->rem.io_class = &_shttpd_io_ssl;
c->rem.chan.ssl.sock = sock;
c->rem.chan.ssl.ssl = ssl;
- ssl_handshake(&c->rem);
+ _shttpd_ssl_handshake(&c->rem);
}
#endif /* NO_SSL */
- EnterCriticalSection(&ctx->mutex);
- LL_TAIL(&ctx->connections, &c->link);
- ctx->nactive++;
- LeaveCriticalSection(&ctx->mutex);
-#ifdef ENABLE_IPV6
- if (wsmand_options_get_use_ipv6()) {
- char str[INET6_ADDRSTRLEN];
- inet_ntop( AF_INET6,&sa.u.sin6.sin6_addr, str, sizeof(str));
- DBG(("%s:%hu connected IPv6 (socket %d)", str , ntohs(sa.u.sin6.sin6_port), sock));
- }
- else {
-#endif
- DBG(("%s:%hu connected IPv4 (socket %d)",
- inet_ntoa(* (struct in_addr *) &sa.u.sin.sin_addr.s_addr),
- ntohs(sa.u.sin.sin_port), sock));
-#ifdef ENABLE_IPV6
- }
-#endif
+ LL_TAIL(&worker->connections, &c->link);
+ worker->num_conns++;
+
+ DBG(("%s:%hu connected (socket %d)",
+ inet_ntoa(* (struct in_addr *) &sa.u.sin.sin_addr.s_addr),
+ ntohs(sa.u.sin.sin_port), sock));
}
}
-int
-shttpd_active(struct shttpd_ctx *ctx)
+static struct worker *
+first_worker(struct shttpd_ctx *ctx)
{
- return (ctx->nactive);
+ return (LL_ENTRY(ctx->workers.next, struct worker, link));
}
-/*
- * Setup a listening socket on given port. Return opened socket or -1
- */
-int
-shttpd_listen(struct shttpd_ctx *ctx, int port, int is_ssl)
+static void
+pass_socket(struct shttpd_ctx *ctx, int sock, int is_ssl)
{
+ struct llhead *lp;
+ struct worker *worker, *lazy;
+ int buf[3];
+
+ lazy = first_worker(ctx);
+
+ /* Find least busy worker */
+ LL_FOREACH(&ctx->workers, lp) {
+ worker = LL_ENTRY(lp, struct worker, link);
+ if (worker->num_conns < lazy->num_conns)
+ lazy = worker;
+ }
+
+ buf[0] = CTL_PASS_SOCKET;
+ buf[1] = sock;
+ buf[2] = is_ssl;
+
+ (void) send(lazy->ctl[1], (void *) buf, sizeof(buf), 0);
+}
+
+static int
+set_ports(struct shttpd_ctx *ctx, const char *p)
+{
+ int sock, len, is_ssl, port;
struct listener *l;
- int sock;
- if ((sock = open_listening_port(port)) == -1) {
- elog(E_FATAL, NULL, "cannot open port %d", port);
- } else if ((l = calloc(1, sizeof(*l))) == NULL) {
- (void) closesocket(sock);
- elog(E_FATAL, NULL, "cannot allocate listener");
- } else if (is_ssl && ctx->ssl_ctx == NULL) {
- (void) closesocket(sock);
- elog(E_FATAL, NULL, "cannot add SSL socket, "
- "please specify certificate file");
- } else {
- l->is_ssl = is_ssl;
- l->sock = sock;
- l->ctx = ctx;
- LL_TAIL(&listeners, &l->link);
- DBG(("shttpd_listen: added socket %d", sock));
+
+ free_list(&ctx->listeners, &listener_destructor);
+
+ FOR_EACH_WORD_IN_LIST(p, len) {
+
+ is_ssl = p[len - 1] == 's' ? 1 : 0;
+ port = atoi(p);
+
+ if ((sock = shttpd_open_listening_port(port)) == -1) {
+ _shttpd_elog(E_LOG, NULL, "cannot open port %d", port);
+ goto fail;
+ } else if (is_ssl && ctx->ssl_ctx == NULL) {
+ (void) closesocket(sock);
+ _shttpd_elog(E_LOG, NULL, "cannot add SSL socket, "
+ "please specify certificate file");
+ goto fail;
+ } else if ((l = calloc(1, sizeof(*l))) == NULL) {
+ (void) closesocket(sock);
+ _shttpd_elog(E_LOG, NULL, "cannot allocate listener");
+ goto fail;
+ } else {
+ l->is_ssl = is_ssl;
+ l->sock = sock;
+ l->ctx = ctx;
+ LL_TAIL(&ctx->listeners, &l->link);
+ DBG(("shttpd_listen: added socket %d", sock));
+ }
}
- return (sock);
-}
-
-int
-shttpd_accept(int lsn_sock, int milliseconds)
-{
- struct timeval tv;
- struct usa sa;
- fd_set read_set;
- int sock = -1;
-
- tv.tv_sec = milliseconds / 1000;
- tv.tv_usec = milliseconds % 1000;
- sa.len = sizeof(sa.u.sin);
- FD_ZERO(&read_set);
- FD_SET(lsn_sock, &read_set);
-
- if (select(lsn_sock + 1, &read_set, NULL, NULL, &tv) == 1)
- sock = accept(lsn_sock, &sa.u.sa, &sa.len);
-
- return (sock);
+ return (TRUE);
+fail:
+ free_list(&ctx->listeners, &listener_destructor);
+ return (FALSE);
}
static void
read_stream(struct stream *stream)
{
- int n, len;
- int sslerr = 0;
- len = io_space_len(&stream->io);
- assert(len > 0);
- /* Do not read more that needed */
- if (stream->content_len > 0 &&
- stream->io.total + len > stream->content_len)
- len = stream->content_len - stream->io.total;
+ int n, len;
- /* Read from underlying channel */
- n = stream->nread_last = stream->io_class->read(stream,
- io_space(&stream->io), len);
- if (n > 0) {
- io_inc_head(&stream->io, n);
- stream->flags &= ~FLAG_SSL_SHOULD_SELECT_ON_WRITE;
- }
- else if (n == -1) {
- if(stream->chan.ssl.ssl) {
- sslerr = SSL_get_error(stream->chan.ssl.ssl, n);
- }
- /* Ignore SSL_ERROR_WANT_READ and SSL_ERROR_WANT_WRITE*/
- if((stream->chan.ssl.ssl && sslerr == SSL_ERROR_SYSCALL &&
- (ERRNO == EINTR || ERRNO == EWOULDBLOCK)) ||
- (ERRNO == EINTR || ERRNO == EWOULDBLOCK)) {
- n = n; /* Ignore EINTR and EAGAIN */
- }
- else if(sslerr == SSL_ERROR_WANT_READ) {
- n = n;
- }
- else if(sslerr == SSL_ERROR_WANT_WRITE) {
- stream->flags |= FLAG_SSL_SHOULD_SELECT_ON_WRITE;
- }
- else if (!(stream->flags & FLAG_DONT_CLOSE))
- stop_stream(stream);
+ len = io_space_len(&stream->io);
+ assert(len > 0);
- }
- else if (!(stream->flags & FLAG_DONT_CLOSE))
- stop_stream(stream);
-#if 0
- DBG(("read_stream (%d %s): read %d/%d/%lu bytes (errno %d)",
- stream->conn->rem.chan.sock,
- stream->io_class ? stream->io_class->name : "(null)",
- n, len, (unsigned long) stream->io.total, ERRNO));
-#endif
+ /* Do not read more that needed */
+ if (stream->content_len > 0 &&
+ stream->io.total + len > stream->content_len)
+ len = stream->content_len - stream->io.total;
- /*
- * Close the local stream if everything was read
- * XXX We do not close the remote stream though! It may be
- * a POST data completed transfer, we do not want the socket
- * to be closed.
- */
- if (stream->content_len > 0 && stream == &stream->conn->loc) {
- assert(stream->io.total <= stream->content_len);
- if (stream->io.total == stream->content_len)
- stop_stream(stream);
- }
+ /* Read from underlying channel */
+ assert(stream->io_class != NULL);
+ n = stream->io_class->read(stream, io_space(&stream->io), len);
+ if (n > 0)
+ io_inc_head(&stream->io, n);
+ else if (n == -1 && (ERRNO == EINTR || ERRNO == EWOULDBLOCK))
+ n = n; /* Ignore EINTR and EAGAIN */
+ else if (!(stream->flags & FLAG_DONT_CLOSE))
+ _shttpd_stop_stream(stream);
- stream->conn->expire_time = current_time + EXPIRE_TIME;
+ DBG(("read_stream (%d %s): read %d/%d/%lu bytes (errno %d)",
+ stream->conn->rem.chan.sock,
+ stream->io_class ? stream->io_class->name : "(null)",
+ n, len, (unsigned long) stream->io.total, ERRNO));
+
+ /*
+ * Close the local stream if everything was read
+ * XXX We do not close the remote stream though! It may be
+ * a POST data completed transfer, we do not want the socket
+ * to be closed.
+ */
+ if (stream->content_len > 0 && stream == &stream->conn->loc) {
+ assert(stream->io.total <= stream->content_len);
+ if (stream->io.total == stream->content_len)
+ _shttpd_stop_stream(stream);
+ }
+
+ stream->conn->expire_time = _shttpd_current_time + EXPIRE_TIME;
}
static void
write_stream(struct stream *from, struct stream *to)
{
- int n, len;
- int sslerr = 0;
- len = io_data_len(&from->io);
- assert(len > 0);
+ int n, len;
- /* TODO: should be assert on CAN_WRITE flag */
- n = to->io_class->write(to, io_data(&from->io), len);
- to->conn->expire_time = current_time + EXPIRE_TIME;
- /*
- DBG(("write_stream (%d %s): written %d/%d bytes (errno %d)",
- to->conn->rem.chan.sock,
- to->io_class ? to->io_class->name : "(null)", n, len, ERRNO));
- */
+ len = io_data_len(&from->io);
+ assert(len > 0);
- if (n > 0) {
- io_inc_tail(&from->io, n);
- to->flags &= ~FLAG_SSL_SHOULD_SELECT_ON_READ;
- }
- else if (n == -1) {
- if(to->chan.ssl.ssl) {
- sslerr = SSL_get_error(to->chan.ssl.ssl, n);
- }
- /* Ignore SSL_ERROR_WANT_READ and SSL_ERROR_WANT_WRITE*/
- if((to->chan.ssl.ssl && sslerr == SSL_ERROR_SYSCALL &&
- (ERRNO == EINTR || ERRNO == EWOULDBLOCK)) ||
- (ERRNO == EINTR || ERRNO == EWOULDBLOCK)) {
- n = n; /* Ignore EINTR and EAGAIN */
- }
- else if(sslerr == SSL_ERROR_WANT_WRITE) {
- n = n;
- }
- else if(sslerr == SSL_ERROR_WANT_READ) {
- to->flags |= FLAG_SSL_SHOULD_SELECT_ON_READ;
- }
+ /* TODO: should be assert on CAN_WRITE flag */
+ n = to->io_class->write(to, io_data(&from->io), len);
+ to->conn->expire_time = _shttpd_current_time + EXPIRE_TIME;
+ DBG(("write_stream (%d %s): written %d/%d bytes (errno %d)",
+ to->conn->rem.chan.sock,
+ to->io_class ? to->io_class->name : "(null)", n, len, ERRNO));
- else if (!(to->flags & FLAG_DONT_CLOSE))
- stop_stream(to);
- }
- else if (!(to->flags & FLAG_DONT_CLOSE))
- stop_stream(to);
+ if (n > 0)
+ io_inc_tail(&from->io, n);
+ else if (n == -1 && (ERRNO == EINTR || ERRNO == EWOULDBLOCK))
+ n = n; /* Ignore EINTR and EAGAIN */
+ else if (!(to->flags & FLAG_DONT_CLOSE))
+ _shttpd_stop_stream(to);
}
-
static void
-disconnect(struct llhead *lp)
+connection_desctructor(struct llhead *lp)
{
struct conn *c = LL_ENTRY(lp, struct conn, link);
- static const struct vec ka = {"keep-alive", 10};
- int dont_close;
+ static const struct vec vec = {"close", 5};
+ int do_close;
DBG(("Disconnecting %d (%.*s)", c->rem.chan.sock,
c->ch.connection.v_vec.len, c->ch.connection.v_vec.ptr));
-#if !defined(_WIN32) || defined(NO_GUI)
- if (c->ctx->access_log != NULL)
-#endif /* _WIN32 */
- // log_access(c->ctx->access_log, c);
+ if (c->request != NULL && c->ctx->access_log != NULL)
+ _shttpd_log_access(c->ctx->access_log, c);
/* In inetd mode, exit if request is finished. */
- if (c->ctx->inetd_mode)
+ if (IS_TRUE(c->ctx, OPT_INETD))
exit(0);
if (c->loc.io_class != NULL && c->loc.io_class->close != NULL)
@@ -988,45 +992,50 @@
* Check the "Connection: " header before we free c->request
* If it its 'keep-alive', then do not close the connection
*/
- dont_close = c->ch.connection.v_vec.len >= ka.len &&
- !strncasecmp(ka.ptr, c->ch.connection.v_vec.ptr, ka.len);
- dont_close = 0;
+ do_close = (c->ch.connection.v_vec.len >= vec.len &&
+ !_shttpd_strncasecmp(vec.ptr,c->ch.connection.v_vec.ptr,vec.len)) ||
+ (c->major_version < 1 ||
+ (c->major_version >= 1 && c->minor_version < 1));
+
if (c->request)
free(c->request);
if (c->uri)
free(c->uri);
- /* Handle Keep-Alive */
- if (dont_close) {
+ /* Keep the connection open only if we have Content-Length set */
+ if (!do_close && c->loc.content_len > 0) {
c->loc.io_class = NULL;
c->loc.flags = 0;
- c->rem.flags = FLAG_W | FLAG_R;
+ c->loc.content_len = 0;
+ c->rem.flags = FLAG_W | FLAG_R | FLAG_SSL_ACCEPTED;
c->query = c->request = c->uri = c->path_info = NULL;
- c->mime_type = NULL;
+ c->mime_type.len = 0;
(void) memset(&c->ch, 0, sizeof(c->ch));
io_clear(&c->loc.io);
+ c->birth_time = _shttpd_current_time;
if (io_data_len(&c->rem.io) > 0)
process_connection(c, 0, 0);
} else {
if (c->rem.io_class != NULL)
c->rem.io_class->close(&c->rem);
- EnterCriticalSection(&c->ctx->mutex);
LL_DEL(&c->link);
- c->ctx->nactive--;
- assert(c->ctx->nactive >= 0);
- LeaveCriticalSection(&c->ctx->mutex);
- #ifdef SHTTPD_GSS
- {
- OM_uint32 majStat, minStat;
- majStat = gss_delete_sec_context(&minStat, c->gss_ctx, 0);
- }
- #endif
+ c->worker->num_conns--;
+ assert(c->worker->num_conns >= 0);
free(c);
}
}
+static void
+worker_destructor(struct llhead *lp)
+{
+ struct worker *worker = LL_ENTRY(lp, struct worker, link);
+
+ free_list(&worker->connections, connection_desctructor);
+ free(worker);
+}
+
static int
is_allowed(const struct shttpd_ctx *ctx, const struct usa *usa)
{
@@ -1037,10 +1046,6 @@
LL_FOREACH(&ctx->acl, lp) {
acl = LL_ENTRY(lp, struct acl, link);
-#ifdef ENABLE_IPV6
- if (wsmand_options_get_use_ipv6())
- return allowed;
-#endif
(void) memcpy(&ip, &usa->u.sin.sin_addr, sizeof(ip));
if (acl->ip == (ntohl(ip) & acl->mask))
allowed = acl->flag;
@@ -1060,23 +1065,19 @@
static void
process_connection(struct conn *c, int remote_ready, int local_ready)
{
-#if 0
- DBG(("loc: %u [%.*s]", io_data_len(&c->loc.io),
- io_data_len(&c->loc.io), io_data(&c->loc.io)));
- DBG(("rem: %u [%.*s]", io_data_len(&c->rem.io),
- io_data_len(&c->rem.io), io_data(&c->rem.io)));
- DBG(("locf=%x,remf=%x", c->loc.flags,c->rem.flags));
-#endif
-
/* Read from remote end if it is ready */
- if(c->loc.flags & FLAG_RESPONSE_COMPLETE)
- c->rem.flags &= ~ FLAG_HEADERS_PARSED;
if (remote_ready && io_space_len(&c->rem.io))
read_stream(&c->rem);
/* If the request is not parsed yet, do so */
if (!(c->rem.flags & FLAG_HEADERS_PARSED))
parse_http_request(c);
+
+ DBG(("loc: %d [%.*s]", (int) io_data_len(&c->loc.io),
+ (int) io_data_len(&c->loc.io), io_data(&c->loc.io)));
+ DBG(("rem: %d [%.*s]", (int) io_data_len(&c->rem.io),
+ (int) io_data_len(&c->rem.io), io_data(&c->rem.io)));
+
/* Read from the local end if it is ready */
if (local_ready && io_space_len(&c->loc.io))
read_stream(&c->loc);
@@ -1088,100 +1089,53 @@
if (io_data_len(&c->loc.io) > 0 && c->rem.io_class != NULL)
write_stream(&c->loc, &c->rem);
- if (c->rem.nread_last > 0)
- c->ctx->in += c->rem.nread_last;
- if (c->loc.nread_last > 0)
- c->ctx->out += c->loc.nread_last;
-
/* Check whether we should close this connection */
- if ((current_time > c->expire_time) ||
+ if ((_shttpd_current_time > c->expire_time) ||
(c->rem.flags & FLAG_CLOSED) ||
((c->loc.flags & FLAG_CLOSED) && !io_data_len(&c->loc.io)))
- disconnect(&c->link);
+ connection_desctructor(&c->link);
}
-/*
- * One iteration of server loop. This is the core of the data exchange.
- */
-void
-shttpd_poll(struct shttpd_ctx *ctx, int milliseconds)
+static int
+num_workers(const struct shttpd_ctx *ctx)
{
- struct llhead *lp, *tmp;
- struct listener *l;
- struct conn *c = NULL;
- struct timeval tv; /* Timeout for select() */
- fd_set read_set, write_set;
- int sock, max_fd = -1, msec = milliseconds;
- struct usa sa;
+ char *p = ctx->options[OPT_THREADS];
+ return (p ? atoi(p) : 1);
+}
- current_time = time(0);
- FD_ZERO(&read_set);
- FD_ZERO(&write_set);
-
- /* Add listening sockets to the read set */
- LL_FOREACH(&listeners, lp) {
- l = LL_ENTRY(lp, struct listener, link);
- FD_SET(l->sock, &read_set);
- if (l->sock > max_fd)
- max_fd = l->sock;
- //DBG(("FD_SET(%d) (listening)", l->sock));
+static void
+handle_connected_socket(struct shttpd_ctx *ctx,
+ struct usa *sap, int sock, int is_ssl)
+{
+#if !defined(_WIN32)
+ if (sock >= (int) FD_SETSIZE) {
+ _shttpd_elog(E_LOG, NULL, "ctx %p: discarding "
+ "socket %d, too busy", ctx, sock);
+ (void) closesocket(sock);
+ } else
+#endif /* !_WIN32 */
+ if (!is_allowed(ctx, sap)) {
+ _shttpd_elog(E_LOG, NULL, "%s is not allowed to connect",
+ inet_ntoa(sap->u.sin.sin_addr));
+ (void) closesocket(sock);
+ } else if (num_workers(ctx) > 1) {
+ pass_socket(ctx, sock, is_ssl);
+ } else {
+ add_socket(first_worker(ctx), sock, is_ssl);
}
+}
- /* Multiplex streams */
- LL_FOREACH(&ctx->connections, lp) {
- c = LL_ENTRY(lp, struct conn, link);
+static int
+do_select(int max_fd, fd_set *read_set, fd_set *write_set, int milliseconds)
+{
+ struct timeval tv;
+ int n;
- /* If there is a space in remote IO, check remote socket */
- if (c->rem.flags & FLAG_SSL_SHOULD_SELECT_ON_READ)
- add_to_set(c->rem.chan.fd, &read_set, &max_fd);
- else if (io_space_len(&c->rem.io) && c->rem.flags &
- FLAG_SSL_SHOULD_SELECT_ON_WRITE)
- add_to_set(c->rem.chan.fd, &write_set, &max_fd);
- else if (io_space_len(&c->rem.io))
- add_to_set(c->rem.chan.fd, &read_set, &max_fd);
-
-#if !defined(NO_CGI)
- /*
- * If there is a space in local IO, and local endpoint is
- * CGI, check local socket for read availability
- */
- if (io_space_len(&c->loc.io) && (c->loc.flags & FLAG_R) &&
- c->loc.io_class == &io_cgi)
- add_to_set(c->loc.chan.fd, &read_set, &max_fd);
-
- /*
- * If there is some data read from remote socket, and
- * local endpoint is CGI, check local for write availability
- */
- if (io_data_len(&c->rem.io) && (c->loc.flags & FLAG_W) &&
- c->loc.io_class == &io_cgi)
- add_to_set(c->loc.chan.fd, &write_set, &max_fd);
-#endif /* NO_CGI */
-
- /*
- * If there is some data read from local endpoint, check the
- * remote socket for write availability
- */
- if (io_data_len(&c->loc.io) && (c->rem.flags &
- FLAG_SSL_SHOULD_SELECT_ON_READ))
- add_to_set(c->rem.chan.fd, &read_set, &max_fd);
- else if (io_data_len(&c->loc.io))
- add_to_set(c->rem.chan.fd, &write_set, &max_fd);
-
- if (io_space_len(&c->loc.io) && (c->loc.flags & FLAG_R) &&
- (c->loc.flags & FLAG_ALWAYS_READY))
- msec = 0;
-
- if (io_data_len(&c->rem.io) && (c->loc.flags & FLAG_W) &&
- (c->loc.flags & FLAG_ALWAYS_READY))
- msec = 0;
- }
-
- tv.tv_sec = msec / 1000;
- tv.tv_usec = msec % 1000;
+ tv.tv_sec = milliseconds / 1000;
+ tv.tv_usec = (milliseconds % 1000) * 1000;
/* Check IO readiness */
- if (select(max_fd + 1, &read_set, &write_set, NULL, &tv) < 0) {
+ if ((n = select(max_fd + 1, read_set, write_set, NULL, &tv)) < 0) {
#ifdef _WIN32
/*
* On windows, if read_set and write_set are empty,
@@ -1192,117 +1146,168 @@
Sleep(milliseconds);
#endif /* _WIN32 */
DBG(("select: %d", ERRNO));
- if(c->rem.chan.ssl.ssl == NULL)
- return;
- else if(!SSL_pending(c->rem.chan.ssl.ssl))
- return;
}
+ return (n);
+}
+
+static int
+multiplex_worker_sockets(const struct worker *worker, int *max_fd,
+ fd_set *read_set, fd_set *write_set)
+{
+ struct llhead *lp;
+ struct conn *c;
+ int nowait = FALSE;
+
+ /* Add control socket */
+ add_to_set(worker->ctl[0], read_set, max_fd);
+
+ /* Multiplex streams */
+ LL_FOREACH(&worker->connections, lp) {
+ c = LL_ENTRY(lp, struct conn, link);
+
+ /* If there is a space in remote IO, check remote socket */
+ if (io_space_len(&c->rem.io))
+ add_to_set(c->rem.chan.fd, read_set, max_fd);
+
+#if !defined(NO_CGI)
+ /*
+ * If there is a space in local IO, and local endpoint is
+ * CGI, check local socket for read availability
+ */
+ if (io_space_len(&c->loc.io) && (c->loc.flags & FLAG_R) &&
+ c->loc.io_class == &_shttpd_io_cgi)
+ add_to_set(c->loc.chan.fd, read_set, max_fd);
+
+ /*
+ * If there is some data read from remote socket, and
+ * local endpoint is CGI, check local for write availability
+ */
+ if (io_data_len(&c->rem.io) && (c->loc.flags & FLAG_W) &&
+ c->loc.io_class == &_shttpd_io_cgi)
+ add_to_set(c->loc.chan.fd, write_set, max_fd);
+#endif /* NO_CGI */
+
+ /*
+ * If there is some data read from local endpoint, check the
+ * remote socket for write availability
+ */
+ if (io_data_len(&c->loc.io) && !(c->loc.flags & FLAG_SUSPEND))
+ add_to_set(c->rem.chan.fd, write_set, max_fd);
+
+ /*
+ * Set select wait interval to zero if FLAG_ALWAYS_READY set
+ */
+ if (io_space_len(&c->loc.io) && (c->loc.flags & FLAG_R) &&
+ (c->loc.flags & FLAG_ALWAYS_READY))
+ nowait = TRUE;
+
+ if (io_data_len(&c->rem.io) && (c->loc.flags & FLAG_W) &&
+ (c->loc.flags & FLAG_ALWAYS_READY))
+ nowait = TRUE;
+ }
+
+ return (nowait);
+}
+
+int
+shttpd_join(struct shttpd_ctx *ctx,
+ fd_set *read_set, fd_set *write_set, int *max_fd)
+{
+ struct llhead *lp;
+ struct listener *l;
+ int nowait = FALSE;
+
+ /* Add listening sockets to the read set */
+ LL_FOREACH(&ctx->listeners, lp) {
+ l = LL_ENTRY(lp, struct listener, link);
+ add_to_set(l->sock, read_set, max_fd);
+ DBG(("FD_SET(%d) (listening)", l->sock));
+ }
+
+ if (num_workers(ctx) == 1)
+ nowait = multiplex_worker_sockets(first_worker(ctx), max_fd,
+ read_set, write_set);
+
+ return (nowait);
+}
+
+
+static void
+process_worker_sockets(struct worker *worker, fd_set *read_set)
+{
+ struct llhead *lp, *tmp;
+ int cmd, skt[2], sock = worker->ctl[0];
+ struct conn *c;
+
+ /* Check if new socket is passed to us over the control socket */
+ if (FD_ISSET(worker->ctl[0], read_set))
+ while (recv(sock, (void *) &cmd, sizeof(cmd), 0) == sizeof(cmd))
+ switch (cmd) {
+ case CTL_PASS_SOCKET:
+ (void)recv(sock, (void *) &skt, sizeof(skt), 0);
+ add_socket(worker, skt[0], skt[1]);
+ break;
+ case CTL_WAKEUP:
+ (void)recv(sock, (void *) &c, sizeof(c), 0);
+ c->loc.flags &= FLAG_SUSPEND;
+ break;
+ default:
+ _shttpd_elog(E_FATAL, NULL, "ctx %p: ctl cmd %d",
+ worker->ctx, cmd);
+ break;
+ }
+
+ /* Process all connections */
+ LL_FOREACH_SAFE(&worker->connections, lp, tmp) {
+ c = LL_ENTRY(lp, struct conn, link);
+ process_connection(c, FD_ISSET(c->rem.chan.sock, read_set),
+ c->loc.io_class != NULL &&
+ ((c->loc.flags & FLAG_ALWAYS_READY)
+#if !defined(NO_CGI)
+ || (c->loc.io_class == &_shttpd_io_cgi &&
+ FD_ISSET(c->loc.chan.fd, read_set))
+#endif /* NO_CGI */
+ ));
+ }
+}
+
+/*
+ * One iteration of server loop. This is the core of the data exchange.
+ */
+void
+shttpd_poll(struct shttpd_ctx *ctx, int milliseconds)
+{
+ struct llhead *lp;
+ struct listener *l;
+ fd_set read_set, write_set;
+ int sock, max_fd = -1;
+ struct usa sa;
+
+ _shttpd_current_time = time(0);
+ FD_ZERO(&read_set);
+ FD_ZERO(&write_set);
+
+ if (shttpd_join(ctx, &read_set, &write_set, &max_fd))
+ milliseconds = 0;
+
+ if (do_select(max_fd, &read_set, &write_set, milliseconds) < 0)
+ return;;
+
/* Check for incoming connections on listener sockets */
- LL_FOREACH(&listeners, lp) {
+ LL_FOREACH(&ctx->listeners, lp) {
l = LL_ENTRY(lp, struct listener, link);
if (!FD_ISSET(l->sock, &read_set))
continue;
do {
sa.len = sizeof(sa.u.sin);
- if ((sock = accept(l->sock, &sa.u.sa, &sa.len)) != -1) {
-#if defined(_WIN32)
- shttpd_add_socket(ctx, sock, l->is_ssl);
-#else
- if (sock >= (int) FD_SETSIZE) {
- elog(E_LOG, NULL,
- "shttpd_poll: ctx %p: disarding "
- "socket %d, too busy", ctx, sock);
- (void) closesocket(sock);
- } else if (!is_allowed(ctx, &sa)) {
- //elog(E_LOG, NULL, "shttpd_poll: %s is not allowed to connect", inet_ntoa(sa.u.sin.sin_addr));
- (void) closesocket(sock);
- } else {
- shttpd_add_socket(ctx, sock, l->is_ssl);
- }
-#endif /* _WIN32 */
- }
+ if ((sock = accept(l->sock, &sa.u.sa, &sa.len)) != -1)
+ handle_connected_socket(ctx,&sa,sock,l->is_ssl);
} while (sock != -1);
}
- /* Process all connections */
- LL_FOREACH_SAFE(&ctx->connections, lp, tmp) {
- c = LL_ENTRY(lp, struct conn, link);
-#ifndef NO_SSL
- process_connection(c, ((FD_ISSET(c->rem.chan.fd, &read_set) &&
- !(c->rem.flags & FLAG_SSL_SHOULD_SELECT_ON_READ)) ||
- (c->rem.chan.ssl.ssl && SSL_pending(c->rem.chan.ssl.ssl)) ||
- (FD_ISSET(c->rem.chan.fd, &write_set) &&
- (c->rem.flags & FLAG_SSL_SHOULD_SELECT_ON_WRITE))),
- ((c->loc.flags & FLAG_ALWAYS_READY)
-
-#else
-
- process_connection(c, FD_ISSET(c->rem.chan.fd, &read_set),
- ((c->loc.flags & FLAG_ALWAYS_READY)
-#endif
-#if !defined(NO_CGI)
- || (c->loc.io_class == &io_cgi &&
- FD_ISSET(c->loc.chan.fd, &read_set))
-#endif /* NO_CGI */
- ));
- }
-}
-
-static void
-free_list(struct llhead *head, void (*dtor)(struct llhead *))
-{
- struct llhead *lp, *tmp;
-
- LL_FOREACH_SAFE(head, lp, tmp) {
- LL_DEL(lp);
- dtor(lp);
- }
-}
-
-static void
-listener_desctructor(struct llhead *lp)
-{
- struct listener *listener = LL_ENTRY(lp, struct listener, link);
-
- (void) closesocket(listener->sock);
- free(listener);
-}
-
-static void
-mime_type_destructor(struct llhead *lp)
-{
- struct mime_type_link *mtl = LL_ENTRY(lp, struct mime_type_link, link);
-
- free(mtl->mime);
- free(mtl->ext);
- free(mtl);
-}
-
-static void
-registered_uri_destructor(struct llhead *lp)
-{
- struct registered_uri *ruri = LL_ENTRY(lp, struct registered_uri, link);
-
- free((void *) ruri->uri);
- free(ruri);
-}
-
-static void
-acl_destructor(struct llhead *lp)
-{
- struct acl *acl = LL_ENTRY(lp, struct acl, link);
- free(acl);
-}
-
-static void
-protected_uri_destructor(struct llhead *lp)
-{
- struct uri_auth *auth = LL_ENTRY(lp, struct uri_auth, link);
-
- free((void *) auth->file_name);
- free((void *) auth->uri);
- free(auth);
+ if (num_workers(ctx) == 1)
+ process_worker_sockets(first_worker(ctx), &read_set);
}
/*
@@ -1311,51 +1316,625 @@
void
shttpd_fini(struct shttpd_ctx *ctx)
{
- free_list(&ctx->mime_types, mime_type_destructor);
- free_list(&ctx->connections, disconnect);
- free_list(&ctx->registered_uris, registered_uri_destructor);
- free_list(&ctx->uri_auths, protected_uri_destructor);
- free_list(&ctx->acl, acl_destructor);
- free_list(&listeners, listener_desctructor);
+ size_t i;
+ free_list(&ctx->workers, worker_destructor);
+ free_list(&ctx->registered_uris, registered_uri_destructor);
+ free_list(&ctx->acl, acl_destructor);
+ free_list(&ctx->listeners, listener_destructor);
#if !defined(NO_SSI)
- if (ctx->ssi_extensions) free(ctx->ssi_extensions);
- free_list(&ctx->ssi_funcs, ssi_func_destructor);
-#endif /* NO_SSI */
+ free_list(&ctx->ssi_funcs, _shttpd_ssi_func_destructor);
+#endif /* !NO_SSI */
+
+ for (i = 0; i < NELEMS(ctx->options); i++)
+ if (ctx->options[i] != NULL)
+ free(ctx->options[i]);
if (ctx->access_log) (void) fclose(ctx->access_log);
if (ctx->error_log) (void) fclose(ctx->error_log);
- if (ctx->put_auth_file) free(ctx->put_auth_file);
- if (ctx->document_root) free(ctx->document_root);
- if (ctx->index_files) free(ctx->index_files);
- if (ctx->aliases) free(ctx->aliases);
-#if !defined(NO_CGI)
- if (ctx->cgi_vars) free(ctx->cgi_vars);
- if (ctx->cgi_extensions) free(ctx->cgi_extensions);
- if (ctx->cgi_interpreter) free(ctx->cgi_interpreter);
-#endif /* NO_CGI */
- if (ctx->auth_realm) free(ctx->auth_realm);
- if (ctx->global_passwd_file) free(ctx->global_passwd_file);
- if (ctx->uid) free(ctx->uid);
- if (ctx->mime_file) free(ctx->mime_file);
- if (ctx->ports) free(ctx->ports);
/* TODO: free SSL context */
- if(ctx->ssl_ctx)
- SSL_CTX_free(ctx->ssl_ctx);
+
free(ctx);
}
-void
-open_listening_ports(struct shttpd_ctx *ctx)
+/*
+ * UNIX socketpair() implementation. Why? Because Windows does not have it.
+ * Return 0 on success, -1 on error.
+ */
+int
+shttpd_socketpair(int sp[2])
{
- const char *p = ctx->ports;
- int len, is_ssl;
+ struct sockaddr_in sa;
+ int sock, ret = -1;
+ socklen_t len = sizeof(sa);
- FOR_EACH_WORD_IN_LIST(p, len) {
- is_ssl = p[len - 1] == 's' ? 1 : 0;
- if (shttpd_listen(ctx, atoi(p), is_ssl) == -1)
- elog(E_FATAL, NULL,
- "Cannot open socket on port %d", atoi(p));
+ sp[0] = sp[1] = -1;
+
+ (void) memset(&sa, 0, sizeof(sa));
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(0);
+ sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) != -1 &&
+ !bind(sock, (struct sockaddr *) &sa, len) &&
+ !listen(sock, 1) &&
+ !getsockname(sock, (struct sockaddr *) &sa, &len) &&
+ (sp[0] = socket(AF_INET, SOCK_STREAM, 6)) != -1 &&
+ !connect(sp[0], (struct sockaddr *) &sa, len) &&
+ (sp[1] = accept(sock,(struct sockaddr *) &sa, &len)) != -1) {
+
+ /* Success */
+ ret = 0;
+ } else {
+
+ /* Failure, close descriptors */
+ if (sp[0] != -1)
+ (void) closesocket(sp[0]);
+ if (sp[1] != -1)
+ (void) closesocket(sp[1]);
}
+
+ (void) closesocket(sock);
+ (void) _shttpd_set_non_blocking_mode(sp[0]);
+ (void) _shttpd_set_non_blocking_mode(sp[1]);
+
+#ifndef _WIN32
+ (void) fcntl(sp[0], F_SETFD, FD_CLOEXEC);
+ (void) fcntl(sp[1], F_SETFD, FD_CLOEXEC);
+#endif /* _WIN32*/
+
+ return (ret);
+}
+
+static int isbyte(int n) { return (n >= 0 && n <= 255); }
+
+static int
+set_inetd(struct shttpd_ctx *ctx, const char *flag)
+{
+ ctx = NULL; /* Unused */
+
+ if (_shttpd_is_true(flag)) {
+ shttpd_set_option(ctx, "ports", NULL);
+ (void) freopen("/dev/null", "a", stderr);
+ add_socket(first_worker(ctx), 0, 0);
+ }
+
+ return (TRUE);
+}
+
+static int
+set_uid(struct shttpd_ctx *ctx, const char *uid)
+{
+ struct passwd *pw;
+
+ ctx = NULL; /* Unused */
+
+#if !defined(_WIN32)
+ if ((pw = getpwnam(uid)) == NULL)
+ _shttpd_elog(E_FATAL, 0, "%s: unknown user [%s]", __func__, uid);
+ else if (setgid(pw->pw_gid) == -1)
+ _shttpd_elog(E_FATAL, NULL, "%s: setgid(%s): %s",
+ __func__, uid, strerror(errno));
+ else if (setuid(pw->pw_uid) == -1)
+ _shttpd_elog(E_FATAL, NULL, "%s: setuid(%s): %s",
+ __func__, uid, strerror(errno));
+#endif /* !_WIN32 */
+ return (TRUE);
+}
+
+static int
+set_acl(struct shttpd_ctx *ctx, const char *s)
+{
+ struct acl *acl = NULL;
+ char flag;
+ int len, a, b, c, d, n, mask;
+
+ /* Delete the old ACLs if any */
+ free_list(&ctx->acl, acl_destructor);
+
+ FOR_EACH_WORD_IN_LIST(s, len) {
+
+ mask = 32;
+
+ if (sscanf(s, "%c%d.%d.%d.%d%n",&flag,&a,&b,&c,&d,&n) != 5) {
+ _shttpd_elog(E_FATAL, NULL, "[%s]: subnet must be "
+ "[+|-]x.x.x.x[/x]", s);
+ } else if (flag != '+' && flag != '-') {
+ _shttpd_elog(E_FATAL, NULL, "flag must be + or -: [%s]", s);
+ } else if (!isbyte(a)||!isbyte(b)||!isbyte(c)||!isbyte(d)) {
+ _shttpd_elog(E_FATAL, NULL, "bad ip address: [%s]", s);
+ } else if ((acl = malloc(sizeof(*acl))) == NULL) {
+ _shttpd_elog(E_FATAL, NULL, "%s", "cannot malloc subnet");
+ } else if (sscanf(s + n, "/%d", &mask) == 0) {
+ /* Do nothing, no mask specified */
+ } else if (mask < 0 || mask > 32) {
+ _shttpd_elog(E_FATAL, NULL, "bad subnet mask: %d [%s]", n, s);
+ }
+
+ acl->ip = (a << 24) | (b << 16) | (c << 8) | d;
+ acl->mask = mask ? 0xffffffffU << (32 - mask) : 0;
+ acl->flag = flag;
+ LL_TAIL(&ctx->acl, &acl->link);
+ }
+
+ return (TRUE);
+}
+
+#ifndef NO_SSL
+/*
+ * Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
+ */
+static int
+set_ssl(struct shttpd_ctx *ctx, const char *pem)
+{
+ SSL_CTX *CTX;
+ void *lib;
+ struct ssl_func *fp;
+ char *ssl_disabled_protocols = wsmand_options_get_ssl_disabled_protocols();
+ int retval = FALSE;
+
+ /* Load SSL library dynamically */
+ if ((lib = dlopen(SSL_LIB, RTLD_LAZY)) == NULL) {
+ _shttpd_elog(E_LOG, NULL, "set_ssl: cannot load %s", SSL_LIB);
+ return (FALSE);
+ }
+
+ for (fp = ssl_sw; fp->name != NULL; fp++)
+ if ((fp->ptr.v_void = dlsym(lib, fp->name)) == NULL) {
+ _shttpd_elog(E_LOG, NULL,"set_ssl: cannot find %s", fp->name);
+ return (FALSE);
+ }
+
+ /* Initialize SSL crap */
+ SSL_library_init();
+
+ if ((CTX = SSL_CTX_new(SSLv23_server_method())) == NULL)
+ _shttpd_elog(E_LOG, NULL, "SSL_CTX_new error");
+ else if (SSL_CTX_use_certificate_file(CTX, wsmand_options_get_ssl_cert_file(), SSL_FILETYPE_PEM) != 1)
+ _shttpd_elog(E_LOG, NULL, "cannot open certificate file %s", pem);
+ else if (SSL_CTX_use_PrivateKey_file(CTX, wsmand_options_get_ssl_key_file(), SSL_FILETYPE_PEM) != 1)
+ _shttpd_elog(E_LOG, NULL, "cannot open PrivateKey %s", pem);
+ else
+ retval = TRUE;
+
+ while (ssl_disabled_protocols) {
+ struct ctx_opts_t {
+ char *name;
+ long opt;
+ } protocols[] = {
+ { "SSLv2", SSL_OP_NO_SSLv2 },
+ { "SSLv3", SSL_OP_NO_SSLv3 },
+ { "TLSv1", SSL_OP_NO_TLSv1 },
+# if OPENSSL_VERSION_NUMBER >= 0x10001000L
+ { "TLSv1_1", SSL_OP_NO_TLSv1_1 },
+ { "TLSv1_2", SSL_OP_NO_TLSv1_2 },
+# endif
+ { NULL, 0 }
+ };
+ char *blank_ptr;
+ int idx;
+ if (*ssl_disabled_protocols == 0)
+ break;
+ blank_ptr = strchr(ssl_disabled_protocols, ' ');
+ if (blank_ptr == NULL)
+ blank_ptr = ssl_disabled_protocols + strlen(ssl_disabled_protocols);
+ for (idx = 0; protocols[idx].name ; ++idx) {
+ if (strncasecmp(protocols[idx].name, ssl_disabled_protocols, blank_ptr-ssl_disabled_protocols) == 0) {
+ //_shttpd_elog(E_LOG, NULL, "SSL: disable %s protocol", protocols[idx].name);
+ debug("SSL: disable %s protocol", protocols[idx].name);
+ SSL_CTX_ctrl(CTX, SSL_CTRL_OPTIONS, protocols[idx].opt, NULL);
+ break;
+ }
+ }
+ if (*blank_ptr == 0)
+ break;
+ ssl_disabled_protocols = blank_ptr + 1;
+ }
+
+ ctx->ssl_ctx = CTX;
+
+ return (retval);
+}
+#endif /* NO_SSL */
+
+static int
+open_log_file(FILE **fpp, const char *path)
+{
+ int retval = TRUE;
+
+ if (*fpp != NULL)
+ (void) fclose(*fpp);
+
+ if (path == NULL) {
+ *fpp = NULL;
+ } else if ((*fpp = fopen(path, "a")) == NULL) {
+ _shttpd_elog(E_LOG, NULL, "cannot open log file %s: %s",
+ path, strerror(errno));
+ retval = FALSE;
+ }
+
+ return (retval);
+}
+
+static int set_alog(struct shttpd_ctx *ctx, const char *path) {
+ return (open_log_file(&ctx->access_log, path));
+}
+
+static int set_elog(struct shttpd_ctx *ctx, const char *path) {
+ return (open_log_file(&ctx->error_log, path));
+}
+
+static void show_cfg_page(struct shttpd_arg *arg);
+
+static int
+set_cfg_uri(struct shttpd_ctx *ctx, const char *uri)
+{
+ free_list(&ctx->registered_uris, ®istered_uri_destructor);
+
+ if (uri != NULL)
+ shttpd_register_uri(ctx, uri, &show_cfg_page, ctx);
+
+ return (TRUE);
+}
+
+static struct worker *
+add_worker(struct shttpd_ctx *ctx)
+{
+ struct worker *worker;
+
+ if ((worker = calloc(1, sizeof(*worker))) == NULL)
+ _shttpd_elog(E_FATAL, NULL, "Cannot allocate worker");
+ LL_INIT(&worker->connections);
+ worker->ctx = ctx;
+ (void) shttpd_socketpair(worker->ctl);
+ LL_TAIL(&ctx->workers, &worker->link);
+
+ return (worker);
+}
+
+#if !defined(NO_THREADS)
+static void
+poll_worker(struct worker *worker, int milliseconds)
+{
+ fd_set read_set, write_set;
+ int max_fd = -1;
+
+ FD_ZERO(&read_set);
+ FD_ZERO(&write_set);
+
+ if (multiplex_worker_sockets(worker, &max_fd, &read_set, &write_set))
+ milliseconds = 0;
+
+ if (do_select(max_fd, &read_set, &write_set, milliseconds) < 0)
+ return;;
+
+ process_worker_sockets(worker, &read_set);
+}
+
+static void
+worker_function(void *param)
+{
+ struct worker *worker = param;
+
+ while (worker->exit_flag == 0)
+ poll_worker(worker, 1000 * 10);
+
+ free_list(&worker->connections, connection_desctructor);
+ free(worker);
+}
+
+static int
+set_workers(struct shttpd_ctx *ctx, const char *value)
+{
+ int new_num, old_num;
+ struct llhead *lp, *tmp;
+ struct worker *worker;
+
+ new_num = atoi(value);
+ old_num = 0;
+ LL_FOREACH(&ctx->workers, lp)
+ old_num++;
+
+ if (new_num == 1) {
+ if (old_num > 1)
+ /* Stop old threads */
+ LL_FOREACH_SAFE(&ctx->workers, lp, tmp) {
+ worker = LL_ENTRY(lp, struct worker, link);
+ LL_DEL(&worker->link);
+ worker = LL_ENTRY(lp, struct worker, link);
+ worker->exit_flag = 1;
+ }
+ (void) add_worker(ctx);
+ } else {
+ /* FIXME: we cannot here reduce the number of threads */
+ while (new_num > 1 && new_num > old_num) {
+ worker = add_worker(ctx);
+ _beginthread(worker_function, 0, worker);
+ old_num++;
+ }
+ }
+
+ return (TRUE);
+}
+#endif /* NO_THREADS */
+
+static const struct opt {
+ int index; /* Index in shttpd_ctx */
+ const char *name; /* Option name in config file */
+ const char *description; /* Description */
+ const char *default_value; /* Default option value */
+ int (*setter)(struct shttpd_ctx *, const char *);
+} known_options[] = {
+ {OPT_ROOT, "root", "\tWeb root directory", ".", NULL},
+ {OPT_INDEX_FILES, "index_files", "Index files", INDEX_FILES, NULL},
+#ifndef NO_SSL
+ {OPT_SSL_CERTIFICATE, "ssl_cert", "SSL certificate file", NULL,set_ssl},
+#endif /* NO_SSL */
+ {OPT_PORTS, "ports", "Listening ports", NULL, set_ports},
+ {OPT_DIR_LIST, "dir_list", "Directory listing", "yes", NULL},
+ {OPT_CFG_URI, "cfg_uri", "Config uri", NULL, set_cfg_uri},
+ {OPT_PROTECT, "protect", "URI to htpasswd mapping", NULL, NULL},
+#ifndef NO_CGI
+ {OPT_CGI_EXTENSIONS, "cgi_ext", "CGI extensions", CGI_EXT, NULL},
+ {OPT_CGI_INTERPRETER, "cgi_interp", "CGI interpreter", NULL, NULL},
+ {OPT_CGI_ENVIRONMENT, "cgi_env", "Additional CGI env vars", NULL, NULL},
+#endif /* NO_CGI */
+ {OPT_SSI_EXTENSIONS, "ssi_ext", "SSI extensions", SSI_EXT, NULL},
+#ifndef NO_AUTH
+ {OPT_AUTH_REALM, "auth_realm", "Authentication domain name",REALM,NULL},
+ {OPT_AUTH_GPASSWD, "auth_gpass", "Global passwords file", NULL, NULL},
+ {OPT_AUTH_PUT, "auth_PUT", "PUT,DELETE auth file", NULL, NULL},
+#endif /* !NO_AUTH */
+#ifdef _WIN32
+ {OPT_SERVICE, "service", "Manage WinNNT service (install"
+ "|uninstall)", NULL, _shttpd_set_nt_service},
+ {OPT_HIDE, "systray", "Hide console, show icon on systray",
+ "no", _shttpd_set_systray},
+#else
+ {OPT_INETD, "inetd", "Inetd mode", "no", set_inetd},
+ {OPT_UID, "uid", "\tRun as user", NULL, set_uid},
+#endif /* _WIN32 */
+ {OPT_ACCESS_LOG, "access_log", "Access log file", NULL, set_alog},
+ {OPT_ERROR_LOG, "error_log", "Error log file", NULL, set_elog},
+ {OPT_MIME_TYPES, "mime_types", "Additional mime types list", NULL,NULL},
+ {OPT_ALIASES, "aliases", "Path=URI mappings", NULL, NULL},
+ {OPT_ACL, "acl", "\tAllow/deny IP addresses/subnets", NULL, set_acl},
+#if !defined(NO_THREADS)
+ {OPT_THREADS, "threads", "Number of worker threads", "1", set_workers},
+#endif /* !NO_THREADS */
+ {-1, NULL, NULL, NULL, NULL}
+};
+
+static const struct opt *
+find_opt(const char *opt_name)
+{
+ int i;
+
+ for (i = 0; known_options[i].name != NULL; i++)
+ if (!strcmp(opt_name, known_options[i].name))
+ return (known_options + i);
+
+ _shttpd_elog(E_FATAL, NULL, "no such option: [%s]", opt_name);
+
+ /* UNREACHABLE */
+ return (NULL);
+}
+
+int
+shttpd_set_option(struct shttpd_ctx *ctx, const char *opt, const char *val)
+{
+ const struct opt *o = find_opt(opt);
+ int retval = TRUE;
+
+ /* Call option setter first, so it can use both new and old values */
+ if (o->setter != NULL)
+ retval = o->setter(ctx, val);
+
+ /* Free old value if any */
+ if (ctx->options[o->index] != NULL)
+ free(ctx->options[o->index]);
+
+ /* Set new option value */
+ ctx->options[o->index] = val ? _shttpd_strdup(val) : NULL;
+
+ return (retval);
+}
+
+static void
+show_cfg_page(struct shttpd_arg *arg)
+{
+ struct shttpd_ctx *ctx = arg->user_data;
+ char opt_name[20], value[BUFSIZ];
+ const struct opt *o;
+
+ opt_name[0] = value[0] = '\0';
+
+ if (!strcmp(shttpd_get_env(arg, "REQUEST_METHOD"), "POST")) {
+ if (arg->flags & SHTTPD_MORE_POST_DATA)
+ return;
+ (void) shttpd_get_var("o", arg->in.buf, arg->in.len,
+ opt_name, sizeof(opt_name));
+ (void) shttpd_get_var("v", arg->in.buf, arg->in.len,
+ value, sizeof(value));
+ shttpd_set_option(ctx, opt_name, value[0] ? value : NULL);
+ }
+
+ shttpd_printf(arg, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"
+ "<html><body><h1>SHTTPD v. %s</h1>", shttpd_version());
+
+ shttpd_printf(arg, "%s", "<table border=1"
+ "<tr><th>Option</th><th>Description</th>"
+ "<th colspan=2>Value</th></tr>");
+
+ if (opt_name[0] != '\0' && value[0] != '\0')
+ shttpd_printf(arg, "<p style='color: green'>Saved: %s=%s</p>",
+ opt_name, value[0] ? value : "NULL");
+
+
+ for (o = known_options; o->name != NULL; o++) {
+ shttpd_printf(arg,
+ "<form method=post><tr><td>%s</td><td>%s</td>"
+ "<input type=hidden name=o value='%s'>"
+ "<td><input type=text name=v value='%s'></td>"
+ "<td><input type=submit value=save></td></form></tr>",
+ o->name, o->description, o->name,
+ ctx->options[o->index] ? ctx->options[o->index] : "");
+ }
+
+ shttpd_printf(arg, "%s", "</table></body></html>");
+ arg->flags |= SHTTPD_END_OF_OUTPUT;
+}
+
+/*
+ * Show usage string and exit.
+ */
+void
+_shttpd_usage(const char *prog)
+{
+ const struct opt *o;
+
+ (void) fprintf(stderr,
+ "SHTTPD version %s (c) Sergey Lyubka\n"
+ "usage: %s [options] [config_file]\n", SHTTPD_VERSION, prog);
+
+#if !defined(NO_AUTH)
+ fprintf(stderr, " -A <htpasswd_file> <realm> <user> <passwd>\n");
+#endif /* NO_AUTH */
+
+ for (o = known_options; o->name != NULL; o++) {
+ (void) fprintf(stderr, " -%s\t%s", o->name, o->description);
+ if (o->default_value != NULL)
+ fprintf(stderr, " (default: %s)", o->default_value);
+ fputc('\n', stderr);
+ }
+
+ exit(EXIT_FAILURE);
+}
+
+static void
+set_opt(struct shttpd_ctx *ctx, const char *opt, const char *value)
+{
+ const struct opt *o;
+
+ o = find_opt(opt);
+ if (ctx->options[o->index] != NULL)
+ free(ctx->options[o->index]);
+ ctx->options[o->index] = _shttpd_strdup(value);
+}
+
+static void
+process_command_line_arguments(struct shttpd_ctx *ctx, char *argv[])
+{
+ const char *config_file = CONFIG_FILE;
+ char line[BUFSIZ], opt[BUFSIZ],
+ val[BUFSIZ], path[FILENAME_MAX], *p;
+ FILE *fp;
+ size_t i, line_no = 0;
+
+ /* First find out, which config file to open */
+ for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2)
+ if (argv[i + 1] == NULL)
+ _shttpd_usage(argv[0]);
+
+ if (argv[i] != NULL && argv[i + 1] != NULL) {
+ /* More than one non-option arguments are given w*/
+ _shttpd_usage(argv[0]);
+ } else if (argv[i] != NULL) {
+ /* Just one non-option argument is given, this is config file */
+ config_file = argv[i];
+ } else {
+ /* No config file specified. Look for one where shttpd lives */
+ if ((p = strrchr(argv[0], DIRSEP)) != 0) {
+ _shttpd_snprintf(path, sizeof(path), "%.*s%s",
+ p - argv[0] + 1, argv[0], config_file);
+ config_file = path;
+ }
+ }
+
+ fp = fopen(config_file, "r");
+
+ /* If config file was set in command line and open failed, exit */
+ if (fp == NULL && argv[i] != NULL)
+ _shttpd_elog(E_FATAL, NULL, "cannot open config file %s: %s",
+ config_file, strerror(errno));
+
+ if (fp != NULL) {
+
+ _shttpd_elog(E_LOG, NULL, "Loading config file %s", config_file);
+
+ /* Loop over the lines in config file */
+ while (fgets(line, sizeof(line), fp) != NULL) {
+
+ line_no++;
+
+ /* Ignore empty lines and comments */
+ if (line[0] == '#' || line[0] == '\n')
+ continue;
+
+ if (sscanf(line, "%s %[^\n#]", opt, val) != 2)
+ _shttpd_elog(E_FATAL, NULL, "line %d in %s is invalid",
+ line_no, config_file);
+
+ set_opt(ctx, opt, val);
+ }
+
+ (void) fclose(fp);
+ }
+
+ /* Now pass through the command line options */
+ for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2)
+ set_opt(ctx, &argv[i][1], argv[i + 1]);
+}
+
+struct shttpd_ctx *
+shttpd_init(int argc, char *argv[])
+{
+ struct shttpd_ctx *ctx;
+ struct tm *tm;
+ const struct opt *o;
+
+ if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
+ _shttpd_elog(E_FATAL, NULL, "cannot allocate shttpd context");
+
+ LL_INIT(&ctx->registered_uris);
+ LL_INIT(&ctx->uri_auths);
+ LL_INIT(&ctx->error_handlers);
+ LL_INIT(&ctx->acl);
+ LL_INIT(&ctx->ssi_funcs);
+ LL_INIT(&ctx->listeners);
+ LL_INIT(&ctx->workers);
+
+ /* Initialize options. First pass: set default option values */
+ for (o = known_options; o->name != NULL; o++)
+ ctx->options[o->index] = o->default_value ?
+ _shttpd_strdup(o->default_value) : NULL;
+
+ /* Second and third passes: config file and argv */
+ if (argc > 0 && argv != NULL)
+ process_command_line_arguments(ctx, argv);
+
+ /* Call setter functions */
+ for (o = known_options; o->name != NULL; o++)
+ if (o->setter && ctx->options[o->index] != NULL)
+ if (o->setter(ctx, ctx->options[o->index]) == FALSE) {
+ shttpd_fini(ctx);
+ return (NULL);
+ }
+
+ _shttpd_current_time = time(NULL);
+ tm = localtime(&_shttpd_current_time);
+ _shttpd_tz_offset = 0;
+
+ if (num_workers(ctx) == 1)
+ (void) add_worker(ctx);
+#if 0
+ tm->tm_gmtoff - 3600 * (tm->tm_isdst > 0 ? 1 : 0);
+#endif
+
+#ifdef _WIN32
+ {WSADATA data; WSAStartup(MAKEWORD(2,2), &data);}
+#endif /* _WIN32 */
+
+ return (ctx);
}
diff --git a/src/server/shttpd/shttpd.h b/src/server/shttpd/shttpd.h
index f134ead..a7d33aa 100644
--- a/src/server/shttpd/shttpd.h
+++ b/src/server/shttpd/shttpd.h
@@ -1,25 +1,13 @@
/*
- * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * Copyright (c) 2004-2008 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
*
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
*
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * $Id: shttpd.h,v 1.6 2008/01/11 11:20:31 drozd Exp $
+ * $Id: shttpd.h,v 1.18 2008/08/23 08:34:50 drozd Exp $
*/
#ifndef SHTTPD_HEADER_INCLUDED
@@ -41,67 +29,67 @@
struct shttpd_arg {
void *priv; /* Private! Do not touch! */
void *state; /* User state */
- void *user_data; /* User-defined data */
+ void *user_data; /* Data from register_uri() */
struct ubuf in; /* Input is here, POST data */
struct ubuf out; /* Output goes here */
+
unsigned int flags;
-#define SHTTPD_END_OF_OUTPUT 1
-#define SHTTPD_CONNECTION_ERROR 2
-#define SHTTPD_MORE_POST_DATA 4
-#define SHTTPD_POST_BUFFER_FULL 8
-#define SHTTPD_SSI_EVAL_TRUE 16
+#define SHTTPD_END_OF_OUTPUT 1 /* No more data do send */
+#define SHTTPD_CONNECTION_ERROR 2 /* Server closed the connection */
+#define SHTTPD_MORE_POST_DATA 4 /* arg->in has incomplete data */
+#define SHTTPD_POST_BUFFER_FULL 8 /* arg->in has max data */
+#define SHTTPD_SSI_EVAL_TRUE 16 /* SSI eval callback must set it*/
+#define SHTTPD_SUSPEND 32 /* User wants to suspend output */
};
/*
* User callback function. Called when certain registered URLs have been
* requested. These are the requirements to the callback function:
*
- * 1. it must copy data into 'out.buf' buffer, not more than 'out.len' bytes,
+ * 1. It must copy data into 'out.buf' buffer, not more than 'out.len' bytes,
* and record how many bytes are copied, into 'out.num_bytes'
- * 2. it must not block the execution
- * 3. it must set SHTTPD_END_OF_OUTPUT flag when finished
- * 4. for POST requests, it must process the incoming data (in.buf) of length
+ * 2. It must not call any blocking functions
+ * 3. It must set SHTTPD_END_OF_OUTPUT flag when there is no more data to send
+ * 4. For POST requests, it must process the incoming data (in.buf) of length
* 'in.len', and set 'in.num_bytes', which is how many bytes of POST
- * data is read and can be discarded by SHTTPD.
+ * data was processed and can be discarded by SHTTPD.
* 5. If callback allocates arg->state, to keep state, it must deallocate it
* at the end of coonection SHTTPD_CONNECTION_ERROR or SHTTPD_END_OF_OUTPUT
+ * 6. If callback function wants to suspend until some event, it must store
+ * arg->priv pointer elsewhere, set SHTTPD_SUSPEND flag and return. When
+ * the event happens, user code should call shttpd_wakeup(priv).
+ * It is safe to call shttpd_wakeup() from any thread. User code must
+ * not call shttpd_wakeup once the connection is closed.
*/
typedef void (*shttpd_callback_t)(struct shttpd_arg *);
/*
- * shttpd_init Initialize shttpd context. Parameters: configuration
- * file name (may be NULL), then NULL-terminated
- * sequence of pairs "option_name", "option_value".
- * shttpd_init2 Same as shttpd_init, but the list of option/value
- * pairs is passed in arrays
- * shttpd_fini Dealocate the context
- * shttpd_register_uri Setup the callback function for specified URL.
- * shttpd_protect_uri Associate authorization file with an URL.
- * shttpd_add_mime_type Add mime type
- * shtppd_listen Setup a listening socket in the SHTTPD context
+ * shttpd_init Initialize shttpd context
+ * shttpd_fini Dealocate the context, close all connections
+ * shttpd_set_option Set new value for option
+ * shttpd_register_uri Setup the callback function for specified URL
* shttpd_poll Do connections processing
* shttpd_version return string with SHTTPD version
* shttpd_get_var Fetch POST/GET variable value by name. Return value len
* shttpd_get_header return value of the specified HTTP header
- * shttpd_get_env return string values for the following
- * pseudo-variables: "REQUEST_METHOD", "REQUEST_URI",
- * "REMOTE_USER" and "REMOTE_ADDR".
+ * shttpd_get_env return values for the following pseudo-variables:
+ "REQUEST_METHOD", "REQUEST_URI",
+ * "REMOTE_USER" and "REMOTE_ADDR"
+ * shttpd_printf helper function to output data
+ * shttpd_handle_error register custom HTTP error handler
+ * shttpd_wakeup clear SHTTPD_SUSPEND state for the connection
*/
typedef int (*basic_auth_callback)(char *user, char *passwd);
struct shttpd_ctx;
-struct shttpd_ctx *shttpd_init(const char *config_file, ...);
-struct shttpd_ctx *shttpd_init2(const char *config_file,
- char *names[], char *values[], size_t num_options);
+struct shttpd_ctx *shttpd_init(int argc, char *argv[]);
+int shttpd_set_option(struct shttpd_ctx *, const char *opt, const char *val);
void shttpd_fini(struct shttpd_ctx *);
-void shttpd_add_mime_type(struct shttpd_ctx *,
- const char *extension, const char *mime_type);
-int shttpd_listen(struct shttpd_ctx *ctx, int port, int is_ssl);
void shttpd_register_uri(struct shttpd_ctx *ctx, const char *uri,
shttpd_callback_t callback, void *const user_data);
void shttpd_protect_uri(struct shttpd_ctx *ctx,
- const char *uri, const char *password_file, basic_auth_callback cb, int type);
+ const char *uri, const char *password_file, basic_auth_callback cb, int type);
void shttpd_poll(struct shttpd_ctx *, int milliseconds);
const char *shttpd_version(void);
int shttpd_get_var(const char *var, const char *buf, int buf_len,
@@ -115,19 +103,9 @@
shttpd_callback_t func, void *const data);
void shttpd_register_ssi_func(struct shttpd_ctx *ctx, const char *name,
shttpd_callback_t func, void *const user_data);
-
-/*
- * The following three functions are for applications that need to
- * load-balance the connections on their own. Many threads may be spawned
- * with one SHTTPD context per thread. Boss thread may only wait for
- * new connections by means of shttpd_accept(). Then it may scan thread
- * pool for the idle thread by means of shttpd_active(), and add new
- * connection to the context by means of shttpd_add().
- */
-void shttpd_add_socket(struct shttpd_ctx *, int sock, int is_ssl);
-int shttpd_accept(int lsn_sock, int milliseconds);
-int shttpd_active(struct shttpd_ctx *);
-
+void shttpd_wakeup(const void *priv);
+int shttpd_join(struct shttpd_ctx *, fd_set *, fd_set *, int *max_fd);
+int shttpd_socketpair(int sp[2]);
#ifdef __cplusplus
}
diff --git a/src/server/shttpd/shttpd_defs.h b/src/server/shttpd/shttpd_defs.h
deleted file mode 100644
index edcc5fd..0000000
--- a/src/server/shttpd/shttpd_defs.h
+++ /dev/null
@@ -1,496 +0,0 @@
-/*
- * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
- * All rights reserved
- *
- * "THE BEER-WARE LICENSE" (Revision 42):
- * Sergey Lyubka wrote this file. As long as you retain this notice you
- * can do whatever you want with this stuff. If we meet some day, and you think
- * this stuff is worth it, you can buy me a beer in return.
- */
-
-#ifndef DEFS_HEADER_DEFINED
-#define DEFS_HEADER_DEFINED
-
-#include "wsman_config.h"
-
-#ifdef SHTTPD_GSS
-#include <gssapi/gssapi_generic.h>
-#endif
-
-#include "std_includes.h"
-#include "llist.h"
-#include "io.h"
-#include "shttpd.h"
-#include "adapter.h"
-#include "md5.h"
-#include "u/libu.h"
-#include <dlfcn.h>
-
-#undef VERSION
-#define VERSION "1.39" /* Version */
-
-#ifndef CONFIG
-#define CONFIG "shttpd.conf" /* Configuration file */
-#endif /* CONFIG */
-
-#define HTPASSWD ".htpasswd" /* Passwords file name */
-#define DFLT_IO_SIZ "16384" /* Default max request size */
-#define LISTENING_PORTS "80" /* Default listening ports */
-#define INDEX_FILES "index.html index.htm index.php index.cgi"
-#define CGI_EXT ".cgi .pl .php" /* Default CGI extensions */
-#define SSI_EXT ".shtml .shtm" /* Default SSI extensions */
-#define REALM "mydomain.com" /* Default authentication realm */
-#define DELIM_CHARS " ," /* Separators for lists */
-
-#define EXPIRE_TIME 3600 /* Expiration time, seconds */
-#define ENV_MAX 4096 /* Size of environment block */
-#define CGI_ENV_VARS 64 /* Maximum vars passed to CGI */
-#define URI_MAX 32768 /* Maximum URI size */
-#define MIN_REQ_LEN 16 /* "GET / HTTP/1.1\n\n" */
-
-#define NELEMS(ar) (sizeof(ar) / sizeof(ar[0]))
-
-#define GLOBAL_DEBUG
-#ifdef GLOBAL_DEBUG
-#ifdef _DEBUG
-#define DBG(x) do { printf x ; putchar('\n'); fflush(stdout); } while (0)
-#else
-#define DBG(x) do { debug x; } while(0)
-#endif /* DEBUG */
-#else
-#define DBG(x)
-#endif
-
-#ifdef EMBEDDED
-#include "shttpd.h"
-#endif /* EMBEDDED */
-
-/*
- * Darwin prior to 7.0 and Win32 do not have socklen_t
- */
-#ifdef NO_SOCKLEN_T
-typedef int socklen_t;
-#endif /* NO_SOCKLEN_T */
-
-/*
- * For parsing. This guy represents a substring.
- */
-struct vec {
- const char *ptr;
- int len;
-};
-
-enum {BASIC_AUTH, DIGEST_AUTH};
-enum {METHOD_GET, METHOD_POST, METHOD_PUT, METHOD_DELETE, METHOD_HEAD};
-enum {HDR_DATE, HDR_INT, HDR_STRING}; /* HTTP header types */
-enum {E_FATAL = 1, E_LOG = 2}; /* Flags for elog() function */
-typedef unsigned long big_int_t; /* Type for Content-Length */
-
-/*
- * Unified socket address
- */
-struct usa {
- socklen_t len;
- union {
- struct sockaddr sa;
-#ifdef ENABLE_IPV6
- struct sockaddr_in6 sin6;
-#endif
- struct sockaddr_in sin;
- } u;
-};
-
-/*
- * This thing is aimed to hold values of any type.
- * Used to store parsed headers' values.
- */
-union variant {
- char *v_str;
- int v_int;
- big_int_t v_big_int;
- time_t v_time;
- void (*v_func)(void);
- void *v_void;
- struct vec v_vec;
-};
-
-/*
- * This structure is used to hold mime types and associated file extensions.
- */
-struct mime_type {
- const char *ext;
- int ext_len;
- const char *mime;
-};
-
-struct mime_type_link {
- struct llhead link;
- char *ext;
- int ext_len;
- char *mime;
-};
-
-/*
- * This is used only in embedded configuration. This structure holds a
- * registered URI, associated callback function with callback data.
- * For non-embedded compilation shttpd_callback_t is not defined, so
- * we use union variant to keep the compiler silent.
- */
-struct registered_uri {
- struct llhead link;
- const char *uri;
- union variant callback;
- void *callback_data;
-};
-
-/*
- * User may bind a passwords file to any URI. This makes that URI password
- * protected: anybody who accesses that URI will be asked to authorize.
- */
-struct uri_auth {
- struct llhead link;
- const char *uri;
- const char *file_name;
- int type;
- size_t uri_len;
- union variant callback;
- void *callback_data;
-};
-
-/*
- * User may want to handle certain errors. This structure holds the
- * handlers for corresponding error codes.
- */
-struct error_handler {
- struct llhead link;
- int code;
- union variant callback;
- void *callback_data;
-};
-
-struct http_header {
- int len; /* Header name length */
- int type; /* Header type */
- size_t offset; /* Value placeholder */
- const char *name; /* Header name */
-};
-
-/*
- * This guy holds parsed HTTP headers
- */
-struct headers {
- union variant cl; /* Content-Length: */
- union variant ct; /* Content-Type: */
- union variant connection; /* Connection: */
- union variant ims; /* If-Modified-Since: */
- union variant user; /* Remote user name */
- union variant auth; /* Authorization */
- union variant useragent; /* User-Agent: */
- union variant referer; /* Referer: */
- union variant cookie; /* Cookie: */
- union variant location; /* Location: */
- union variant range; /* Range: */
- union variant status; /* Status: */
- union variant transenc; /* Transfer-Encoding: */
-};
-
-/* Must go after union variant definition */
-#include "ssl.h"
-#include <openssl/err.h>
-
-/*
- * The communication channel
- */
-union channel {
- int fd; /* Regular static file */
- int sock; /* Connected socket */
- struct {
- int sock; /* XXX important. must be first */
- SSL *ssl; /* shttpd_poll() assumes that */
- } ssl; /* SSL-ed socket */
- struct {
- DIR *dirp;
- char *path;
- } dir; /* Opened directory */
- struct {
- void *state; /* For keeping state */
- union variant func; /* User callback function */
- void *data; /* User defined parameters */
- } emb; /* Embedded, user callback */
-};
-
-struct stream;
-
-/*
- * IO class descriptor (file, directory, socket, SSL, CGI, etc)
- * These classes are defined in io_*.c files.
- */
-struct io_class {
- const char *name;
- int (*read)(struct stream *, void *buf, size_t len);
- int (*write)(struct stream *, const void *buf, size_t len);
- void (*close)(struct stream *);
-};
-
-/*
- * Data exchange stream. It is backed by some communication channel:
- * opened file, socket, etc. The 'read' and 'write' methods are
- * determined by a communication channel.
- */
-struct stream {
- struct conn *conn;
- union channel chan; /* Descriptor */
- struct io io; /* IO buffer */
- const struct io_class *io_class; /* IO class */
- int nread_last; /* Bytes last read */
- int headers_len;
- big_int_t content_len;
- unsigned int flags;
-#define FLAG_HEADERS_PARSED 1
-#define FLAG_SSL_ACCEPTED 2
-#define FLAG_R 4 /* Can read in general */
-#define FLAG_W 8 /* Can write in general */
-#define FLAG_CLOSED 16
-#define FLAG_DONT_CLOSE 32
-#define FLAG_ALWAYS_READY 64 /* File, dir, user_func */
-#define FLAG_SSL_SHOULD_SELECT_ON_WRITE 128 /* ssl should select on write next time */
-#define FLAG_SSL_SHOULD_SELECT_ON_READ 256 /* ssl should select on read next time */
-#define FLAG_RESPONSE_COMPLETE 512
-};
-
-struct conn {
- struct llhead link; /* Connections chain */
- struct shttpd_ctx *ctx; /* Context this conn belongs to */
- struct usa sa; /* Remote socket address */
- time_t birth_time; /* Creation time */
- time_t expire_time; /* Expiration time */
-
- int loc_port; /* Local port */
- int status; /* Reply status code */
- int method; /* Request method */
- char *uri; /* Decoded URI */
- unsigned long major_version; /* Major HTTP version number */
- unsigned long minor_version; /* Minor HTTP version number */
- char *request; /* Request line */
- char *headers; /* Request headers */
- char *query; /* QUERY_STRING part of the URI */
- char *path_info; /* PATH_INFO thing */
- const char *mime_type; /* Mime type */
-
- struct headers ch; /* Parsed client headers */
-
- struct stream loc; /* Local stream */
- struct stream rem; /* Remote stream */
-#ifdef SHTTPD_GSS
- gss_ctx_id_t gss_ctx; /* GSS context */
-#endif
-
-#if !defined(NO_SSI)
- void *ssi; /* SSI descriptor */
-#endif /* NO_SSI */
-};
-
-
-/*
- * SHTTPD context
- */
-struct shttpd_ctx {
- time_t start_time; /* Start time */
- int nactive; /* # of connections now */
- unsigned long nrequests; /* Requests made */
- uint64_t in, out; /* IN/OUT traffic counters */
- SSL_CTX *ssl_ctx; /* SSL context */
- struct llhead connections; /* List of connections */
-
- struct llhead mime_types; /* Known mime types */
- struct llhead registered_uris;/* User urls */
- struct llhead uri_auths; /* User auth files */
- struct llhead error_handlers; /* Embedded error handlers */
- struct llhead acl; /* Access control list */
-
- FILE *access_log; /* Access log stream */
- FILE *error_log; /* Error log stream */
- char *put_auth_file; /* PUT auth file */
- char *document_root; /* Document root */
- char *index_files; /* Index files */
- char *aliases; /* Aliases */
- char *mime_file; /* Mime types file */
-#if !defined(NO_CGI)
- char *cgi_vars; /* CGI environment variables */
- char *cgi_extensions; /* CGI extensions */
- char *cgi_interpreter; /* CGI script interpreter */
-#endif /* NO_CGI */
-#if !defined(NO_SSI)
- char *ssi_extensions; /* SSI file extensions */
- struct llhead ssi_funcs; /* SSI callback functions */
-#endif /* NO_SSI */
- char *auth_realm; /* Auth realm */
- char *global_passwd_file; /* Global passwords file */
- char *uid; /* Run as user */
- char *ports; /* Listening ports */
- int dirlist; /* Directory listing */
- int gui; /* Show GUI flag */
- int auto_start; /* Start on OS boot */
- int io_buf_size; /* IO buffer size */
- int inetd_mode; /* Inetd flag */
-#if defined(_WIN32)
- CRITICAL_SECTION mutex; /* For MT case */
- HANDLE ev[2]; /* For thread synchronization */
-#elif defined(__rtems__)
- rtems_id mutex;
-#endif /* _WIN32 */
-};
-
-/* Option setter function */
-typedef void (*optset_t)(struct shttpd_ctx *, void *ptr, const char *string);
-struct opt {
- int sw; /* Command line switch */
- const char *name; /* Option name in config file */
- const char *desc; /* Description */
- optset_t setter; /* Option setter function */
- size_t ofs; /* Value offset in context */
- const char *arg; /* Argument format */
- const char *def; /* Default option value */
- unsigned int flags; /* Flags */
-#define OPT_BOOL 1
-#define OPT_INT 2
-#define OPT_FILE 4
-#define OPT_DIR 8
-#define OPT_ADVANCED 16
-};
-
-extern const struct opt options[];
-
-/*
- * In SHTTPD, list of values are represented as comma or space separated
- * string. For example, list of CGI extensions can be represented as
- * ".cgi,.php,.pl", or ".cgi .php .pl". The macro that follows allows to
- * loop through the individual values in that list.
- *
- * A "const char *" pointer and size_t variable must be passed to the macro.
- * Spaces or commas can be used as delimiters (macro DELIM_CHARS).
- *
- * In every iteration of the loop, "s" points to the current value, and
- * "len" specifies its length. The code inside loop must not change
- * "s" and "len" parameters.
- */
-#define FOR_EACH_WORD_IN_LIST(s,len) \
- for (; s != NULL && (len = strcspn(s, DELIM_CHARS)) != 0; \
- s += len, s+= strspn(s, DELIM_CHARS))
-
-/*
- * IPv4 ACL entry. Specifies subnet with deny/allow flag
- */
-struct acl {
- struct llhead link;
- uint32_t ip; /* IP, in network byte order */
- uint32_t mask; /* Also in network byte order */
- int flag; /* Either '+' or '-' */
-};
-
-/*
- * shttpd.c
- */
-extern time_t current_time; /* Current UTC time */
-extern int tz_offset; /* Offset from GMT time zone */
-extern const struct vec known_http_methods[];
-
-extern void stop_stream(struct stream *stream);
-extern int url_decode(const char *, int, char *dst, int);
-extern void send_server_error(struct conn *, int code, const char *reason);
-extern int get_headers_len(const char *buf, size_t buflen);
-extern void parse_headers(const char *s, int len, struct headers *parsed);
-extern void open_listening_ports(struct shttpd_ctx *ctx);
-
-/*
- * mime_type.c
- */
-extern const char *get_mime_type(struct shttpd_ctx *, const char *uri, int len);
-extern void set_mime_types(struct shttpd_ctx *ctx, const char *path);
-
-/*
- * config.c
- */
-extern void usage(const char *prog);
-extern struct shttpd_ctx *init_from_argc_argv(const char *, int, char *[]);
-
-
-
-/*
- * log.c
- */
-// extern void elog(int flags, struct conn *c, const char *fmt, ...);
-#define elog(level, conn, ...) \
- do { \
- debug( __VA_ARGS__ ); \
- if (level == E_FATAL) exit(EXIT_FAILURE); \
- } while (0)
-
-// extern void log_access(FILE *fp, const struct conn *c);
-
-/*
- * string.c
- */
-extern void my_strlcpy(register char *, register const char *, size_t);
-/*
-extern int my_strncasecmp(register const char *, register const char *, size_t);
-extern char *my_strndup(const char *ptr, size_t len);
-extern char *my_strdup(const char *str);
-extern int my_snprintf(char *buf, size_t buflen, const char *fmt, ...);
-*/
-extern int match_extension(const char *path, const char *ext_list);
-
-/*
- * compat_*.c
- */
-extern void set_close_on_exec(int fd);
-extern int set_non_blocking_mode(int fd);
-extern int my_stat(const char *, struct stat *stp);
-extern int my_open(const char *, int flags, int mode);
-extern int my_remove(const char *);
-extern int my_rename(const char *, const char *);
-extern int my_mkdir(const char *, int);
-extern char * my_getcwd(char *, int);
-extern int spawn_process(struct conn *c, const char *prog,
- char *envblk, char *envp[], int sock, const char *dir);
-
-/*
- * io_*.c
- */
-extern const struct io_class io_file;
-extern const struct io_class io_socket;
-extern const struct io_class io_ssl;
-extern const struct io_class io_cgi;
-extern const struct io_class io_dir;
-extern const struct io_class io_embedded;
-extern const struct io_class io_ssi;
-
-extern int put_dir(const char *path);
-extern void get_dir(struct conn *c);
-extern void get_file(struct conn *c, struct stat *stp);
-extern void ssl_handshake(struct stream *stream);
-extern void setup_embedded_stream(struct conn *, union variant, void *);
-extern struct registered_uri *is_registered_uri(struct shttpd_ctx *,
- const char *uri);
-extern void do_ssi(struct conn *);
-extern void ssi_func_destructor(struct llhead *lp);
-
-/*
- * auth.c
- */
-extern int check_authorization(struct conn *c, const char *path);
-extern int is_authorized_for_put(struct conn *c);
-extern int send_authorization_request(struct conn *c);
-extern int edit_passwords(const char *fname, const char *domain,
- const char *user, const char *pass);
-
-/*
- * cgi.c
- */
-extern int run_cgi(struct conn *c, const char *prog);
-extern void do_cgi(struct conn *c);
-
-#define CGI_REPLY "HTTP/1.1 OK\r\n"
-#define CGI_REPLY_LEN (sizeof(CGI_REPLY) - 1)
-
-#endif /* DEFS_HEADER_DEFINED */
diff --git a/src/server/shttpd/ssl.h b/src/server/shttpd/ssl.h
index d045b6e..a863f2c 100644
--- a/src/server/shttpd/ssl.h
+++ b/src/server/shttpd/ssl.h
@@ -10,7 +10,7 @@
#ifdef HAVE_OPENSSL
-# include <openssl/ssl.h>
+#include <openssl/ssl.h>
#else
@@ -26,8 +26,8 @@
#define SSL_ERROR_WANT_READ 2
#define SSL_ERROR_WANT_WRITE 3
-#define SSL_ERROR_SYSCALL 5
-#define SSL_FILETYPE_PEM 1
+#define SSL_ERROR_SYSCALL 5
+#define SSL_FILETYPE_PEM 1
#endif
@@ -59,9 +59,3 @@
const char *, int)) FUNC(11))((x), (y), (z))
#define SSL_CTX_use_certificate_file(x,y,z) (* (int (*)(SSL_CTX *, \
const char *, int)) FUNC(12))((x), (y), (z))
-#define SSL_CTX_use_certificate_chain_file(x,y) (* (int (*)(SSL_CTX *, \
- const char *)) FUNC(15))((x), (y))
-#define SSL_CTX_free(x) (*(void (*)(SSL_CTX *)) FUNC(13))(x)
-#define SSL_pending(x) (*(int (*)(SSL *)) FUNC(14))(x)
-#define SSL_CTX_ctrl(w,x,y,z) (*(long (*)(SSL_CTX *,int,long,void *)) FUNC(16))((w),(x),(y),(z))
-
diff --git a/src/server/shttpd/standalone.c b/src/server/shttpd/standalone.c
new file mode 100644
index 0000000..db920b2
--- /dev/null
+++ b/src/server/shttpd/standalone.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#include "defs.h"
+
+static int exit_flag; /* Program termination flag */
+
+static void
+signal_handler(int sig_num)
+{
+ switch (sig_num) {
+#ifndef _WIN32
+ case SIGCHLD:
+ while (waitpid(-1, &sig_num, WNOHANG) > 0) ;
+ break;
+#endif /* !_WIN32 */
+ default:
+ exit_flag = sig_num;
+ break;
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct shttpd_ctx *ctx;
+
+#if !defined(NO_AUTH)
+ if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'A') {
+ if (argc != 6)
+ _shttpd_usage(argv[0]);
+ exit(_shttpd_edit_passwords(argv[2],argv[3],argv[4],argv[5]));
+ }
+#endif /* NO_AUTH */
+
+ if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")))
+ _shttpd_usage(argv[0]);
+
+#if defined(_WIN32)
+ try_to_run_as_nt_service();
+#endif /* _WIN32 */
+
+#ifndef _WIN32
+ (void) signal(SIGCHLD, signal_handler);
+ (void) signal(SIGPIPE, SIG_IGN);
+#endif /* _WIN32 */
+
+ (void) signal(SIGTERM, signal_handler);
+ (void) signal(SIGINT, signal_handler);
+
+ if ((ctx = shttpd_init(argc, argv)) == NULL)
+ _shttpd_elog(E_FATAL, NULL, "%s",
+ "Cannot initialize SHTTPD context");
+
+ _shttpd_elog(E_LOG, NULL, "shttpd %s started on port(s) %s, serving %s",
+ SHTTPD_VERSION, ctx->options[OPT_PORTS], ctx->options[OPT_ROOT]);
+
+ while (exit_flag == 0)
+ shttpd_poll(ctx, 10 * 1000);
+
+ _shttpd_elog(E_LOG, NULL, "Exit on signal %d", exit_flag);
+ shttpd_fini(ctx);
+
+ return (EXIT_SUCCESS);
+}
diff --git a/src/server/shttpd/std_includes.h b/src/server/shttpd/std_includes.h
index f61503c..4bf1ea7 100644
--- a/src/server/shttpd/std_includes.h
+++ b/src/server/shttpd/std_includes.h
@@ -28,7 +28,6 @@
#include <limits.h>
#include <stddef.h>
#include <stdio.h>
-#include <wchar.h>
#if defined(_WIN32) /* Windows specific */
#include "compat_win32.h"
diff --git a/src/server/shttpd/string.c b/src/server/shttpd/string.c
index 0f84ce3..7e09e42 100644
--- a/src/server/shttpd/string.c
+++ b/src/server/shttpd/string.c
@@ -8,30 +8,88 @@
* this stuff is worth it, you can buy me a beer in return.
*/
-#include "shttpd_defs.h"
+#include "defs.h"
void
-my_strlcpy(register char *dst, register const char *src, size_t n)
+_shttpd_strlcpy(register char *dst, register const char *src, size_t n)
{
- for (; *src != '\0' && n > 1; n--)
- *dst++ = *src++;
- *dst = '\0';
+ for (; *src != '\0' && n > 1; n--)
+ *dst++ = *src++;
+ *dst = '\0';
+}
+
+int
+_shttpd_strncasecmp(const char *str1, const char *str2, size_t len)
+{
+ register const unsigned char *s1 = (unsigned char *) str1,
+ *s2 = (unsigned char *) str2, *e;
+ int ret;
+
+ for (e = s1 + len - 1; s1 < e && *s1 != '\0' && *s2 != '\0' &&
+ tolower(*s1) == tolower(*s2); s1++, s2++) ;
+ ret = tolower(*s1) - tolower(*s2);
+
+ return (ret);
+}
+
+char *
+_shttpd_strndup(const char *ptr, size_t len)
+{
+ char *p;
+
+ if ((p = malloc(len + 1)) != NULL)
+ _shttpd_strlcpy(p, ptr, len + 1);
+
+ return (p);
+
+}
+
+char *
+_shttpd_strdup(const char *str)
+{
+ return (_shttpd_strndup(str, strlen(str)));
+}
+
+/*
+ * Sane snprintf(). Acts like snprintf(), but never return -1 or the
+ * value bigger than supplied buffer.
+ * Thanks Adam Zeldis to pointing snprintf()-caused vulnerability
+ * in his audit report.
+ */
+int
+_shttpd_snprintf(char *buf, size_t buflen, const char *fmt, ...)
+{
+ va_list ap;
+ int n;
+
+ if (buflen == 0)
+ return (0);
+
+ va_start(ap, fmt);
+ n = vsnprintf(buf, buflen, fmt, ap);
+ va_end(ap);
+
+ if (n < 0 || (size_t) n >= buflen)
+ n = buflen - 1;
+ buf[n] = '\0';
+
+ return (n);
}
/*
* Verify that given file has certain extension
*/
int
-match_extension(const char *path, const char *ext_list)
+_shttpd_match_extension(const char *path, const char *ext_list)
{
size_t len, path_len;
path_len = strlen(path);
FOR_EACH_WORD_IN_LIST(ext_list, len)
- if (len < path_len &&
- !strncasecmp(path + path_len - len, ext_list, len))
- return (1);
+ if (len < path_len && path[path_len - len - 1] == '.' &&
+ !_shttpd_strncasecmp(path + path_len - len, ext_list, len))
+ return (TRUE);
- return (0);
+ return (FALSE);
}
diff --git a/src/server/wsmand-listener.c b/src/server/wsmand-listener.c
index a1cfe1f..2d20550 100644
--- a/src/server/wsmand-listener.c
+++ b/src/server/wsmand-listener.c
@@ -65,7 +65,6 @@
#include "shttpd.h"
-#include "adapter.h"
#include "wsman-plugins.h"
#include "wsmand-listener.h"
@@ -343,7 +342,8 @@
DONE:
if (fault_reason == NULL) {
- fault_reason = shttpd_reason_phrase(status);
+ // this is a way to segfault, investigate
+ //fault_reason = shttpd_reason_phrase(status);
}
debug("Response status=%d (%s)", status, fault_reason);
@@ -433,28 +433,29 @@
}
}
-static struct shttpd_ctx *create_shttpd_context(SoapH soap)
+static struct shttpd_ctx *create_shttpd_context(SoapH soap, int port)
{
struct shttpd_ctx *ctx;
- if (wsmand_options_get_use_ssl()) {
- message("ssl certificate: %s", wsmand_options_get_ssl_cert_file());
- message("Using SSL");
- ctx = shttpd_init(NULL,
- "ssl_certificate",
- wsmand_options_get_ssl_cert_file(),
- "auth_realm",
- AUTHENTICATION_REALM,
- NULL);
- } else {
- ctx = shttpd_init(NULL,
- "auth_realm", AUTHENTICATION_REALM,
- NULL);
- }
+ char *tmps;
+ int len;
+
+ ctx = shttpd_init(0, NULL);
if (ctx == NULL) {
return NULL;
}
+ if (wsmand_options_get_use_ssl()) {
+ message("ssl certificate: %s", wsmand_options_get_ssl_cert_file());
+ shttpd_set_option(ctx, "ssl_cert", wsmand_options_get_ssl_cert_file());
+ }
+ len = snprintf(NULL, 0, "%d%s", port, wsmand_options_get_use_ssl() ? "s" : "");
+ tmps = malloc((len+1) * sizeof(char));
+ snprintf(tmps, len+1, "%d%s", port, wsmand_options_get_use_ssl() ? "s" : "");
+ shttpd_set_option(ctx, "ports", tmps);
+ free(tmps);
+ shttpd_set_option(ctx, "auth_realm", AUTHENTICATION_REALM);
shttpd_register_uri(ctx, wsmand_options_get_service_path(),
server_callback, (void *) soap);
+ protect_uri(ctx, wsmand_options_get_service_path());
shttpd_register_uri(ctx, ANON_IDENTIFY_PATH,
server_callback, (void *) soap);
@@ -464,7 +465,6 @@
protect_uri( ctx, DEFAULT_CIMINDICATION_PATH );
#endif
- protect_uri( ctx, wsmand_options_get_service_path());
return ctx;
}
@@ -608,56 +608,6 @@
}
}
-static void *thread_function(void *param)
-{
- struct thread *thread = param;
-
- for (;;)
- shttpd_poll(thread->ctx, 1000);
-
- return NULL;
-}
-
-
-static struct thread *
-spawn_new_thread(pthread_attr_t pattrs, SoapH soap)
-{
- struct shttpd_ctx *ctx;
- struct thread *thread;
- pthread_t tid;
- debug("spawning new thread");
-
- thread = malloc(sizeof(*thread));
- ctx = create_shttpd_context(soap);
-
- assert(ctx != NULL);
- assert(thread != NULL);
-
- thread->ctx = ctx;
- thread->next = threads;
- threads = thread;
-
- pthread_create(&tid, &pattrs, thread_function, thread);
-
- return (thread);
-}
-
-
-static struct thread *
-find_not_busy_thread(int *num_threads, int max_connections_per_thread)
-{
- struct thread *thread;
-
- for (thread = threads, *num_threads=0; thread != NULL; thread = thread->next) {
- debug("Active sockets: %d, Thread Number: %d", shttpd_active(thread->ctx), *num_threads );
- (*num_threads)++;
- if (shttpd_active(thread->ctx) < max_connections_per_thread)
- return (thread);
- }
-
- return (NULL);
-}
-
WsManListenerH *wsmand_start_server(dictionary * ini)
{
@@ -717,9 +667,7 @@
wsmand_shutdown_add_handler(listener_shutdown_handler,
&continue_working);
- httpd_ctx = create_shttpd_context(soap);
-
- lsn = shttpd_listen(httpd_ctx, port, use_ssl);
+ httpd_ctx = create_shttpd_context(soap, port);
if (wsman_setup_thread(&pattrs) == 0 )
return listener;
@@ -730,24 +678,7 @@
#endif
while (continue_working) {
- if ((sock = shttpd_accept(lsn, 1000)) == -1) {
- continue;
- }
- debug("Sock %d accepted", sock);
- if ((thread = find_not_busy_thread(&num_threads, max_connections_per_thread)) == NULL){
- if(max_threads){
- if(num_threads < max_threads){
- thread = spawn_new_thread(pattrs, soap);
- }
- else{
- continue;
- }
- }
- else{
- thread = spawn_new_thread(pattrs, soap);
- }
- }
- shttpd_add_socket(thread->ctx, sock, use_ssl);
- }
- return listener;
+ shttpd_poll(httpd_ctx, 1000);
+ }
+ return listener;
}