diff --git a/database b/database index 941db6777..519a0e1f9 100644 Binary files a/database and b/database differ diff --git a/resources/database/vscp-hashtypeevents.sqlite3 b/resources/database/vscp-hashtypeevents.sqlite3 index 941db6777..519a0e1f9 100644 Binary files a/resources/database/vscp-hashtypeevents.sqlite3 and b/resources/database/vscp-hashtypeevents.sqlite3 differ diff --git a/src/common/sockettcp.c b/src/common/sockettcp.c index ea66e13f4..40947582f 100644 --- a/src/common/sockettcp.c +++ b/src/common/sockettcp.c @@ -1,27 +1,27 @@ /////////////////////////////////////////////////////////////////////////////// // sockettcp.h: // -// This file is part of the VSCP (https://www.vscp.org) +// This file is part of the VSCP (https://www.vscp.org) // // The MIT License (MIT) -// +// // Copyright (c) 2004-2013 Sergey Lyubka // Copyright (c) 2013-2017 the Civetweb developers () // // Adopted for VSCP, Small changes additions // Copyright (C) 2018-2023 Ake Hedman, the VSCP project // -// +// // 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 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 @@ -57,14 +57,14 @@ #define __STDC_LIMIT_MACROS /* C++ wants that for INT64_MAX */ #endif #ifdef __sun -#define __EXTENSIONS__ /* to expose flockfile and friends in stdio.h */ -#define __inline inline /* not recognized on older compiler versions */ +#define __EXTENSIONS__ /* to expose flockfile and friends in stdio.h */ +#define __inline inline /* not recognized on older compiler versions */ #endif #endif #if defined(__GNUC__) || defined(__MINGW32__) /* Disable unused macros warnings - not all defines are required -* for all systems and all compilers. */ + * for all systems and all compilers. */ #pragma GCC diagnostic ignored "-Wunused-macros" /* A padding warning is just plain useless */ #pragma GCC diagnostic ignored "-Wpadded" @@ -72,8 +72,8 @@ #if defined(__clang__) /* GCC does not (yet) support this pragma */ /* We must set some flags for the headers we include. These flags -* are reserved ids according to C99, so we need to disable a -* warning for that. */ + * are reserved ids according to C99, so we need to disable a + * warning for that. */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wreserved-id-macro" #endif @@ -109,8 +109,8 @@ #define _DARWIN_UNLIMITED_SELECT #endif #if defined(__sun) -#define __EXTENSIONS__ /* to expose flockfile and friends in stdio.h */ -#define __inline inline /* not recognized on older compiler versions */ +#define __EXTENSIONS__ /* to expose flockfile and friends in stdio.h */ +#define __inline inline /* not recognized on older compiler versions */ #endif #endif @@ -138,12 +138,12 @@ #include "vscpmd5.h" -//#define UNUSED(expr) (do { (void)(expr); } while (0)) -#define SUPPRESS_WARNING(a) (void)a +// #define UNUSED(expr) (do { (void)(expr); } while (0)) +#define SUPPRESS_WARNING(a) (void) a /* Flags for SSL usage */ -#define NO_SSL 0 -#define USE_SSL 1 +#define NO_SSL 0 +#define USE_SSL 1 #if defined(_WIN32) @@ -173,38 +173,36 @@ typedef const char *SOCK_OPT_TYPE; #include #include -#define MAKEUQUAD(lo, hi) \ - ((uint64_t)(((uint32_t)(lo)) | ((uint64_t)((uint32_t)(hi))) << 32)) -#define RATE_DIFF (10000000) /* 100 nsecs */ -#define EPOCH_DIFF (MAKEUQUAD(0xd53e8000, 0x019db1de)) -#define SYS2UNIX_TIME(lo, hi) \ - ((time_t)((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF)) +#define MAKEUQUAD(lo, hi) ((uint64_t) (((uint32_t) (lo)) | ((uint64_t) ((uint32_t) (hi))) << 32)) +#define RATE_DIFF (10000000) /* 100 nsecs */ +#define EPOCH_DIFF (MAKEUQUAD(0xd53e8000, 0x019db1de)) +#define SYS2UNIX_TIME(lo, hi) ((time_t) ((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF)) /* Visual Studio 6 does not know __func__ or __FUNCTION__ -* The rest of MS compilers use __FUNCTION__, not C99 __func__ -* Also use _strtoui64 on modern M$ compilers */ + * The rest of MS compilers use __FUNCTION__, not C99 __func__ + * Also use _strtoui64 on modern M$ compilers */ #if defined(_MSC_VER) #if (_MSC_VER < 1300) -#define STRX(x) #x -#define STR(x) STRX(x) -#define __func__ __FILE__ ":" STR(__LINE__) -#define strtoull(x, y, z) ((unsigned __int64)_atoi64(x)) -#define strtoll(x, y, z) (_atoi64(x)) +#define STRX(x) #x +#define STR(x) STRX(x) +#define __func__ __FILE__ ":" STR(__LINE__) +#define strtoull(x, y, z) ((unsigned __int64) _atoi64(x)) +#define strtoll(x, y, z) (_atoi64(x)) #else -#define __func__ __FUNCTION__ +#define __func__ __FUNCTION__ #define strtoull(x, y, z) (_strtoui64(x, y, z)) -#define strtoll(x, y, z) (_strtoi64(x, y, z)) +#define strtoll(x, y, z) (_strtoi64(x, y, z)) #endif #endif /* _MSC_VER */ -#define ERRNO ((int)(GetLastError())) +#define ERRNO ((int) (GetLastError())) #define NO_SOCKLEN_T #if defined(_WIN64) || defined(__MINGW64__) -#define SSL_LIB "ssleay64.dll" +#define SSL_LIB "ssleay64.dll" #define CRYPTO_LIB "libeay64.dll" #else -#define SSL_LIB "ssleay32.dll" +#define SSL_LIB "ssleay32.dll" #define CRYPTO_LIB "libeay32.dll" #endif @@ -216,13 +214,13 @@ typedef const char *SOCK_OPT_TYPE; #define EWOULDBLOCK WSAEWOULDBLOCK #endif /* !EWOULDBLOCK */ #define _POSIX_ -#define INT64_FMT "I64d" +#define INT64_FMT "I64d" #define UINT64_FMT "I64u" -#define WINCDECL __cdecl +#define WINCDECL __cdecl #define vsnprintf_impl _vsnprintf -#define access _access -#define mg_sleep(x) (Sleep(x)) +#define access _access +#define mg_sleep(x) (Sleep(x)) #define pipe(x) _pipe(x, MG_BUF_LEN, _O_BINARY) #ifndef popen @@ -231,18 +229,18 @@ typedef const char *SOCK_OPT_TYPE; #ifndef pclose #define pclose(x) (_pclose(x)) #endif -#define close(x) (_close(x)) -#define dlsym(x, y) (GetProcAddress((HINSTANCE)(x), (y))) -#define RTLD_LAZY (0) +#define close(x) (_close(x)) +#define dlsym(x, y) (GetProcAddress((HINSTANCE) (x), (y))) +#define RTLD_LAZY (0) #define fseeko(x, y, z) ((_lseeki64(_fileno(x), (y), (z)) == -1) ? -1 : 0) -#define fdopen(x, y) (_fdopen((x), (y))) -#define write(x, y, z) (_write((x), (y), (unsigned)z)) -#define read(x, y, z) (_read((x), (y), (unsigned)z)) -#define flockfile(x) (EnterCriticalSection(&global_log_file_lock)) -#define funlockfile(x) (LeaveCriticalSection(&global_log_file_lock)) -#define sleep(x) (Sleep((x)*1000)) -#define rmdir(x) (_rmdir(x)) -#define timegm(x) (_mkgmtime(x)) +#define fdopen(x, y) (_fdopen((x), (y))) +#define write(x, y, z) (_write((x), (y), (unsigned) z)) +#define read(x, y, z) (_read((x), (y), (unsigned) z)) +#define flockfile(x) (EnterCriticalSection(&global_log_file_lock)) +#define funlockfile(x) (LeaveCriticalSection(&global_log_file_lock)) +#define sleep(x) (Sleep((x) * 1000)) +#define rmdir(x) (_rmdir(x)) +#define timegm(x) (_mkgmtime(x)) #define NEED_TIMEGM #if !defined(fileno) @@ -274,14 +272,13 @@ typedef DWORD clockid_t; #define CLOCK_PROCESS (4) #endif - #if defined(_MSC_VER) && (_MSC_VER >= 1900) #define _TIMESPEC_DEFINED #endif #ifndef _TIMESPEC_DEFINED struct timespec { - time_t tv_sec; /* seconds */ - long tv_nsec; /* nanoseconds */ + time_t tv_sec; /* seconds */ + long tv_nsec; /* nanoseconds */ }; #endif @@ -294,97 +291,85 @@ struct timespec { static int clock_gettime(clockid_t clk_id, struct timespec *tp) { - FILETIME ft; - ULARGE_INTEGER li, li2; - BOOL ok = FALSE; - double d; - static double perfcnt_per_sec = 0.0; - - if (tp) { - memset(tp, 0, sizeof(*tp)); - - if (clk_id == CLOCK_REALTIME) { - - /* BEGIN: CLOCK_REALTIME = wall clock (date and time) */ - GetSystemTimeAsFileTime(&ft); - li.LowPart = ft.dwLowDateTime; - li.HighPart = ft.dwHighDateTime; - li.QuadPart -= 116444736000000000; /* 1.1.1970 in filedate */ - tp->tv_sec = (time_t)(li.QuadPart / 10000000); - tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100; - ok = TRUE; - /* END: CLOCK_REALTIME */ - - } - else if (clk_id == CLOCK_MONOTONIC) { - - /* BEGIN: CLOCK_MONOTONIC = stopwatch (time differences) */ - if (perfcnt_per_sec == 0.0) { - QueryPerformanceFrequency((LARGE_INTEGER *)&li); - perfcnt_per_sec = 1.0 / li.QuadPart; - } - if (perfcnt_per_sec != 0.0) { - QueryPerformanceCounter((LARGE_INTEGER *)&li); - d = li.QuadPart * perfcnt_per_sec; - tp->tv_sec = (time_t)d; - d -= tp->tv_sec; - tp->tv_nsec = (long)(d * 1.0E9); - ok = TRUE; - } - /* END: CLOCK_MONOTONIC */ - - } - else if (clk_id == CLOCK_THREAD) { - - /* BEGIN: CLOCK_THREAD = CPU usage of thread */ - FILETIME t_create, t_exit, t_kernel, t_user; - if (GetThreadTimes(GetCurrentThread(), - &t_create, - &t_exit, - &t_kernel, - &t_user)) { - li.LowPart = t_user.dwLowDateTime; - li.HighPart = t_user.dwHighDateTime; - li2.LowPart = t_kernel.dwLowDateTime; - li2.HighPart = t_kernel.dwHighDateTime; - li.QuadPart += li2.QuadPart; - tp->tv_sec = (time_t)(li.QuadPart / 10000000); - tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100; - ok = TRUE; - } - /* END: CLOCK_THREAD */ - - } - else if (clk_id == CLOCK_PROCESS) { - - /* BEGIN: CLOCK_PROCESS = CPU usage of process */ - FILETIME t_create, t_exit, t_kernel, t_user; - if (GetProcessTimes(GetCurrentProcess(), - &t_create, - &t_exit, - &t_kernel, - &t_user)) { - li.LowPart = t_user.dwLowDateTime; - li.HighPart = t_user.dwHighDateTime; - li2.LowPart = t_kernel.dwLowDateTime; - li2.HighPart = t_kernel.dwHighDateTime; - li.QuadPart += li2.QuadPart; - tp->tv_sec = (time_t)(li.QuadPart / 10000000); - tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100; - ok = TRUE; - } - /* END: CLOCK_PROCESS */ - - } - else { + FILETIME ft; + ULARGE_INTEGER li, li2; + BOOL ok = FALSE; + double d; + static double perfcnt_per_sec = 0.0; + + if (tp) { + memset(tp, 0, sizeof(*tp)); + + if (clk_id == CLOCK_REALTIME) { + + /* BEGIN: CLOCK_REALTIME = wall clock (date and time) */ + GetSystemTimeAsFileTime(&ft); + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + li.QuadPart -= 116444736000000000; /* 1.1.1970 in filedate */ + tp->tv_sec = (time_t) (li.QuadPart / 10000000); + tp->tv_nsec = (long) (li.QuadPart % 10000000) * 100; + ok = TRUE; + /* END: CLOCK_REALTIME */ + } + else if (clk_id == CLOCK_MONOTONIC) { + + /* BEGIN: CLOCK_MONOTONIC = stopwatch (time differences) */ + if (perfcnt_per_sec == 0.0) { + QueryPerformanceFrequency((LARGE_INTEGER *) &li); + perfcnt_per_sec = 1.0 / li.QuadPart; + } + if (perfcnt_per_sec != 0.0) { + QueryPerformanceCounter((LARGE_INTEGER *) &li); + d = li.QuadPart * perfcnt_per_sec; + tp->tv_sec = (time_t) d; + d -= tp->tv_sec; + tp->tv_nsec = (long) (d * 1.0E9); + ok = TRUE; + } + /* END: CLOCK_MONOTONIC */ + } + else if (clk_id == CLOCK_THREAD) { + + /* BEGIN: CLOCK_THREAD = CPU usage of thread */ + FILETIME t_create, t_exit, t_kernel, t_user; + if (GetThreadTimes(GetCurrentThread(), &t_create, &t_exit, &t_kernel, &t_user)) { + li.LowPart = t_user.dwLowDateTime; + li.HighPart = t_user.dwHighDateTime; + li2.LowPart = t_kernel.dwLowDateTime; + li2.HighPart = t_kernel.dwHighDateTime; + li.QuadPart += li2.QuadPart; + tp->tv_sec = (time_t) (li.QuadPart / 10000000); + tp->tv_nsec = (long) (li.QuadPart % 10000000) * 100; + ok = TRUE; + } + /* END: CLOCK_THREAD */ + } + else if (clk_id == CLOCK_PROCESS) { + + /* BEGIN: CLOCK_PROCESS = CPU usage of process */ + FILETIME t_create, t_exit, t_kernel, t_user; + if (GetProcessTimes(GetCurrentProcess(), &t_create, &t_exit, &t_kernel, &t_user)) { + li.LowPart = t_user.dwLowDateTime; + li.HighPart = t_user.dwHighDateTime; + li2.LowPart = t_kernel.dwLowDateTime; + li2.HighPart = t_kernel.dwHighDateTime; + li.QuadPart += li2.QuadPart; + tp->tv_sec = (time_t) (li.QuadPart / 10000000); + tp->tv_nsec = (long) (li.QuadPart % 10000000) * 100; + ok = TRUE; + } + /* END: CLOCK_PROCESS */ + } + else { - /* BEGIN: unknown clock */ - /* ok = FALSE; already set by init */ - /* END: unknown clock */ - } + /* BEGIN: unknown clock */ + /* ok = FALSE; already set by init */ + /* END: unknown clock */ } + } - return ok ? 0 : -1; + return ok ? 0 : -1; } #endif @@ -392,10 +377,8 @@ clock_gettime(clockid_t clk_id, struct timespec *tp) // static int pthread_mutex_lock(pthread_mutex_t *); // static int pthread_mutex_unlock(pthread_mutex_t *); -static void path_to_unicode(const struct mg_connection *conn, - const char *path, - wchar_t *wbuf, - size_t wbuf_len); +static void +path_to_unicode(const struct mg_connection *conn, const char *path, wchar_t *wbuf, size_t wbuf_len); /* All file operations need to be rewritten to solve #246. */ @@ -404,24 +387,23 @@ struct mg_file; static const char * mg_fgets(char *buf, size_t size, struct mg_file *filep, char **p); - /* POSIX dirent interface */ struct dirent { - char d_name[PATH_MAX]; + char d_name[PATH_MAX]; }; typedef struct DIR { - HANDLE handle; - WIN32_FIND_DATAW info; - struct dirent result; + HANDLE handle; + WIN32_FIND_DATAW info; + struct dirent result; } DIR; #if defined(_WIN32) && !defined(POLLIN) #if !defined(HAVE_POLL) struct pollfd { - SOCKET fd; - short events; - short revents; + SOCKET fd; + short events; + short revents; }; #define POLLIN (0x0300) #endif @@ -437,7 +419,7 @@ struct pollfd { #include #include #include -//#include +// #include #include #include #include @@ -471,8 +453,8 @@ typedef const void *SOCK_OPT_TYPE; #endif #define INVALID_SOCKET (-1) -#define INT64_FMT PRId64 -#define UINT64_FMT PRIu64 +#define INT64_FMT PRId64 +#define UINT64_FMT PRIu64 #define WINCDECL #endif /* unix block */ @@ -488,7 +470,7 @@ typedef const void *SOCK_OPT_TYPE; #endif #include -#include +#include #include #include #include @@ -501,28 +483,22 @@ typedef const void *SOCK_OPT_TYPE; #include #include "sockettcp.h" - - -#if defined(_WIN32) +#if defined(_WIN32) #define pid_t HANDLE /* MINGW typedefs pid_t to int. Using #define here. */ -//static int pthread_mutex_lock(pthread_mutex_t *); -//static int pthread_mutex_unlock(pthread_mutex_t *); - - -static void path_to_unicode( const struct mg_connection *conn, - const char *path, - wchar_t *wbuf, - size_t wbuf_len); +// static int pthread_mutex_lock(pthread_mutex_t *); +// static int pthread_mutex_unlock(pthread_mutex_t *); +static void +path_to_unicode(const struct mg_connection *conn, const char *path, wchar_t *wbuf, size_t wbuf_len); #if defined(_WIN32) && !defined(POLLIN) #ifndef HAVE_POLL struct pollfd { - SOCKET fd; - short events; - short revents; + SOCKET fd; + short events; + short revents; }; #define POLLIN (0x0300) #endif @@ -533,7 +509,7 @@ struct pollfd { #pragma comment(lib, "Ws2_32.lib") #endif -#else /* defined(_WIN32) WINDOWS / UNIX include block */ +#else /* defined(_WIN32) WINDOWS / UNIX include block */ #include #include @@ -548,7 +524,6 @@ struct pollfd { #include typedef const void *SOCK_OPT_TYPE; - #include #include #include @@ -560,7 +535,7 @@ typedef const void *SOCK_OPT_TYPE; #endif #include #if defined(__MACH__) -#define SSL_LIB "libssl.dylib" +#define SSL_LIB "libssl.dylib" #define CRYPTO_LIB "libcrypto.dylib" #else #if !defined(SSL_LIB) @@ -573,17 +548,17 @@ typedef const void *SOCK_OPT_TYPE; #ifndef O_BINARY #define O_BINARY (0) #endif /* O_BINARY */ -#define closesocket(a) (close(a)) +#define closesocket(a) (close(a)) #define mg_mkdir(conn, path, mode) (mkdir(path, mode)) -#define mg_remove(conn, x) (remove(x)) -#define mg_sleep(x) (usleep((x)*1000)) -#define mg_opendir(conn, x) (opendir(x)) -#define mg_closedir(x) (closedir(x)) -#define mg_readdir(x) (readdir(x)) -#define ERRNO (errno) -#define INVALID_SOCKET (-1) -#define INT64_FMT PRId64 -#define UINT64_FMT PRIu64 +#define mg_remove(conn, x) (remove(x)) +#define mg_sleep(x) (usleep((x) * 1000)) +#define mg_opendir(conn, x) (opendir(x)) +#define mg_closedir(x) (closedir(x)) +#define mg_readdir(x) (readdir(x)) +#define ERRNO (errno) +#define INVALID_SOCKET (-1) +#define INT64_FMT PRId64 +#define UINT64_FMT PRIu64 typedef int SOCKET; #define WINCDECL @@ -604,7 +579,7 @@ typedef int SOCKET; #define socklen_t int #endif /* hpux */ -#endif /* defined(_WIN32) && !defined(__SYMBIAN32__) - \ +#endif /* defined(_WIN32) && !defined(__SYMBIAN32__) - \ WINDOWS / UNIX include block */ #if defined(_MSC_VER) @@ -636,8 +611,8 @@ typedef int SOCKET; #pragma clang diagnostic ignored "-Wdisabled-macro-expansion" #endif -#define SHUTDOWN_RD (0) -#define SHUTDOWN_WR (1) +#define SHUTDOWN_RD (0) +#define SHUTDOWN_WR (1) #define SHUTDOWN_BOTH (2) #ifndef WIN32 @@ -650,128 +625,127 @@ __attribute__((unused)) static int stcp_ssl_initialized = 0; static pthread_key_t sTlsKey; // Thread local storage index struct stcp_workerTLS { - int is_master; - unsigned long thread_idx; + int is_master; + unsigned long thread_idx; #if defined(_WIN32) - HANDLE pthread_cond_helper_mutex; - struct stcp_workerTLS *next_waiting_thread; + HANDLE pthread_cond_helper_mutex; + struct stcp_workerTLS *next_waiting_thread; #endif }; -#define stcp_sleep(x) (usleep((x)*1000)) - +#define stcp_sleep(x) (usleep((x) * 1000)) //////////////////////////////////////////////////////////////////////////////// // stcp_get_current_time_ns // static uint64_t -stcp_get_current_time_ns( void ) +stcp_get_current_time_ns(void) { - struct timespec tsnow; - clock_gettime(CLOCK_REALTIME, &tsnow); - return ( ( (uint64_t)tsnow.tv_sec ) * 1000000000) + (uint64_t)tsnow.tv_nsec; + struct timespec tsnow; + clock_gettime(CLOCK_REALTIME, &tsnow); + return (((uint64_t) tsnow.tv_sec) * 1000000000) + (uint64_t) tsnow.tv_nsec; } - // ****************** Windows specific ****************** #if defined(_WIN32) - - #if defined(_WIN32) && !defined(POLLIN) #ifndef HAVE_POLL struct pollfd { - SOCKET fd; - short events; - short revents; + SOCKET fd; + short events; + short revents; }; #define POLLIN (0x0300) #endif #endif - -void usleep(__int64 usec) +void +usleep(__int64 usec) { - HANDLE timer; - LARGE_INTEGER ft; + HANDLE timer; + LARGE_INTEGER ft; - ft.QuadPart = -(10 * usec); // Convert to 100 nanosecond interval, negative value indicates relative time + ft.QuadPart = -(10 * usec); // Convert to 100 nanosecond interval, negative value indicates relative time - timer = CreateWaitableTimer(NULL, TRUE, NULL); - SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); - WaitForSingleObject(timer, INFINITE); - CloseHandle(timer); + timer = CreateWaitableTimer(NULL, TRUE, NULL); + SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); + WaitForSingleObject(timer, INFINITE); + CloseHandle(timer); } -static char *__strdup(const char *s) { - size_t size = strlen(s) + 1; - char *p = malloc(size); - if (p != NULL) { - memcpy(p, s, size); - } - return p; +static char * +__strdup(const char *s) +{ + size_t size = strlen(s) + 1; + char *p = malloc(size); + if (p != NULL) { + memcpy(p, s, size); + } + return p; } -static char *__strndup(const char *s, size_t n) { - char *p; - size_t n1; +static char * +__strndup(const char *s, size_t n) +{ + char *p; + size_t n1; - for (n1 = 0; n1 < n && s[n1] != '\0'; n1++) - continue; - p = malloc(n + 1); - if (p != NULL) { - memcpy(p, s, n1); - p[n1] = '\0'; - } - return p; + for (n1 = 0; n1 < n && s[n1] != '\0'; n1++) + continue; + p = malloc(n + 1); + if (p != NULL) { + memcpy(p, s, n1); + p[n1] = '\0'; + } + return p; } #ifndef HAVE_POLL int stcp_poll(struct pollfd *pfd, unsigned int n, int milliseconds, volatile int *stop_server) { - struct timeval tv; - fd_set set; - unsigned int i; - int result; - SOCKET maxfd = 0; + struct timeval tv; + fd_set set; + unsigned int i; + int result; + SOCKET maxfd = 0; - memset(&tv, 0, sizeof(tv)); - tv.tv_sec = milliseconds / 1000; - tv.tv_usec = (milliseconds % 1000) * 1000; - FD_ZERO(&set); + memset(&tv, 0, sizeof(tv)); + tv.tv_sec = milliseconds / 1000; + tv.tv_usec = (milliseconds % 1000) * 1000; + FD_ZERO(&set); - for (i = 0; i < n; i++) { - FD_SET((SOCKET)pfd[i].fd, &set); - pfd[i].revents = 0; + for (i = 0; i < n; i++) { + FD_SET((SOCKET) pfd[i].fd, &set); + pfd[i].revents = 0; - if (pfd[i].fd > maxfd) { - maxfd = pfd[i].fd; - } + if (pfd[i].fd > maxfd) { + maxfd = pfd[i].fd; } + } - if ((result = select((int)maxfd + 1, &set, NULL, NULL, &tv)) > 0) { - for (i = 0; i < n; i++) { - if (FD_ISSET(pfd[i].fd, &set)) { - pfd[i].revents = POLLIN; - } - } + if ((result = select((int) maxfd + 1, &set, NULL, NULL, &tv)) > 0) { + for (i = 0; i < n; i++) { + if (FD_ISSET(pfd[i].fd, &set)) { + pfd[i].revents = POLLIN; + } } + } - /* We should subtract the time used in select from remaining - * "milliseconds", in particular if called from mg_poll with a - * timeout quantum. - * Unfortunately, the remaining time is not stored in "tv" in all - * implementations, so the result in "tv" must be considered undefined. - * See http://man7.org/linux/man-pages/man2/select.2.html */ + /* We should subtract the time used in select from remaining + * "milliseconds", in particular if called from mg_poll with a + * timeout quantum. + * Unfortunately, the remaining time is not stored in "tv" in all + * implementations, so the result in "tv" must be considered undefined. + * See http://man7.org/linux/man-pages/man2/select.2.html */ - return result; + return result; } #endif /* HAVE_POLL */ - //////////////////////////////////////////////////////////////////////////////// // set_blocking_mode // @@ -779,15 +753,15 @@ stcp_poll(struct pollfd *pfd, unsigned int n, int milliseconds, volatile int *st static int set_blocking_mode(int sock) { - unsigned long non_blocking = 0; - return ioctlsocket(sock, (long) FIONBIO, &non_blocking); + unsigned long non_blocking = 0; + return ioctlsocket(sock, (long) FIONBIO, &non_blocking); } static int set_non_blocking_mode(int sock) { - unsigned long non_blocking = 1; - return ioctlsocket(sock, (long)FIONBIO, &non_blocking); + unsigned long non_blocking = 1; + return ioctlsocket(sock, (long) FIONBIO, &non_blocking); } /////////////////////////////////////////////////////////////////////////////// @@ -1026,7 +1000,7 @@ pthread_cond_destroy( pthread_cond_t *cv ) static void * event_create(void) { - return (void *) CreateEvent(NULL, FALSE, FALSE, NULL); + return (void *) CreateEvent(NULL, FALSE, FALSE, NULL); } //////////////////////////////////////////////////////////////////////////////// @@ -1036,8 +1010,8 @@ event_create(void) static int event_wait(void *eventhdl) { - int res = WaitForSingleObject((HANDLE) eventhdl, (DWORD) INFINITE); - return (res == WAIT_OBJECT_0); + int res = WaitForSingleObject((HANDLE) eventhdl, (DWORD) INFINITE); + return (res == WAIT_OBJECT_0); } //////////////////////////////////////////////////////////////////////////////// @@ -1047,7 +1021,7 @@ event_wait(void *eventhdl) static int event_signal(void *eventhdl) { - return (int) SetEvent((HANDLE) eventhdl); + return (int) SetEvent((HANDLE) eventhdl); } //////////////////////////////////////////////////////////////////////////////// @@ -1057,7 +1031,7 @@ event_signal(void *eventhdl) static void event_destroy(void *eventhdl) { - CloseHandle((HANDLE) eventhdl); + CloseHandle((HANDLE) eventhdl); } //////////////////////////////////////////////////////////////////////////////// @@ -1067,10 +1041,9 @@ event_destroy(void *eventhdl) static void set_close_on_exec(SOCKET sock) { - (void)sock; // Unused. + (void) sock; // Unused. } - //////////////////////////////////////////////////////////////////////////////// // report_error // @@ -1080,60 +1053,53 @@ set_close_on_exec(SOCKET sock) static void stcp_report_error(const char *fmt, ...) { - va_list args; - - //flockfile(stdout); // TODO - va_start(args, fmt); - vprintf(fmt, args); - va_end(args); - putchar('\n'); - //funlockfile(stdout); // TODO - fflush(stdout); -} - - -#else // windows vs. unix + va_list args; + // flockfile(stdout); // TODO + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + putchar('\n'); + // funlockfile(stdout); // TODO + fflush(stdout); +} +#else // windows vs. unix // ****************** Unix specific ****************** - //////////////////////////////////////////////////////////////////////////////// // report_error // // TODO Make general -static void -stcp_report_error( const char *fmt, ... ) +static void +stcp_report_error(const char *fmt, ...) { - va_list args; - - flockfile( stdout ); - va_start( args, fmt ); - vprintf( fmt, args ); - va_end( args ); - putchar('\n'); - funlockfile( stdout ); - fflush( stdout ); -} + va_list args; + flockfile(stdout); + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + putchar('\n'); + funlockfile(stdout); + fflush(stdout); +} //////////////////////////////////////////////////////////////////////////////// // set_close_on_exec // static void -set_close_on_exec( SOCKET fd ) +set_close_on_exec(SOCKET fd) { - if ( fcntl( fd, F_SETFD, FD_CLOEXEC ) != 0 ) { - ; - } - + if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) { + ; + } } - //////////////////////////////////////////////////////////////////////////////// // set_blocking_mode // @@ -1141,36 +1107,36 @@ set_close_on_exec( SOCKET fd ) static int set_non_blocking_mode(int sock) { - int flags = fcntl(sock, F_GETFL, 0); + int flags = fcntl(sock, F_GETFL, 0); - if ( flags < 0 ) { - return -1; - } + if (flags < 0) { + return -1; + } - if ( fcntl( sock, F_SETFL, ( flags | O_NONBLOCK ) ) < 0 ) { - return -1; - } + if (fcntl(sock, F_SETFL, (flags | O_NONBLOCK)) < 0) { + return -1; + } - return 0; + return 0; } static int -set_blocking_mode( int sock ) +set_blocking_mode(int sock) { - int flags = fcntl( sock, F_GETFL, 0 ); + int flags = fcntl(sock, F_GETFL, 0); - if ( flags < 0 ) { - return -1; - } + if (flags < 0) { + return -1; + } - if ( fcntl( sock, F_SETFL, flags & (~(int)(O_NONBLOCK ) ) ) < 0 ) { - return -1; - } + if (fcntl(sock, F_SETFL, flags & (~(int) (O_NONBLOCK))) < 0) { + return -1; + } - return 0; + return 0; } -#endif // windows vs. unix +#endif // windows vs. unix /* va_copy should always be a macro, C99 and C++11 - DTL */ #ifndef va_copy @@ -1186,7 +1152,6 @@ set_blocking_mode( int sock ) #pragma GCC diagnostic ignored "-Wunused-function" #endif - static CRITICAL_SECTION global_log_file_lock; // static DWORD @@ -1195,7 +1160,6 @@ static CRITICAL_SECTION global_log_file_lock; // return GetCurrentThreadId(); // } - // static int // pthread_key_create( // pthread_key_t *key, @@ -1211,21 +1175,18 @@ static CRITICAL_SECTION global_log_file_lock; // return -2; // } - // static int // pthread_key_delete(pthread_key_t key) // { // return TlsFree(key) ? 0 : 1; // } - // static int // pthread_setspecific(pthread_key_t key, void *value) // { // return TlsSetValue(key, value) ? 0 : 1; // } - /*static void * pthread_getspecific(pthread_key_t key) { @@ -1242,7 +1203,6 @@ static struct pthread_mutex_undefined_struct *pthread_mutex_attr = NULL; __attribute__((unused)) static pthread_mutexattr_t pthread_mutex_attr; #endif /* _WIN32 */ - /* mg_init_library counter */ #ifndef WIN32 __attribute__((unused)) static int mg_init_library_called = 0; @@ -1262,18 +1222,17 @@ static int thread_idx_max = 0; #endif struct mg_workerTLS { - int is_master; - unsigned long thread_idx; -#if defined(_WIN32) - HANDLE pthread_cond_helper_mutex; - struct mg_workerTLS *next_waiting_thread; + int is_master; + unsigned long thread_idx; +#if defined(_WIN32) + HANDLE pthread_cond_helper_mutex; + struct mg_workerTLS *next_waiting_thread; #endif #if defined(MG_ALLOW_USING_GET_REQUEST_INFO_FOR_RESPONSE) - char txtbuf[4]; + char txtbuf[4]; #endif }; - #if defined(__GNUC__) || defined(__MINGW32__) /* Show no warning in case system functions are not used. */ #if GCC_VERSION >= 40500 @@ -1287,31 +1246,29 @@ struct mg_workerTLS { #pragma clang diagnostic ignored "-Wunused-function" #endif - static const char * -stcp_ssl_error( void ); // Forward declaration +stcp_ssl_error(void); // Forward declaration static pthread_mutex_t global_lock_mutex; - #if defined(_WIN32) // Forward declaration for Windows -//static int pthread_mutex_lock(pthread_mutex_t *mutex); -//static int pthread_mutex_unlock(pthread_mutex_t *mutex); +// static int pthread_mutex_lock(pthread_mutex_t *mutex); +// static int pthread_mutex_unlock(pthread_mutex_t *mutex); #endif - //////////////////////////////////////////////////////////////////////////////// // stcp_global_lock // #ifndef WIN32 -static void stcp_global_lock(void) __attribute__ ((unused)); +static void +stcp_global_lock(void) __attribute__((unused)); #endif static void stcp_global_lock(void) { - (void)pthread_mutex_lock( &global_lock_mutex ); + (void) pthread_mutex_lock(&global_lock_mutex); } //////////////////////////////////////////////////////////////////////////////// @@ -1319,15 +1276,15 @@ stcp_global_lock(void) // #ifndef WIN32 -static void stcp_global_unlock(void) __attribute__ ((unused)); +static void +stcp_global_unlock(void) __attribute__((unused)); #endif static void stcp_global_unlock(void) { - (void)pthread_mutex_unlock( &global_lock_mutex ); + (void) pthread_mutex_unlock(&global_lock_mutex); } - //////////////////////////////////////////////////////////////////////////////// // atomic_inc // @@ -1335,22 +1292,20 @@ stcp_global_unlock(void) static int atomic_inc(volatile int *addr) { - int ret; -#if defined(_WIN32) && !defined(NO_ATOMICS) - // Depending on the SDK, this function uses either - // (volatile unsigned int *) or (volatile LONG *), - // so whatever you use, the other SDK is likely to raise a warning. */ - ret = InterlockedIncrement((volatile long *) addr); -#elif defined(__GNUC__) \ - && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \ - && !defined(NO_ATOMICS) - ret = __sync_add_and_fetch(addr, 1); + int ret; +#if defined(_WIN32) && !defined(NO_ATOMICS) + // Depending on the SDK, this function uses either + // (volatile unsigned int *) or (volatile LONG *), + // so whatever you use, the other SDK is likely to raise a warning. */ + ret = InterlockedIncrement((volatile long *) addr); +#elif defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) && !defined(NO_ATOMICS) + ret = __sync_add_and_fetch(addr, 1); #else - stcp_global_lock(); - ret = (++(*addr)); - stcp_global_unlock(); + stcp_global_lock(); + ret = (++(*addr)); + stcp_global_unlock(); #endif - return ret; + return ret; } //////////////////////////////////////////////////////////////////////////////// @@ -1360,22 +1315,20 @@ atomic_inc(volatile int *addr) static int atomic_dec(volatile int *addr) { - int ret; -#if defined( _WIN32 ) && !defined( NO_ATOMICS ) - // Depending on the SDK, this function uses either - // (volatile unsigned int *) or (volatile LONG *), - // so whatever you use, the other SDK is likely to raise a warning. - ret = InterlockedDecrement((volatile long *) addr); -#elif defined(__GNUC__) \ - && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \ - && !defined(NO_ATOMICS) - ret = __sync_sub_and_fetch(addr, 1); + int ret; +#if defined(_WIN32) && !defined(NO_ATOMICS) + // Depending on the SDK, this function uses either + // (volatile unsigned int *) or (volatile LONG *), + // so whatever you use, the other SDK is likely to raise a warning. + ret = InterlockedDecrement((volatile long *) addr); +#elif defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) && !defined(NO_ATOMICS) + ret = __sync_sub_and_fetch(addr, 1); #else - stcp_global_lock(); - ret = (--(*addr)); - stcp_global_unlock(); + stcp_global_lock(); + ret = (--(*addr)); + stcp_global_unlock(); #endif - return ret; + return ret; } //////////////////////////////////////////////////////////////////////////////// @@ -1393,62 +1346,59 @@ atomic_dec(volatile int *addr) // #ifndef WIN32 -static unsigned long stcp_current_thread_id( void ) __attribute__((unused)); +static unsigned long +stcp_current_thread_id(void) __attribute__((unused)); #endif static unsigned long -stcp_current_thread_id( void ) +stcp_current_thread_id(void) { #ifdef _WIN32 - return GetCurrentThreadId(); + return GetCurrentThreadId(); #else #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunreachable-code" - // For every compiler, either "sizeof(pthread_t) > sizeof(unsigned long)" - // or not, so one of the two conditions will be unreachable by construction. - // Unfortunately the C standard does not define a way to check this at - // compile time, since the #if preprocessor conditions can not use the sizeof - // operator as an argument. + // For every compiler, either "sizeof(pthread_t) > sizeof(unsigned long)" + // or not, so one of the two conditions will be unreachable by construction. + // Unfortunately the C standard does not define a way to check this at + // compile time, since the #if preprocessor conditions can not use the sizeof + // operator as an argument. #endif - if ( sizeof( pthread_t ) > sizeof( unsigned long ) ) { - - // This is the problematic case for CRYPTO_set_id_callback: - // The OS pthread_t can not be cast to unsigned long. - struct stcp_workerTLS *tls = - (struct stcp_workerTLS *)pthread_getspecific( sTlsKey ); - - if ( NULL == tls ) { + if (sizeof(pthread_t) > sizeof(unsigned long)) { - // SSL called from an unknown thread: Create some thread index. - tls = (struct stcp_workerTLS *)malloc(sizeof (struct stcp_workerTLS)); - tls->is_master = -2; /* -2 means "3rd party thread" */ - tls->thread_idx = (unsigned)atomic_inc(&thread_idx_max); - pthread_setspecific( sTlsKey, tls ); + // This is the problematic case for CRYPTO_set_id_callback: + // The OS pthread_t can not be cast to unsigned long. + struct stcp_workerTLS *tls = (struct stcp_workerTLS *) pthread_getspecific(sTlsKey); - } + if (NULL == tls) { - return tls->thread_idx; + // SSL called from an unknown thread: Create some thread index. + tls = (struct stcp_workerTLS *) malloc(sizeof(struct stcp_workerTLS)); + tls->is_master = -2; /* -2 means "3rd party thread" */ + tls->thread_idx = (unsigned) atomic_inc(&thread_idx_max); + pthread_setspecific(sTlsKey, tls); } - else { - // pthread_t may be any data type, so a simple cast to unsigned long - // can rise a warning/error, depending on the platform. - // Here memcpy is used as an anything-to-anything cast. - unsigned long ret = 0; - pthread_t t = pthread_self(); - memcpy(&ret, &t, sizeof (pthread_t)); - return ret; - - } + return tls->thread_idx; + } + else { + + // pthread_t may be any data type, so a simple cast to unsigned long + // can rise a warning/error, depending on the platform. + // Here memcpy is used as an anything-to-anything cast. + unsigned long ret = 0; + pthread_t t = pthread_self(); + memcpy(&ret, &t, sizeof(pthread_t)); + return ret; + } #ifdef __clang__ #pragma clang diagnostic pop #endif #endif - } /* Darwin prior to 7.0 and Win32 do not have socklen_t */ @@ -1463,37 +1413,38 @@ typedef int socklen_t; #define MSG_NOSIGNAL (0) #endif - //////////////////////////////////////////////////////////////////////////////// // strlcpy // -static void _strlcpy( register char *dst, register const char *src, size_t n ) +static void +_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'; +} //////////////////////////////////////////////////////////////////////////////// // stcp_strndup // #ifndef WIN32 -static char *stcp_strndup( const char *ptr, size_t len ) __attribute__ ((unused)); +static char * +stcp_strndup(const char *ptr, size_t len) __attribute__((unused)); #endif -static char *stcp_strndup( const char *ptr, size_t len ) +static char * +stcp_strndup(const char *ptr, size_t len) { - char *p; + char *p; - if ( ( p = (char *)malloc( len + 1 ) ) != NULL ) { - _strlcpy( p, ptr, len + 1 ); - } + if ((p = (char *) malloc(len + 1)) != NULL) { + _strlcpy(p, ptr, len + 1); + } - return p; + return p; } //////////////////////////////////////////////////////////////////////////////// @@ -1501,15 +1452,17 @@ static char *stcp_strndup( const char *ptr, size_t len ) // #ifndef WIN32 -static char *stcp_strdup( const char *str ) __attribute__ ((unused)); +static char * +stcp_strdup(const char *str) __attribute__((unused)); #endif -static char *stcp_strdup( const char *str ) +static char * +stcp_strdup(const char *str) { #ifndef WIN32 - return strndup(str, strlen(str)); -#else - return __strndup(str, strlen(str)); -#endif + return strndup(str, strlen(str)); +#else + return __strndup(str, strlen(str)); +#endif } //////////////////////////////////////////////////////////////////////////////// @@ -1517,30 +1470,30 @@ static char *stcp_strdup( const char *str ) // static int -hexdump2string( void *mem, int memlen, char *buf, int buflen ) +hexdump2string(void *mem, int memlen, char *buf, int buflen) { - int i; - const char hexdigit[] = "0123456789abcdef"; + int i; + const char hexdigit[] = "0123456789abcdef"; - if ( ( memlen <= 0 ) || ( buflen <= 0 ) ) { - return 0; - } + if ((memlen <= 0) || (buflen <= 0)) { + return 0; + } - if ( buflen < (3 * memlen) ) { - return 0; - } + if (buflen < (3 * memlen)) { + return 0; + } - for ( i = 0; i < memlen; i++) { - if ( i > 0 ) { - buf[3 * i - 1] = ' '; - } - buf[3 * i] = hexdigit[(((uint8_t *) mem)[i] >> 4) & 0xF]; - buf[3 * i + 1] = hexdigit[((uint8_t *) mem)[i] & 0xF]; + for (i = 0; i < memlen; i++) { + if (i > 0) { + buf[3 * i - 1] = ' '; } - - buf[3 * memlen - 1] = 0; + buf[3 * i] = hexdigit[(((uint8_t *) mem)[i] >> 4) & 0xF]; + buf[3 * i + 1] = hexdigit[((uint8_t *) mem)[i] & 0xF]; + } - return 1; + buf[3 * memlen - 1] = 0; + + return 1; } //////////////////////////////////////////////////////////////////////////////// @@ -1548,40 +1501,24 @@ hexdump2string( void *mem, int memlen, char *buf, int buflen ) // static void -sockaddr_to_string( char *buf, size_t len, const union usa *usa ) +sockaddr_to_string(char *buf, size_t len, const union usa *usa) { - buf[0] = '\0'; - - if ( !usa ) { - return; - } + buf[0] = '\0'; - if ( AF_INET == usa->sa.sa_family ) { - - getnameinfo( &usa->sa, - sizeof( usa->sin ), - buf, - (unsigned)len, - NULL, - 0, - NI_NUMERICHOST ); - - } - else if ( AF_INET6 == usa->sa.sa_family ) { + if (!usa) { + return; + } - getnameinfo( &usa->sa, - sizeof( usa->sin6 ), - buf, - (unsigned)len, - NULL, - 0, - NI_NUMERICHOST ); + if (AF_INET == usa->sa.sa_family) { - } + getnameinfo(&usa->sa, sizeof(usa->sin), buf, (unsigned) len, NULL, 0, NI_NUMERICHOST); + } + else if (AF_INET6 == usa->sa.sa_family) { + getnameinfo(&usa->sa, sizeof(usa->sin6), buf, (unsigned) len, NULL, 0, NI_NUMERICHOST); + } } - /////////////////////////////////////////////////////////////////////////////// // OPENSSL /////////////////////////////////////////////////////////////////////////////// @@ -1596,47 +1533,40 @@ static pthread_mutex_t *ssl_mutexes; // static int -ssl_use_pem_file( SSL_CTX *ssl_ctx, const char *pem, const char *chain ) +ssl_use_pem_file(SSL_CTX *ssl_ctx, const char *pem, const char *chain) { - if ( 0 == SSL_CTX_use_certificate_file( ssl_ctx, pem, 1 ) ) { - stcp_report_error( "sockettcp: Cannot open certificate file %s: %s", - pem, - stcp_ssl_error() ); - return 0; - } + if (0 == SSL_CTX_use_certificate_file(ssl_ctx, pem, 1)) { + stcp_report_error("sockettcp: Cannot open certificate file %s: %s", pem, stcp_ssl_error()); + return 0; + } - // could use SSL_CTX_set_default_passwd_cb_userdata - if ( 0 == SSL_CTX_use_PrivateKey_file( ssl_ctx, pem, 1 ) ) { - stcp_report_error( "sockettcp: Cannot open private key file %s: %s", - pem, - stcp_ssl_error() ); - return 0; - } + // could use SSL_CTX_set_default_passwd_cb_userdata + if (0 == SSL_CTX_use_PrivateKey_file(ssl_ctx, pem, 1)) { + stcp_report_error("sockettcp: Cannot open private key file %s: %s", pem, stcp_ssl_error()); + return 0; + } - if ( 0 == SSL_CTX_check_private_key( ssl_ctx ) ) { - stcp_report_error( "sockettcp: Certificate and private key do not match: %s", - pem ); - return 0; - } + if (0 == SSL_CTX_check_private_key(ssl_ctx)) { + stcp_report_error("sockettcp: Certificate and private key do not match: %s", pem); + return 0; + } - // In contrast to OpenSSL, wolfSSL does not support certificate - // chain files that contain private keys and certificates in - // SSL_CTX_use_certificate_chain_file. - // The vscpweb-Server used pem-Files that contained both information. - // In order to make wolfSSL work, it is split in two files. - // One file that contains key and certificate used by the server and - // an optional chain file for the ssl stack. - // - if (chain) { - if ( 0 == SSL_CTX_use_certificate_chain_file( ssl_ctx, chain ) ) { - stcp_report_error( "sockettcp: Cannot use certificate chain file %s: %s", - pem, - stcp_ssl_error() ); - return 0; - } + // In contrast to OpenSSL, wolfSSL does not support certificate + // chain files that contain private keys and certificates in + // SSL_CTX_use_certificate_chain_file. + // The vscpweb-Server used pem-Files that contained both information. + // In order to make wolfSSL work, it is split in two files. + // One file that contains key and certificate used by the server and + // an optional chain file for the ssl stack. + // + if (chain) { + if (0 == SSL_CTX_use_certificate_chain_file(ssl_ctx, chain)) { + stcp_report_error("sockettcp: Cannot use certificate chain file %s: %s", pem, stcp_ssl_error()); + return 0; } + } - return 1; + return 1; } //////////////////////////////////////////////////////////////////////////////// @@ -1644,82 +1574,79 @@ ssl_use_pem_file( SSL_CTX *ssl_ctx, const char *pem, const char *chain ) // static int -refresh_trust( struct stcp_connection *conn, - const char *pem, - const char *chain, - const char *ca_path, - const char *ca_file ) -{ - static int reload_lock = 0; - static long int data_check = 0; - volatile int *p_reload_lock = (volatile int *) &reload_lock; - - struct stat cert_buf; - long int t; - //const char *pem; - //const char *chain; - int should_verify_peer; - - if ( NULL == pem ) { - // If peem is NULL and conn->ctx->callbacks.init_ssl is not, - // refresh_trust still can not work. - return 0; - } - - if ( NULL == chain ) { - // pem is not NULL here - chain = pem; - } - if (*chain == 0) { - chain = NULL; - } +refresh_trust(struct stcp_connection *conn, + const char *pem, + const char *chain, + const char *ca_path, + const char *ca_file) +{ + static int reload_lock = 0; + static long int data_check = 0; + volatile int *p_reload_lock = (volatile int *) &reload_lock; + + struct stat cert_buf; + long int t; + // const char *pem; + // const char *chain; + int should_verify_peer; + + if (NULL == pem) { + // If peem is NULL and conn->ctx->callbacks.init_ssl is not, + // refresh_trust still can not work. + return 0; + } - t = data_check; - if (stat(pem, &cert_buf) != -1) { - t = (long int) cert_buf.st_mtime; - } + if (NULL == chain) { + // pem is not NULL here + chain = pem; + } + if (*chain == 0) { + chain = NULL; + } - if (data_check != t) { - - data_check = t; + t = data_check; + if (stat(pem, &cert_buf) != -1) { + t = (long int) cert_buf.st_mtime; + } - should_verify_peer = 0; - if ( 1 == STCP_SSL_DO_VERIFY_PEER ) { - should_verify_peer = 1; - } - else if ( 0 == STCP_SSL_DO_VERIFY_PEER ) { - should_verify_peer = 1; - } + if (data_check != t) { - if ( should_verify_peer ) { - - if ( SSL_CTX_load_verify_locations( conn->ssl_ctx, - ca_file, - ca_path ) != 1) { - stcp_report_error( - "sockettcp: SSL_CTX_load_verify_locations error: %s " - "ssl_verify_peer requires setting " - "either ssl_ca_path or ssl_ca_file. Is any of them " - "present in " - "the .conf file?", - stcp_ssl_error() ); - return 0; - } - } + data_check = t; - if ( 1 == atomic_inc( p_reload_lock ) ) { - if ( 0 == ssl_use_pem_file( conn->ssl_ctx, pem, chain ) ) { - return 0; - } - *p_reload_lock = 0; - } + should_verify_peer = 0; + if (1 == STCP_SSL_DO_VERIFY_PEER) { + should_verify_peer = 1; } - // lock while cert is reloading - while (*p_reload_lock) { - sleep(1); + else if (0 == STCP_SSL_DO_VERIFY_PEER) { + should_verify_peer = 1; } - return 1; + if (should_verify_peer) { + + if (SSL_CTX_load_verify_locations(conn->ssl_ctx, ca_file, ca_path) != 1) { + stcp_report_error("sockettcp: SSL_CTX_load_verify_locations error: %s " + "ssl_verify_peer requires setting " + "either ssl_ca_path or ssl_ca_file. Is any of them " + "present in " + "the .conf file?", + stcp_ssl_error()); + return 0; + } + } + + if (1 == atomic_inc(p_reload_lock)) { + if (0 == ssl_use_pem_file(conn->ssl_ctx, pem, chain)) { + return 0; + } + *p_reload_lock = 0; + } + } + // lock while cert is reloading + while (*p_reload_lock) { + sleep(1); + } + + return 1; } //////////////////////////////////////////////////////////////////////////////// @@ -1729,107 +1656,104 @@ refresh_trust( struct stcp_connection *conn, // static int -make_ssl( struct stcp_connection *conn, - struct stcp_secure_options *secure_opts, - SSL_CTX *s, - int (*func)(SSL *), - volatile int *stop_server ) +make_ssl(struct stcp_connection *conn, + struct stcp_secure_options *secure_opts, + SSL_CTX *s, + int (*func)(SSL *), + volatile int *stop_server) { - int ret, err; - unsigned i; + int ret, err; + unsigned i; - if ( ( NULL == conn ) || ( NULL == secure_opts ) ) { - return 0; - } + if ((NULL == conn) || (NULL == secure_opts)) { + return 0; + } - if ( STCP_SSL_SHORT_TRUST ) { - int trust_ret = refresh_trust( conn, - secure_opts->pem, - secure_opts->chain, - secure_opts->ca_path, - secure_opts->ca_file ); - if ( !trust_ret ) { - return trust_ret; - } + if (STCP_SSL_SHORT_TRUST) { + int trust_ret = + refresh_trust(conn, secure_opts->pem, secure_opts->chain, secure_opts->ca_path, secure_opts->ca_file); + if (!trust_ret) { + return trust_ret; } + } - conn->ssl = SSL_new( s ); - if ( NULL == conn->ssl ) { - return 0; - } + conn->ssl = SSL_new(s); + if (NULL == conn->ssl) { + return 0; + } - SSL_set_app_data( conn->ssl, (char *)conn ); - - ret = SSL_set_fd( conn->ssl, conn->client.sock ); - if ( ret != 1 ) { - err = SSL_get_error( conn->ssl, ret ); - (void)err; // TODO: set some error message - SSL_free( conn->ssl ); - conn->ssl = NULL; - // Avoid CRYPTO_cleanup_all_ex_data(); See discussion: - // https://wiki.openssl.org/index.php/Talk:Library_Initialization -#ifdef OPENSSL_API_1_0 - ERR_remove_state(0); // deprecated in 1.0.0, solved by going to 1.1.0 + SSL_set_app_data(conn->ssl, (char *) conn); + + ret = SSL_set_fd(conn->ssl, conn->client.sock); + if (ret != 1) { + err = SSL_get_error(conn->ssl, ret); + (void) err; // TODO: set some error message + SSL_free(conn->ssl); + conn->ssl = NULL; +// Avoid CRYPTO_cleanup_all_ex_data(); See discussion: +// https://wiki.openssl.org/index.php/Talk:Library_Initialization +#if OPENSSL_VERSION_NUMBER >= 0x10000000L && OPENSSL_VERSION_NUMBER < 0x10100000L + ERR_remove_thread_state(NULL); +#elif OPENSSL_VERSION_NUMBER < 0x10000000L + ERR_remove_state(0); #endif - return 0; - } + return 0; + } - // SSL functions may fail and require to be called again: - // see https://www.openssl.org/docs/manmaster/ssl/SSL_get_error.html - // Here "func" could be SSL_connect or SSL_accept. - for ( i = 16; i <= 1024; i *= 2 ) { - - ret = func( conn->ssl ); - if (ret != 1 ) { - - err = SSL_get_error( conn->ssl, ret ); - if ( ( err == SSL_ERROR_WANT_CONNECT ) || - ( err == SSL_ERROR_WANT_ACCEPT ) || - ( err == SSL_ERROR_WANT_READ ) || - ( err == SSL_ERROR_WANT_WRITE ) ) { - // Need to retry the function call "later". - // See https://linux.die.net/man/3/ssl_get_error - // This is typical for non-blocking sockets. - if ( *stop_server ) { - // Don't wait if the server is going to be stopped. - break; - } - - stcp_sleep( i ); + // SSL functions may fail and require to be called again: + // see https://www.openssl.org/docs/manmaster/ssl/SSL_get_error.html + // Here "func" could be SSL_connect or SSL_accept. + for (i = 16; i <= 1024; i *= 2) { - } - else if ( err == SSL_ERROR_SYSCALL ) { - // This is an IO error. Look at errno. - err = errno; - // TODO: set some error message - (void)err; - break; - } - else { - // This is an SSL specific error - // TODO: set some error message - break; - } + ret = func(conn->ssl); + if (ret != 1) { + err = SSL_get_error(conn->ssl, ret); + if ((err == SSL_ERROR_WANT_CONNECT) || (err == SSL_ERROR_WANT_ACCEPT) || (err == SSL_ERROR_WANT_READ) || + (err == SSL_ERROR_WANT_WRITE)) { + // Need to retry the function call "later". + // See https://linux.die.net/man/3/ssl_get_error + // This is typical for non-blocking sockets. + if (*stop_server) { + // Don't wait if the server is going to be stopped. + break; } - else { - // success - break; - } + + stcp_sleep(i); + } + else if (err == SSL_ERROR_SYSCALL) { + // This is an IO error. Look at errno. + err = errno; + // TODO: set some error message + (void) err; + break; + } + else { + // This is an SSL specific error + // TODO: set some error message + break; + } } + else { + // success + break; + } + } - if ( ret != 1 ) { - SSL_free( conn->ssl ); - conn->ssl = NULL; - // Avoid CRYPTO_cleanup_all_ex_data(); See discussion: - // https://wiki.openssl.org/index.php/Talk:Library_Initialization -#ifdef OPENSSL_API_1_0 - ERR_remove_state(0); // deprecated in 1.0.0, solved by going to 1.1.0 + if (ret != 1) { + SSL_free(conn->ssl); + conn->ssl = NULL; +// Avoid CRYPTO_cleanup_all_ex_data(); See discussion: +// https://wiki.openssl.org/index.php/Talk:Library_Initialization +#if OPENSSL_VERSION_NUMBER >= 0x10000000L && OPENSSL_VERSION_NUMBER < 0x10100000L + ERR_remove_thread_state(NULL); +#elif OPENSSL_VERSION_NUMBER < 0x10000000L + ERR_remove_state(0); #endif - return 0; - } + return 0; + } - return 1; + return 1; } //////////////////////////////////////////////////////////////////////////////// @@ -1839,111 +1763,105 @@ make_ssl( struct stcp_connection *conn, // static const char * -stcp_ssl_error( void ) +stcp_ssl_error(void) { - unsigned long err; - err = ERR_get_error(); - return ( ( err == 0 ) ? "" : ERR_error_string( err, NULL ) ); + unsigned long err; + err = ERR_get_error(); + return ((err == 0) ? "" : ERR_error_string(err, NULL)); } - //////////////////////////////////////////////////////////////////////////////// // ssl_get_client_cert_info // static void -ssl_get_client_cert_info( struct stcp_connection *conn, - struct stcp_srv_client_cert *client_cert ) -{ - X509 *cert = SSL_get_peer_certificate( conn->ssl ); - if ( cert ) { - char str_subject[1024]; - char str_issuer[1024]; - char str_finger[1024]; - unsigned char buf[256]; - char *str_serial = NULL; - unsigned int ulen; - int ilen; - unsigned char *tmp_buf; - unsigned char *tmp_p; - - /* Handle to algorithm used for fingerprint */ - const EVP_MD *digest = EVP_get_digestbyname("sha1"); - - /* Get Subject and issuer */ - X509_NAME *subj = X509_get_subject_name( cert ); - X509_NAME *iss = X509_get_issuer_name( cert ); - - /* Get serial number */ - ASN1_INTEGER *serial = X509_get_serialNumber( cert ); - - /* Translate serial number to a hex string */ - BIGNUM *serial_bn = ASN1_INTEGER_to_BN( serial, NULL ); - str_serial = BN_bn2hex( serial_bn ); - BN_free( serial_bn ); - - /* Translate subject and issuer to a string */ - (void)X509_NAME_oneline( subj, str_subject, (int)sizeof( str_subject ) ); - (void)X509_NAME_oneline( iss, str_issuer, (int)sizeof( str_issuer ) ); - - /* Calculate SHA1 fingerprint and store as a hex string */ - ulen = 0; +ssl_get_client_cert_info(struct stcp_connection *conn, struct stcp_srv_client_cert *client_cert) +{ + X509 *cert = SSL_get_peer_certificate(conn->ssl); + if (cert) { + char str_subject[1024]; + char str_issuer[1024]; + char str_finger[1024]; + unsigned char buf[256]; + char *str_serial = NULL; + unsigned int ulen; + int ilen; + unsigned char *tmp_buf; + unsigned char *tmp_p; + + /* Handle to algorithm used for fingerprint */ + const EVP_MD *digest = EVP_get_digestbyname("sha1"); + + /* Get Subject and issuer */ + X509_NAME *subj = X509_get_subject_name(cert); + X509_NAME *iss = X509_get_issuer_name(cert); + + /* Get serial number */ + ASN1_INTEGER *serial = X509_get_serialNumber(cert); + + /* Translate serial number to a hex string */ + BIGNUM *serial_bn = ASN1_INTEGER_to_BN(serial, NULL); + str_serial = BN_bn2hex(serial_bn); + BN_free(serial_bn); + + /* Translate subject and issuer to a string */ + (void) X509_NAME_oneline(subj, str_subject, (int) sizeof(str_subject)); + (void) X509_NAME_oneline(iss, str_issuer, (int) sizeof(str_issuer)); + + /* Calculate SHA1 fingerprint and store as a hex string */ + ulen = 0; + + /* + ASN1_digest is deprecated. Do the calculation manually, + using EVP_Digest. + */ + ilen = i2d_X509(cert, NULL); + tmp_buf = (ilen > 0) ? (unsigned char *) malloc((unsigned) ilen + 1) : NULL; + if (tmp_buf) { - /* - ASN1_digest is deprecated. Do the calculation manually, - using EVP_Digest. - */ - ilen = i2d_X509( cert, NULL ); - tmp_buf = (ilen > 0) ? (unsigned char *)malloc( (unsigned)ilen + 1 ) - : NULL; - if ( tmp_buf ) { - - tmp_p = tmp_buf; - (void)i2d_X509( cert, &tmp_p ); - if ( !EVP_Digest( tmp_buf, (unsigned)ilen, buf, &ulen, digest, NULL ) ) { - ulen = 0; - } + tmp_p = tmp_buf; + (void) i2d_X509(cert, &tmp_p); + if (!EVP_Digest(tmp_buf, (unsigned) ilen, buf, &ulen, digest, NULL)) { + ulen = 0; + } - free( tmp_buf ); - } + free(tmp_buf); + } - if ( !hexdump2string( buf, (int)ulen, str_finger, (int)sizeof( str_finger ) ) ) { - *str_finger = 0; - } + if (!hexdump2string(buf, (int) ulen, str_finger, (int) sizeof(str_finger))) { + *str_finger = 0; + } - client_cert = (struct stcp_srv_client_cert *) - malloc( sizeof( struct stcp_srv_client_cert ) ); + client_cert = (struct stcp_srv_client_cert *) malloc(sizeof(struct stcp_srv_client_cert)); - if ( client_cert ) { + if (client_cert) { #ifndef WIN32 - client_cert->subject = strdup( str_subject ); - client_cert->issuer = strdup( str_issuer ); - client_cert->serial = strdup( str_serial ); - client_cert->finger = strdup( str_finger ); -#else - client_cert->subject = __strdup( str_subject ); - client_cert->issuer = __strdup( str_issuer ); - client_cert->serial = __strdup( str_serial ); - client_cert->finger = __strdup( str_finger ); -#endif - } - else { - stcp_report_error( "sockettcp: Out of memory: Cannot allocate memory for client " - "certificate" ); - } - - /* - Strings returned from bn_bn2hex must be freed using OPENSSL_free, - see https://linux.die.net/man/3/bn_bn2hex - */ - OPENSSL_free( str_serial ); - - /* Free certificate memory */ - X509_free( cert ); + client_cert->subject = strdup(str_subject); + client_cert->issuer = strdup(str_issuer); + client_cert->serial = strdup(str_serial); + client_cert->finger = strdup(str_finger); +#else + client_cert->subject = __strdup(str_subject); + client_cert->issuer = __strdup(str_issuer); + client_cert->serial = __strdup(str_serial); + client_cert->finger = __strdup(str_finger); +#endif + } + else { + stcp_report_error("sockettcp: Out of memory: Cannot allocate memory for client " + "certificate"); } -} + /* + Strings returned from bn_bn2hex must be freed using OPENSSL_free, + see https://linux.die.net/man/3/bn_bn2hex + */ + OPENSSL_free(str_serial); + /* Free certificate memory */ + X509_free(cert); + } +} //////////////////////////////////////////////////////////////////////////////// // ssl_locking_callback @@ -1951,23 +1869,23 @@ ssl_get_client_cert_info( struct stcp_connection *conn, #ifdef OPENSSL_API_1_1 - // Not needed of for 1.1 +// Not needed of for 1.1 #else static void -ssl_locking_callback( int mode, int mutex_num, const char *file, int line ) +ssl_locking_callback(int mode, int mutex_num, const char *file, int line) { - (void)line; - (void)file; + (void) line; + (void) file; - if ( mode & 1 ) { - // 1 is CRYPTO_LOCK - (void)pthread_mutex_lock( &ssl_mutexes[ mutex_num ] ); - } - else { - (void)pthread_mutex_unlock( &ssl_mutexes[ mutex_num ] ); - } + if (mode & 1) { + // 1 is CRYPTO_LOCK + (void) pthread_mutex_lock(&ssl_mutexes[mutex_num]); + } + else { + (void) pthread_mutex_unlock(&ssl_mutexes[mutex_num]); + } } #endif @@ -1983,64 +1901,59 @@ static int cryptolib_users = 1; // Reference counter for crypto library. static int cryptolib_users = 0; // Reference counter for crypto library. #endif - //////////////////////////////////////////////////////////////////////////////// // stcp_init_mt_ssl // int -stcp_init_mt_ssl( void ) +stcp_init_mt_ssl(void) { #ifdef OPENSSL_API_1_1 - if ( atomic_inc( &cryptolib_users ) > 1 ) { - return 1; - } + if (atomic_inc(&cryptolib_users) > 1) { + return 1; + } #else // not OPENSSL_API_1_1 - int i; - size_t size; + int i; + size_t size; - if ( atomic_inc( &cryptolib_users ) > 1 ) { - return 1; - } + if (atomic_inc(&cryptolib_users) > 1) { + return 1; + } - // Initialize locking callbacks, needed for thread safety. - // http://www.openssl.org/support/faq.html#PROG1 - // - i = CRYPTO_num_locks(); - if ( i < 0 ) { - i = 0; - } + // Initialize locking callbacks, needed for thread safety. + // http://www.openssl.org/support/faq.html#PROG1 + // + i = CRYPTO_num_locks(); + if (i < 0) { + i = 0; + } - size = sizeof( pthread_mutex_t ) * ((size_t)(i)); + size = sizeof(pthread_mutex_t) * ((size_t) (i)); - if ( 0 == size ) { - ssl_mutexes = NULL; - } - else if ( NULL == ( ssl_mutexes = (pthread_mutex_t *)malloc( size ) ) ) { - stcp_report_error( "sockettcp: Cannot allocate mutexes: %s", stcp_ssl_error() ); - return 0; - } + if (0 == size) { + ssl_mutexes = NULL; + } + else if (NULL == (ssl_mutexes = (pthread_mutex_t *) malloc(size))) { + stcp_report_error("sockettcp: Cannot allocate mutexes: %s", stcp_ssl_error()); + return 0; + } - for ( i = 0; i < CRYPTO_num_locks(); i++ ) { - pthread_mutex_init( &ssl_mutexes[i], (void *)&pthread_mutex_attr ); - } + for (i = 0; i < CRYPTO_num_locks(); i++) { + pthread_mutex_init(&ssl_mutexes[i], (void *) &pthread_mutex_attr); + } - CRYPTO_set_locking_callback( &ssl_locking_callback ); - CRYPTO_set_id_callback( &stcp_current_thread_id ); + CRYPTO_set_locking_callback(&ssl_locking_callback); + CRYPTO_set_id_callback(&stcp_current_thread_id); #endif // OPENSSL_API_1_1 - return 1; + return 1; } - - - - #ifdef OPENSSL_API_1_1 //////////////////////////////////////////////////////////////////////////////// @@ -2050,16 +1963,16 @@ stcp_init_mt_ssl( void ) static unsigned long ssl_get_protocol(int version_id) { - long unsigned ret = SSL_OP_ALL; - if (version_id > 0) - ret |= SSL_OP_NO_SSLv2; - if (version_id > 1) - ret |= SSL_OP_NO_SSLv3; - if (version_id > 2) - ret |= SSL_OP_NO_TLSv1; - if (version_id > 3) - ret |= SSL_OP_NO_TLSv1_1; - return ret; + long unsigned ret = SSL_OP_ALL; + if (version_id > 0) + ret |= SSL_OP_NO_SSLv2; + if (version_id > 1) + ret |= SSL_OP_NO_SSLv3; + if (version_id > 2) + ret |= SSL_OP_NO_TLSv1; + if (version_id > 3) + ret |= SSL_OP_NO_TLSv1_1; + return ret; } #else @@ -2069,18 +1982,18 @@ ssl_get_protocol(int version_id) // static long -ssl_get_protocol( int version_id ) +ssl_get_protocol(int version_id) { - long ret = SSL_OP_ALL; - if ( version_id > 0 ) - ret |= SSL_OP_NO_SSLv2; - if ( version_id > 1 ) - ret |= SSL_OP_NO_SSLv3; - if ( version_id > 2 ) - ret |= SSL_OP_NO_TLSv1; - if ( version_id > 3 ) - ret |= SSL_OP_NO_TLSv1_1; - return ret; + long ret = SSL_OP_ALL; + if (version_id > 0) + ret |= SSL_OP_NO_SSLv2; + if (version_id > 1) + ret |= SSL_OP_NO_SSLv3; + if (version_id > 2) + ret |= SSL_OP_NO_TLSv1; + if (version_id > 3) + ret |= SSL_OP_NO_TLSv1_1; + return ret; } #endif // OPENSSL_API_1_1 @@ -2095,16 +2008,16 @@ ssl_get_protocol( int version_id ) static void ssl_info_callback(SSL *ssl, int what, int ret) { - (void) ret; + (void) ret; - if ( what & SSL_CB_HANDSHAKE_START ) { - SSL_get_app_data(ssl); - } - if ( what & SSL_CB_HANDSHAKE_DONE) { - // TODO: check for openSSL 1.1 - // #define SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS 0x0001 - // ssl->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS; - } + if (what & SSL_CB_HANDSHAKE_START) { + SSL_get_app_data(ssl); + } + if (what & SSL_CB_HANDSHAKE_DONE) { + // TODO: check for openSSL 1.1 + // #define SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS 0x0001 + // ssl->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS; + } } //////////////////////////////////////////////////////////////////////////////// @@ -2114,197 +2027,172 @@ ssl_info_callback(SSL *ssl, int what, int ret) // int -stcp_init_ssl( SSL_CTX *ssl_ctx, struct stcp_secure_options *secure_opts ) +stcp_init_ssl(SSL_CTX *ssl_ctx, struct stcp_secure_options *secure_opts) { -#ifndef WIN32 - __attribute__((unused))int callback_ret; -#endif - int should_verify_peer; - int peer_certificate_optional; - int use_default_verify_paths; -#ifndef WIN32 - __attribute__((unused))int verify_depth; -#endif - time_t now_rt = time(NULL); - struct timespec now_mt; - md5_byte_t ssl_context_id[16]; - md5_state_t md5state; - #ifndef WIN32 - __attribute__((unused))int protocol_ver; - #endif - - /* Must have secure options */ - if ( NULL == secure_opts ) { - return 0; - } +#ifndef WIN32 + __attribute__((unused)) int callback_ret; +#endif + int should_verify_peer; + int peer_certificate_optional; + int use_default_verify_paths; +#ifndef WIN32 + __attribute__((unused)) int verify_depth; +#endif + time_t now_rt = time(NULL); + struct timespec now_mt; + md5_byte_t ssl_context_id[16]; + md5_state_t md5state; +#ifndef WIN32 + __attribute__((unused)) int protocol_ver; +#endif - /* - If PEM file is not specified and the init_ssl callback - is not specified, skip SSL initialization. - */ + /* Must have secure options */ + if (NULL == secure_opts) { + return 0; + } - if ( NULL == secure_opts->pem ) { - return 1; - } + /* + If PEM file is not specified and the init_ssl callback + is not specified, skip SSL initialization. + */ - if ( NULL == secure_opts->chain ) { - secure_opts->chain = secure_opts->pem; - } - - if ( ( secure_opts->chain != NULL ) && ( *secure_opts->chain == 0 ) ) { - secure_opts->chain = NULL; - } + if (NULL == secure_opts->pem) { + return 1; + } - /* Init. ssl multithread locks for ssl 1.0 */ - if ( !(secure_opts->bNOInitMT) ) { - if ( !stcp_init_mt_ssl() ) { - stcp_report_error( "sockettcp: Failed to init ssl\n" ); - return 0; - } - } + if (NULL == secure_opts->chain) { + secure_opts->chain = secure_opts->pem; + } + if ((secure_opts->chain != NULL) && (*secure_opts->chain == 0)) { + secure_opts->chain = NULL; + } -#ifdef OPENSSL_API_1_1 - // Initialize SSL library - OPENSSL_init_ssl(0, NULL); - OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS - | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, - NULL); - - if ( NULL == ( ssl_ctx = SSL_CTX_new( TLS_server_method() ) ) ) { - stcp_report_error( "sockettcp: SSL_CTX_new (server) error: %s", stcp_ssl_error() ); - return 0; + /* Init. ssl multithread locks for ssl 1.0 */ + if (!(secure_opts->bNOInitMT)) { + if (!stcp_init_mt_ssl()) { + stcp_report_error("sockettcp: Failed to init ssl\n"); + return 0; } + } + +#ifdef OPENSSL_API_1_1 + // Initialize SSL library + OPENSSL_init_ssl(0, NULL); + OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); + + if (NULL == (ssl_ctx = SSL_CTX_new(TLS_server_method()))) { + stcp_report_error("sockettcp: SSL_CTX_new (server) error: %s", stcp_ssl_error()); + return 0; + } #else - // Initialize SSL library - SSL_library_init(); - SSL_load_error_strings(); + // Initialize SSL library + SSL_library_init(); + SSL_load_error_strings(); - if ( NULL == ( ssl_ctx = SSL_CTX_new( SSLv23_server_method() ) ) ) { - stcp_report_error( "sockettcp: SSL_CTX_new (server) error: %s", stcp_ssl_error() ); - return 0; - } + if (NULL == (ssl_ctx = SSL_CTX_new(SSLv23_server_method()))) { + stcp_report_error("sockettcp: SSL_CTX_new (server) error: %s", stcp_ssl_error()); + return 0; + } #endif // OPENSSL_API_1_1 - SSL_CTX_clear_options( ssl_ctx, - SSL_OP_NO_SSLv2 | - SSL_OP_NO_SSLv3 | - SSL_OP_NO_TLSv1 | - SSL_OP_NO_TLSv1_1 ); + SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1); - SSL_CTX_set_options( ssl_ctx, ssl_get_protocol( STCP_SSL_PROTOCOL_VERSION ) ); - SSL_CTX_set_options( ssl_ctx, SSL_OP_SINGLE_DH_USE); - SSL_CTX_set_options( ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); - SSL_CTX_set_options( ssl_ctx, SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); - SSL_CTX_set_options( ssl_ctx, SSL_OP_NO_COMPRESSION); + SSL_CTX_set_options(ssl_ctx, ssl_get_protocol(STCP_SSL_PROTOCOL_VERSION)); + SSL_CTX_set_options(ssl_ctx, SSL_OP_SINGLE_DH_USE); + SSL_CTX_set_options(ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); + SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_COMPRESSION); #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wincompatible-pointer-types" #endif - // Depending on the OpenSSL version, the callback may be - // 'void (*)(SSL *, int, int)' or 'void (*)(const SSL *, int, int)' - // yielding in an "incompatible-pointer-type" warning for the other - // version. It seems to be "unclear" what is correct: - // https://bugs.launchpad.net/ubuntu/+source/openssl/+bug/1147526 - // https://www.openssl.org/docs/man1.0.2/ssl/ssl.html - // https://www.openssl.org/docs/man1.1.0/ssl/ssl.html - // https://github.com/openssl/openssl/blob/1d97c8435171a7af575f73c526d79e1ef0ee5960/ssl/ssl.h#L1173 - // Disable this warning here. - // Alternative would be a version dependent ssl_info_callback and - // a const-cast to call 'char *SSL_get_app_data(SSL *ssl)' there. - // - SSL_CTX_set_info_callback( ssl_ctx, (void *)ssl_info_callback ); + // Depending on the OpenSSL version, the callback may be + // 'void (*)(SSL *, int, int)' or 'void (*)(const SSL *, int, int)' + // yielding in an "incompatible-pointer-type" warning for the other + // version. It seems to be "unclear" what is correct: + // https://bugs.launchpad.net/ubuntu/+source/openssl/+bug/1147526 + // https://www.openssl.org/docs/man1.0.2/ssl/ssl.html + // https://www.openssl.org/docs/man1.1.0/ssl/ssl.html + // https://github.com/openssl/openssl/blob/1d97c8435171a7af575f73c526d79e1ef0ee5960/ssl/ssl.h#L1173 + // Disable this warning here. + // Alternative would be a version dependent ssl_info_callback and + // a const-cast to call 'char *SSL_get_app_data(SSL *ssl)' there. + // + SSL_CTX_set_info_callback(ssl_ctx, (void *) ssl_info_callback); #ifdef __clang__ #pragma clang diagnostic pop #endif + // Use some UID as session context ID. TODO + vscpmd5_init(&md5state); + vscpmd5_append(&md5state, (const md5_byte_t *) &now_rt, sizeof(now_rt)); + clock_gettime(CLOCK_MONOTONIC, &now_mt); + vscpmd5_append(&md5state, (const md5_byte_t *) &now_mt, sizeof(now_mt)); + vscpmd5_append(&md5state, (const md5_byte_t *) "Stay foolish be hungry", strlen("Stay foolish be hungry")); + vscpmd5_append(&md5state, (const md5_byte_t *) secure_opts, sizeof(*secure_opts)); + vscpmd5_finish(&md5state, ssl_context_id); - // Use some UID as session context ID. TODO - vscpmd5_init( &md5state ); - vscpmd5_append( &md5state, (const md5_byte_t *)&now_rt, sizeof( now_rt ) ); - clock_gettime( CLOCK_MONOTONIC, &now_mt ); - vscpmd5_append( &md5state, (const md5_byte_t *)&now_mt, sizeof( now_mt ) ); - vscpmd5_append( &md5state, - (const md5_byte_t *)"Stay foolish be hungry", - strlen("Stay foolish be hungry") ); - vscpmd5_append( &md5state, (const md5_byte_t *)secure_opts, sizeof (*secure_opts) ); - vscpmd5_finish( &md5state, ssl_context_id ); - - SSL_CTX_set_session_id_context( ssl_ctx, - (const unsigned char *)&ssl_context_id, - sizeof( ssl_context_id ) ); + SSL_CTX_set_session_id_context(ssl_ctx, (const unsigned char *) &ssl_context_id, sizeof(ssl_context_id)); - if ( secure_opts->pem != NULL ) { + if (secure_opts->pem != NULL) { - if ( !ssl_use_pem_file( ssl_ctx, - secure_opts->pem, - secure_opts->chain ) ) { - return 0; - } + if (!ssl_use_pem_file(ssl_ctx, secure_opts->pem, secure_opts->chain)) { + return 0; } + } - // Should we support client certificates? - // Default is "no". - should_verify_peer = 0; + // Should we support client certificates? + // Default is "no". + should_verify_peer = 0; + peer_certificate_optional = 0; + if (1 == STCP_SSL_DO_VERIFY_PEER) { + // Mandatory + should_verify_peer = 1; peer_certificate_optional = 0; - if ( 1 == STCP_SSL_DO_VERIFY_PEER ) { - // Mandatory - should_verify_peer = 1; - peer_certificate_optional = 0; - } - else if ( 2 == STCP_SSL_DO_VERIFY_PEER ) { - // Optional - should_verify_peer = 1; - peer_certificate_optional = 1; - } - + } + else if (2 == STCP_SSL_DO_VERIFY_PEER) { + // Optional + should_verify_peer = 1; + peer_certificate_optional = 1; + } - use_default_verify_paths = STCP_SSL_DEFAULT_VERIFY_PATHS; + use_default_verify_paths = STCP_SSL_DEFAULT_VERIFY_PATHS; - if ( should_verify_peer ) { + if (should_verify_peer) { - if ( SSL_CTX_load_verify_locations( ssl_ctx, - secure_opts->ca_file, - secure_opts->ca_path ) - != 1) { - stcp_report_error( - "sockettcp: SSL_CTX_load_verify_locations error: %s " + if (SSL_CTX_load_verify_locations(ssl_ctx, secure_opts->ca_file, secure_opts->ca_path) != 1) { + stcp_report_error("sockettcp: SSL_CTX_load_verify_locations error: %s " "ssl_verify_peer requires setting " "either ssl_ca_path or ssl_ca_file. Is any of them " "present in " "the .conf file?", - stcp_ssl_error() ); - return 0; - } - - if ( peer_certificate_optional ) { - SSL_CTX_set_verify( ssl_ctx, SSL_VERIFY_PEER, NULL); - } - else { - SSL_CTX_set_verify( ssl_ctx, - SSL_VERIFY_PEER | - SSL_VERIFY_FAIL_IF_NO_PEER_CERT, - NULL ); - } - - if ( use_default_verify_paths && - ( SSL_CTX_set_default_verify_paths( ssl_ctx ) != 1 ) ) { - stcp_report_error( "sockettcp: SSL_CTX_set_default_verify_paths error: %s", - stcp_ssl_error() ); - return 0; - } - - SSL_CTX_set_verify_depth( ssl_ctx, STCP_SSL_VERIFY_DEPTH ); + stcp_ssl_error()); + return 0; + } + if (peer_certificate_optional) { + SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); + } + else { + SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); } - if ( SSL_CTX_set_cipher_list( ssl_ctx, STCP_SSL_CIPHER_LIST ) != 1 ) { - stcp_report_error( "sockettcp: SSL_CTX_set_cipher_list error: %s", stcp_ssl_error() ); + if (use_default_verify_paths && (SSL_CTX_set_default_verify_paths(ssl_ctx) != 1)) { + stcp_report_error("sockettcp: SSL_CTX_set_default_verify_paths error: %s", stcp_ssl_error()); + return 0; } - return 1; + SSL_CTX_set_verify_depth(ssl_ctx, STCP_SSL_VERIFY_DEPTH); + } + + if (SSL_CTX_set_cipher_list(ssl_ctx, STCP_SSL_CIPHER_LIST) != 1) { + stcp_report_error("sockettcp: SSL_CTX_set_cipher_list error: %s", stcp_ssl_error()); + } + + return 1; } //////////////////////////////////////////////////////////////////////////////// @@ -2312,58 +2200,58 @@ stcp_init_ssl( SSL_CTX *ssl_ctx, struct stcp_secure_options *secure_opts ) // void -stcp_uninit_ssl( void ) +stcp_uninit_ssl(void) { #ifdef OPENSSL_API_1_1 - if ( 0 == atomic_dec( &cryptolib_users ) ) { + if (0 == atomic_dec(&cryptolib_users)) { - // Shutdown according to - // https://wiki.openssl.org/index.php/Library_Initialization#Cleanup - // http://stackoverflow.com/questions/29845527/how-to-properly-uninitialize-openssl - // - CONF_modules_unload(1); + // Shutdown according to + // https://wiki.openssl.org/index.php/Library_Initialization#Cleanup + // http://stackoverflow.com/questions/29845527/how-to-properly-uninitialize-openssl + // + CONF_modules_unload(1); #else - int i; - - if ( 0 == atomic_dec( &cryptolib_users ) ) { - - // Shutdown according to - // https://wiki.openssl.org/index.php/Library_Initialization#Cleanup - // http://stackoverflow.com/questions/29845527/how-to-properly-uninitialize-openssl - // - CRYPTO_set_locking_callback(NULL); - CRYPTO_set_id_callback(NULL); - ENGINE_cleanup(); - CONF_modules_unload(1); - ERR_free_strings(); - EVP_cleanup(); - CRYPTO_cleanup_all_ex_data(); -#ifdef OPENSSL_API_1_0 - ERR_remove_state(0); // deprecated in 1.0.0, solved by going to 1.1.0 -#endif - - for (i = 0; i < CRYPTO_num_locks(); i++) { - pthread_mutex_destroy( &ssl_mutexes[i] ); - } - free(ssl_mutexes); - ssl_mutexes = NULL; + int i; + + if (0 == atomic_dec(&cryptolib_users)) { + + // Shutdown according to + // https://wiki.openssl.org/index.php/Library_Initialization#Cleanup + // http://stackoverflow.com/questions/29845527/how-to-properly-uninitialize-openssl + // + CRYPTO_set_locking_callback(NULL); + CRYPTO_set_id_callback(NULL); + ENGINE_cleanup(); + CONF_modules_unload(1); + ERR_free_strings(); + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); +#if OPENSSL_VERSION_NUMBER >= 0x10000000L && OPENSSL_VERSION_NUMBER < 0x10100000L + ERR_remove_thread_state(NULL); +#elif OPENSSL_VERSION_NUMBER < 0x10000000L + ERR_remove_state(0); +#endif + + for (i = 0; i < CRYPTO_num_locks(); i++) { + pthread_mutex_destroy(&ssl_mutexes[i]); + } + free(ssl_mutexes); + ssl_mutexes = NULL; #endif // OPENSSL_API_1_1 - } + } } - //////////////////////////////////////////////////////////////////////////////// // is_valid_port // static int -is_valid_port( unsigned long port ) +is_valid_port(unsigned long port) { - return (port <= 0xffff); + return (port <= 0xffff); } - //////////////////////////////////////////////////////////////////////////////// // stcp_inet_pton // @@ -2374,73 +2262,69 @@ is_valid_port( unsigned long port ) // static int -stcp_inet_pton( int af, const char *src, void *dst, size_t dstlen ) +stcp_inet_pton(int af, const char *src, void *dst, size_t dstlen) { - struct addrinfo hints, *res, *ressave; - int func_ret = 0; - int gai_ret; - - memset(&hints, 0, sizeof (struct addrinfo)); - hints.ai_family = af; - - gai_ret = getaddrinfo(src, NULL, &hints, &res); - if (gai_ret != 0) { - - // gai_strerror could be used to convert gai_ret to a string - // POSIX return values: see - // http://pubs.opengroup.org/onlinepubs/9699919799/functions/freeaddrinfo.html - // - // Windows return values: see - // https://msdn.microsoft.com/en-us/library/windows/desktop/ms738520%28v=vs.85%29.aspx - // - return 0; - } + struct addrinfo hints, *res, *ressave; + int func_ret = 0; + int gai_ret; - ressave = res; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = af; - while (res) { + gai_ret = getaddrinfo(src, NULL, &hints, &res); + if (gai_ret != 0) { - if (dstlen >= (size_t) res->ai_addrlen) { - memcpy(dst, res->ai_addr, res->ai_addrlen); - func_ret = 1; - } + // gai_strerror could be used to convert gai_ret to a string + // POSIX return values: see + // http://pubs.opengroup.org/onlinepubs/9699919799/functions/freeaddrinfo.html + // + // Windows return values: see + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms738520%28v=vs.85%29.aspx + // + return 0; + } - res = res->ai_next; + ressave = res; + + while (res) { + + if (dstlen >= (size_t) res->ai_addrlen) { + memcpy(dst, res->ai_addr, res->ai_addrlen); + func_ret = 1; } - freeaddrinfo(ressave); + res = res->ai_next; + } - return func_ret; -} + freeaddrinfo(ressave); + return func_ret; +} //////////////////////////////////////////////////////////////////////////////// // set_tcp_nodelay // static int -set_tcp_nodelay( int sock, int nodelay_on ) +set_tcp_nodelay(int sock, int nodelay_on) { - if ( setsockopt( sock, - IPPROTO_TCP, - TCP_NODELAY, - (SOCK_OPT_TYPE) &nodelay_on, - sizeof( nodelay_on ) ) != 0 ) { - // Error - return 1; - } + if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (SOCK_OPT_TYPE) &nodelay_on, sizeof(nodelay_on)) != 0) { + // Error + return 1; + } - /// OK - return 0; + /// OK + return 0; } //////////////////////////////////////////////////////////////////////////////// // stcp_new_connection // -struct stcp_connection * stcp_new_connection(void) +struct stcp_connection * +stcp_new_connection(void) { - return (struct stcp_connection *)calloc( 1, sizeof( struct stcp_connection ) ); + return (struct stcp_connection *) calloc(1, sizeof(struct stcp_connection)); } //////////////////////////////////////////////////////////////////////////////// @@ -2448,119 +2332,107 @@ struct stcp_connection * stcp_new_connection(void) // static int -stcp_connect_socket( const char *hostip, - int port, - int use_ssl, - int *sock, // output: socket, must not be NULL - union usa *sa // output: socket address, must not be NULL - ) -{ - int ip_ver = 0; - *sock = INVALID_SOCKET; - memset(sa, 0, sizeof (*sa)); - - if ( NULL == hostip ) { - stcp_report_error( "sockettcp: NULL host" ); - return 0; - } +stcp_connect_socket(const char *hostip, + int port, + int use_ssl, + int *sock, // output: socket, must not be NULL + union usa *sa // output: socket address, must not be NULL +) +{ + int ip_ver = 0; + *sock = INVALID_SOCKET; + memset(sa, 0, sizeof(*sa)); + + if (NULL == hostip) { + stcp_report_error("sockettcp: NULL host"); + return 0; + } - if ( (port <= 0) || !is_valid_port((unsigned) port)) { - stcp_report_error("sockettcp: invalid port"); - return 0; - } + if ((port <= 0) || !is_valid_port((unsigned) port)) { + stcp_report_error("sockettcp: invalid port"); + return 0; + } - (void)use_ssl; + (void) use_ssl; - if ( stcp_inet_pton( AF_INET, hostip, &sa->sin, sizeof( sa->sin ) ) ) { // .sin_addr - sa->sin.sin_family = AF_INET; - sa->sin.sin_port = htons((uint16_t) port); - ip_ver = 4; - } - else if ( stcp_inet_pton( AF_INET6, hostip, &sa->sin6, sizeof( sa->sin6 ) ) ) { // .sin6_addr + if (stcp_inet_pton(AF_INET, hostip, &sa->sin, sizeof(sa->sin))) { // .sin_addr + sa->sin.sin_family = AF_INET; + sa->sin.sin_port = htons((uint16_t) port); + ip_ver = 4; + } + else if (stcp_inet_pton(AF_INET6, hostip, &sa->sin6, sizeof(sa->sin6))) { // .sin6_addr + sa->sin6.sin6_family = AF_INET6; + sa->sin6.sin6_port = htons((uint16_t) port); + ip_ver = 6; + } + else if (hostip[0] == '[') { + // While getaddrinfo on Windows will work with [::1], + // getaddrinfo on Linux only works with ::1 (without []). + size_t l = strlen(hostip + 1); +#ifndef WIN32 + char *h = (l > 1) ? strdup(hostip + 1) : NULL; +#else + char *h = (l > 1) ? __strdup(hostip + 1) : NULL; +#endif + if (h) { + h[l - 1] = 0; + if (stcp_inet_pton(AF_INET6, h, &sa->sin6, sizeof(sa->sin6))) { // .sin6_addr sa->sin6.sin6_family = AF_INET6; - sa->sin6.sin6_port = htons((uint16_t) port); - ip_ver = 6; + sa->sin6.sin6_port = htons((uint16_t) port); + ip_ver = 6; + } + free(h); } - else if ( hostip[0] == '[' ) { - // While getaddrinfo on Windows will work with [::1], - // getaddrinfo on Linux only works with ::1 (without []). - size_t l = strlen(hostip + 1); -#ifndef WIN32 - char *h = (l > 1) ? strdup(hostip + 1) : NULL; -#else - char *h = (l > 1) ? __strdup(hostip + 1) : NULL; -#endif - if ( h ) { - h[l - 1] = 0; - if ( stcp_inet_pton(AF_INET6, h, &sa->sin6, sizeof( sa->sin6 ) ) ) { // .sin6_addr - sa->sin6.sin6_family = AF_INET6; - sa->sin6.sin6_port = htons((uint16_t) port); - ip_ver = 6; - } - free(h); - } + } - } + if (0 == ip_ver) { + stcp_report_error("sockettcp: host not found"); + return 0; + } - if ( 0 == ip_ver ) { - stcp_report_error("sockettcp: host not found"); - return 0; - } - - if ( 4 == ip_ver ) { - *sock = (int)socket(PF_INET, SOCK_STREAM, 0); - } - else if ( 6 == ip_ver ) { - *sock = (int)socket(PF_INET6, SOCK_STREAM, 0); - } + if (4 == ip_ver) { + *sock = (int) socket(PF_INET, SOCK_STREAM, 0); + } + else if (6 == ip_ver) { + *sock = (int) socket(PF_INET6, SOCK_STREAM, 0); + } - if (*sock == INVALID_SOCKET) { - stcp_report_error("sockettcp: socket(): Invalid "); - // strerror(ERRNO) ); - return 0; - } + if (*sock == INVALID_SOCKET) { + stcp_report_error("sockettcp: socket(): Invalid "); + // strerror(ERRNO) ); + return 0; + } - if ( ( 4 == ip_ver ) && - ( 0 == connect( *sock, - (struct sockaddr *)&sa->sin, - sizeof(sa->sin) ) ) ) { - // connected with IPv4 - if ( 0 == set_non_blocking_mode( *sock ) ) { - return 1; // Ok - } - - // failed - // TODO: specific error message - + if ((4 == ip_ver) && (0 == connect(*sock, (struct sockaddr *) &sa->sin, sizeof(sa->sin)))) { + // connected with IPv4 + if (0 == set_non_blocking_mode(*sock)) { + return 1; // Ok } - if ( ( 6 == ip_ver ) && - ( 0 == connect( *sock, - (struct sockaddr *)&sa->sin6, - sizeof(sa->sin6) ) ) ) { - // connected with IPv6 - if ( 0 == set_non_blocking_mode(*sock) ) { - return 1; // Ok - } - - // failed - // TODO: specific error message + // failed + // TODO: specific error message + } + if ((6 == ip_ver) && (0 == connect(*sock, (struct sockaddr *) &sa->sin6, sizeof(sa->sin6)))) { + // connected with IPv6 + if (0 == set_non_blocking_mode(*sock)) { + return 1; // Ok } - // Not connected - stcp_report_error("sockettcp: connect(%s:%d): %s", - hostip, - port, - strerror( ERRNO ) ); + // failed + // TODO: specific error message + } + + // Not connected + stcp_report_error("sockettcp: connect(%s:%d): %s", hostip, port, strerror(ERRNO)); #ifdef _WIN32 - closesocket(*sock); + closesocket(*sock); #else - close(*sock); + close(*sock); #endif - *sock = INVALID_SOCKET; + *sock = INVALID_SOCKET; - return 0; + return 0; } //////////////////////////////////////////////////////////////////////////////// @@ -2568,148 +2440,131 @@ stcp_connect_socket( const char *hostip, // static struct stcp_connection * -stcp_connect_remote_impl( const char *host, - int port, - struct stcp_secure_options *secure_options, - int bUseSSL, - int /*timeout*/ ) -{ - struct stcp_connection *conn = NULL; - int sock; - union usa sa; - struct sockaddr *psa; - socklen_t len; - - // Need secure options if SSL - if ( bUseSSL && ( NULL == secure_options ) ) { - return 0; - } +stcp_connect_remote_impl(const char *host, + int port, + struct stcp_secure_options *secure_options, + int bUseSSL, + int /*timeout*/) +{ + struct stcp_connection *conn = NULL; + int sock; + union usa sa; + struct sockaddr *psa; + socklen_t len; + + // Need secure options if SSL + if (bUseSSL && (NULL == secure_options)) { + return 0; + } - conn = (struct stcp_connection *)calloc( 1, sizeof( struct stcp_connection ) ); - if ( NULL == conn ) { - // Error - return NULL; - } + conn = (struct stcp_connection *) calloc(1, sizeof(struct stcp_connection)); + if (NULL == conn) { + // Error + return NULL; + } - if ( bUseSSL ) { - // Init SSL subsystem - if ( 0 == stcp_init_ssl( conn->ssl_ctx, secure_options ) ) { - free( conn ); - return NULL; - } + if (bUseSSL) { + // Init SSL subsystem + if (0 == stcp_init_ssl(conn->ssl_ctx, secure_options)) { + free(conn); + return NULL; } + } - if ( !stcp_connect_socket( host, - port, - bUseSSL, - &sock, - &sa ) ) { - // ebuf is set by connect_socket, - // free all memory and return NULL; - free( conn ); - return NULL; - } + if (!stcp_connect_socket(host, port, bUseSSL, &sock, &sa)) { + // ebuf is set by connect_socket, + // free all memory and return NULL; + free(conn); + return NULL; + } #ifdef OPENSSL_API_1_1 - if ( bUseSSL && - ( NULL == ( conn->ssl_ctx = SSL_CTX_new( TLS_client_method() ) ) ) ) { - stcp_report_error("sockettcp: SSL_CTX_new error"); - close(sock); - free(conn); - return NULL; - } + if (bUseSSL && (NULL == (conn->ssl_ctx = SSL_CTX_new(TLS_client_method())))) { + stcp_report_error("sockettcp: SSL_CTX_new error"); + close(sock); + free(conn); + return NULL; + } #else - if ( bUseSSL && - ( NULL == ( conn->ssl_ctx = SSL_CTX_new( SSLv23_client_method() ) ) ) ) { - unsigned long ssl_err = ERR_get_error(); - const char* const str = ERR_reason_error_string( ssl_err ); - stcp_report_error("sockettcp: SSL_CTX_new error. %s", str ); + if (bUseSSL && (NULL == (conn->ssl_ctx = SSL_CTX_new(SSLv23_client_method())))) { + unsigned long ssl_err = ERR_get_error(); + const char *const str = ERR_reason_error_string(ssl_err); + stcp_report_error("sockettcp: SSL_CTX_new error. %s", str); #ifdef _WIN32 - closesocket(sock); + closesocket(sock); #else - close(sock); + close(sock); #endif - free(conn); - return NULL; - } + free(conn); + return NULL; + } #endif // OPENSSL_API_1_1 - len = ( sa.sa.sa_family == AF_INET) ? sizeof( conn->client.rsa.sin ) - : sizeof( conn->client.rsa.sin6 ); - psa = ( sa.sa.sa_family == AF_INET ) - ? (struct sockaddr *)&( conn->client.rsa.sin ) - : (struct sockaddr *)&( conn->client.rsa.sin6 ); + len = (sa.sa.sa_family == AF_INET) ? sizeof(conn->client.rsa.sin) : sizeof(conn->client.rsa.sin6); + psa = (sa.sa.sa_family == AF_INET) ? (struct sockaddr *) &(conn->client.rsa.sin) + : (struct sockaddr *) &(conn->client.rsa.sin6); - conn->client.sock = sock; - conn->client.lsa = sa; + conn->client.sock = sock; + conn->client.lsa = sa; - if ( getsockname(sock, psa, &len) != 0 ) { - stcp_report_error( "sockettcp: getsockname() failed: %s", strerror( ERRNO ) ); - } + if (getsockname(sock, psa, &len) != 0) { + stcp_report_error("sockettcp: getsockname() failed: %s", strerror(ERRNO)); + } - conn->client.is_ssl = bUseSSL ? 1 : 0; + conn->client.is_ssl = bUseSSL ? 1 : 0; - if ( bUseSSL ) { + if (bUseSSL) { - // TODO: Check ssl_verify_peer and ssl_ca_path here. - // SSL_CTX_set_verify call is needed to switch off server - // certificate checking, which is off by default in OpenSSL and - // on in yaSSL. - // TODO: SSL_CTX_set_verify(conn->client_ssl_ctx, - // SSL_VERIFY_PEER, verify_ssl_server); + // TODO: Check ssl_verify_peer and ssl_ca_path here. + // SSL_CTX_set_verify call is needed to switch off server + // certificate checking, which is off by default in OpenSSL and + // on in yaSSL. + // TODO: SSL_CTX_set_verify(conn->client_ssl_ctx, + // SSL_VERIFY_PEER, verify_ssl_server); - if ( secure_options->client_cert_path ) { - if ( !ssl_use_pem_file( conn->ssl_ctx, - secure_options->client_cert_path, - NULL ) ) { - stcp_report_error( "sockettcp: Can not use SSL client certificate" ); - SSL_CTX_free( conn->ssl_ctx ); + if (secure_options->client_cert_path) { + if (!ssl_use_pem_file(conn->ssl_ctx, secure_options->client_cert_path, NULL)) { + stcp_report_error("sockettcp: Can not use SSL client certificate"); + SSL_CTX_free(conn->ssl_ctx); #ifdef _WIN32 - closesocket(sock); + closesocket(sock); #else - close(sock); + close(sock); #endif - free( conn ); - return NULL; - } - } + free(conn); + return NULL; + } + } - // Set default locations for trusted CA certificates (file in pem format) - if ( secure_options->server_cert_path ) { - SSL_CTX_load_verify_locations( conn->ssl_ctx, - secure_options->server_cert_path, - NULL ); - SSL_CTX_set_verify( conn->ssl_ctx, SSL_VERIFY_PEER, NULL ); - } - else { - SSL_CTX_set_verify( conn->ssl_ctx, SSL_VERIFY_NONE, NULL ); - } + // Set default locations for trusted CA certificates (file in pem format) + if (secure_options->server_cert_path) { + SSL_CTX_load_verify_locations(conn->ssl_ctx, secure_options->server_cert_path, NULL); + SSL_CTX_set_verify(conn->ssl_ctx, SSL_VERIFY_PEER, NULL); + } + else { + SSL_CTX_set_verify(conn->ssl_ctx, SSL_VERIFY_NONE, NULL); + } - if ( !make_ssl( conn, - secure_options, - conn->ssl_ctx, - SSL_connect, - &(conn->stop_flag ) ) ) { - stcp_report_error("sockettcp: SSL connection error"); - SSL_CTX_free( conn->ssl_ctx ); + if (!make_ssl(conn, secure_options, conn->ssl_ctx, SSL_connect, &(conn->stop_flag))) { + stcp_report_error("sockettcp: SSL connection error"); + SSL_CTX_free(conn->ssl_ctx); #ifdef _WIN32 - closesocket(sock); + closesocket(sock); #else - close(sock); + close(sock); #endif - free( conn ); - return NULL; - } - + free(conn); + return NULL; } + } - if ( 0 != set_non_blocking_mode( sock ) ) { - // TODO: handle error - ; - } + if (0 != set_non_blocking_mode(sock)) { + // TODO: handle error + ; + } - conn->conn_state = STCP_CONN_STATE_CONNECTED; - return conn; + conn->conn_state = STCP_CONN_STATE_CONNECTED; + return conn; } //////////////////////////////////////////////////////////////////////////////// @@ -2717,20 +2572,13 @@ stcp_connect_remote_impl( const char *host, // struct stcp_connection * -stcp_connect_remote_secure( const char *host, - int port, - struct stcp_secure_options *client_options, - int timeout ) +stcp_connect_remote_secure(const char *host, int port, struct stcp_secure_options *client_options, int timeout) { - struct stcp_connection *conn; - - conn = stcp_connect_remote_impl( host, - port, - client_options, - USE_SSL, - timeout ); - - return conn; + struct stcp_connection *conn; + + conn = stcp_connect_remote_impl(host, port, client_options, USE_SSL, timeout); + + return conn; } //////////////////////////////////////////////////////////////////////////////// @@ -2738,65 +2586,56 @@ stcp_connect_remote_secure( const char *host, // struct stcp_connection * -stcp_connect_remote( const char *host, - int port, - int timeout ) -{ - return stcp_connect_remote_impl( host, - port, - NULL, - NO_SSL, - timeout ); +stcp_connect_remote(const char *host, int port, int timeout) +{ + return stcp_connect_remote_impl(host, port, NULL, NO_SSL, timeout); } - //////////////////////////////////////////////////////////////////////////////// // stcp_close_socket_gracefully // static void -stcp_close_socket_gracefully( struct stcp_connection *conn ) +stcp_close_socket_gracefully(struct stcp_connection *conn) { #if defined(_WIN32) - char buf[STCP_BUF_LEN]; - int n; + char buf[STCP_BUF_LEN]; + int n; #endif - struct linger linger; - int error_code = 0; - int linger_timeout = -2; - socklen_t opt_len = sizeof (error_code); - - if ( NULL == conn ) { - return; - } + struct linger linger; + int error_code = 0; + int linger_timeout = -2; + socklen_t opt_len = sizeof(error_code); - // http://msdn.microsoft.com/en-us/library/ms739165(v=vs.85).aspx: - // "Note that enabling a nonzero timeout on a nonblocking socket - // is not recommended.", so set it to blocking now - set_blocking_mode( conn->client.sock ); + if (NULL == conn) { + return; + } - /// Send FIN to the client - shutdown(conn->client.sock, SHUTDOWN_WR); + // http://msdn.microsoft.com/en-us/library/ms739165(v=vs.85).aspx: + // "Note that enabling a nonzero timeout on a nonblocking socket + // is not recommended.", so set it to blocking now + set_blocking_mode(conn->client.sock); + /// Send FIN to the client + shutdown(conn->client.sock, SHUTDOWN_WR); #if defined(_WIN32) - // Read and discard pending incoming data. If we do not do that and - // close - // the socket, the data in the send buffer may be discarded. This - // behaviour is seen on Windows, when client keeps sending data - // when server decides to close the connection; then when client - // does recv() it gets no data back. - do { - n = stcp_pull_inner( NULL, conn, buf, sizeof( buf ), /* Timeout in s: */ 1.0 ); - } - while (n > 0); -#endif - - // Set linger option according to configuration - if (STCP_LINGER_TIMEOUT >= 0) { - // Set linger option to avoid socket hanging out after close. This - // prevent ephemeral port exhaust problem under high QPS. - linger.l_onoff = 1; + // Read and discard pending incoming data. If we do not do that and + // close + // the socket, the data in the send buffer may be discarded. This + // behaviour is seen on Windows, when client keeps sending data + // when server decides to close the connection; then when client + // does recv() it gets no data back. + do { + n = stcp_pull_inner(NULL, conn, buf, sizeof(buf), /* Timeout in s: */ 1.0); + } while (n > 0); +#endif + + // Set linger option according to configuration + if (STCP_LINGER_TIMEOUT >= 0) { + // Set linger option to avoid socket hanging out after close. This + // prevent ephemeral port exhaust problem under high QPS. + linger.l_onoff = 1; #if defined(_MSC_VER) #pragma warning(push) @@ -2806,10 +2645,10 @@ stcp_close_socket_gracefully( struct stcp_connection *conn ) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wconversion" #endif - // Data type of linger structure elements may differ, - // so we don't know what cast we need here. - // Disable type conversion warnings. - linger.l_linger = (linger_timeout + 999) / 1000; + // Data type of linger structure elements may differ, + // so we don't know what cast we need here. + // Disable type conversion warnings. + linger.l_linger = (linger_timeout + 999) / 1000; #if defined(__GNUC__) || defined(__MINGW32__) #pragma GCC diagnostic pop @@ -2817,53 +2656,42 @@ stcp_close_socket_gracefully( struct stcp_connection *conn ) #if defined(_MSC_VER) #pragma warning(pop) #endif + } + else { + linger.l_onoff = 0; + linger.l_linger = 0; + } - } - else { - linger.l_onoff = 0; - linger.l_linger = 0; - } - - if (STCP_LINGER_TIMEOUT < -1) { - // Default: don't configure any linger - } - else if ( getsockopt( conn->client.sock, - SOL_SOCKET, - SO_ERROR, - (char *) &error_code, - &opt_len) != 0 ) { - // Cannot determine if socket is already closed. This should - // not occur and never did in a test. Log an error message - // and continue. - stcp_report_error("sockettcp: getsockopt(SOL_SOCKET SO_ERROR) failed: %s", - strerror(ERRNO) ); - } - else if ( error_code == ECONNRESET ) { - // Socket already closed by client/peer, close socket without linger - } - else { - - // Set linger timeout - if ( setsockopt( conn->client.sock, - SOL_SOCKET, - SO_LINGER, - (char *)&linger, - sizeof( linger ) ) != 0 ) { - stcp_report_error("sockettcp: setsockopt(SOL_SOCKET SO_LINGER(%i,%i)) failed: %s", - linger.l_onoff, - linger.l_linger, - strerror( ERRNO ) ); - } + if (STCP_LINGER_TIMEOUT < -1) { + // Default: don't configure any linger + } + else if (getsockopt(conn->client.sock, SOL_SOCKET, SO_ERROR, (char *) &error_code, &opt_len) != 0) { + // Cannot determine if socket is already closed. This should + // not occur and never did in a test. Log an error message + // and continue. + stcp_report_error("sockettcp: getsockopt(SOL_SOCKET SO_ERROR) failed: %s", strerror(ERRNO)); + } + else if (error_code == ECONNRESET) { + // Socket already closed by client/peer, close socket without linger + } + else { + // Set linger timeout + if (setsockopt(conn->client.sock, SOL_SOCKET, SO_LINGER, (char *) &linger, sizeof(linger)) != 0) { + stcp_report_error("sockettcp: setsockopt(SOL_SOCKET SO_LINGER(%i,%i)) failed: %s", + linger.l_onoff, + linger.l_linger, + strerror(ERRNO)); } + } - // Now we know that our FIN is ACK-ed, safe to close + // Now we know that our FIN is ACK-ed, safe to close #ifdef _WIN32 - closesocket( conn->client.sock ); + closesocket(conn->client.sock); #else - close( conn->client.sock ); + close(conn->client.sock); #endif - conn->client.sock = INVALID_SOCKET; + conn->client.sock = INVALID_SOCKET; } //////////////////////////////////////////////////////////////////////////////// @@ -2871,38 +2699,43 @@ stcp_close_socket_gracefully( struct stcp_connection *conn ) // static void -close_connection( struct stcp_connection *conn ) +close_connection(struct stcp_connection *conn) { - if ( STCP_CONN_STATE_CONNECTED != conn->conn_state ) { - conn->conn_state = STCP_CONN_STATE_CLOSED; // closed - return; - } + if (STCP_CONN_STATE_CONNECTED != conn->conn_state) { + conn->conn_state = STCP_CONN_STATE_CLOSED; // closed + return; + } - conn->conn_state = STCP_CONN_STATE_TOCLOSE; // to close + conn->conn_state = STCP_CONN_STATE_TOCLOSE; // to close - // Set close flag, so keep-alive loops will stop - conn->must_close = 1; + // Set close flag, so keep-alive loops will stop + conn->must_close = 1; - conn->conn_state = STCP_CONN_STATE_CLOSING; // closing + conn->conn_state = STCP_CONN_STATE_CLOSING; // closing - if ( conn->ssl != NULL ) { - // Run SSL_shutdown twice to ensure complexly close SSL connection - SSL_shutdown( conn->ssl ); - SSL_free( conn->ssl ); - // Avoid CRYPTO_cleanup_all_ex_data(); See discussion: - // https://wiki.openssl.org/index.php/Talk:Library_Initialization -#ifdef OPENSSL_API_1_0 - ERR_remove_state(0); // deprecated in 1.0.0, solved by going to 1.1.0 + if (conn->ssl != NULL) { + // Run SSL_shutdown twice to ensure complexly close SSL connection + SSL_shutdown(conn->ssl); + SSL_free(conn->ssl); + // Avoid CRYPTO_cleanup_all_ex_data(); See discussion: + // https://wiki.openssl.org/index.php/Talk:Library_Initialization +// #ifdef OPENSSL_API_1_0 +// ERR_remove_state(0); // deprecated in 1.0.0, solved by going to 1.1.0 +// #endif +#if OPENSSL_VERSION_NUMBER >= 0x10000000L && OPENSSL_VERSION_NUMBER < 0x10100000L + ERR_remove_thread_state(NULL); +#elif OPENSSL_VERSION_NUMBER < 0x10000000L + ERR_remove_state(0); #endif - conn->ssl = NULL; - } + conn->ssl = NULL; + } - if ( conn->client.sock != INVALID_SOCKET ) { - stcp_close_socket_gracefully( conn ); - conn->client.sock = INVALID_SOCKET; - } + if (conn->client.sock != INVALID_SOCKET) { + stcp_close_socket_gracefully(conn); + conn->client.sock = INVALID_SOCKET; + } - conn->conn_state = STCP_CONN_STATE_CLOSED; // closed + conn->conn_state = STCP_CONN_STATE_CLOSED; // closed } //////////////////////////////////////////////////////////////////////////////// @@ -2910,21 +2743,20 @@ close_connection( struct stcp_connection *conn ) // void -stcp_close_connection( struct stcp_connection *conn ) +stcp_close_connection(struct stcp_connection *conn) { - if ( NULL == conn ) { - return; - } + if (NULL == conn) { + return; + } - close_connection( conn ); + close_connection(conn); - if ( conn->ssl_ctx != NULL ) { - SSL_CTX_free( (SSL_CTX *)conn->ssl_ctx ); - } + if (conn->ssl_ctx != NULL) { + SSL_CTX_free((SSL_CTX *) conn->ssl_ctx); + } - // If client free connection data - free( conn ); - + // If client free connection data + free(conn); } /*! @@ -2938,7 +2770,7 @@ stcp_close_connection( struct stcp_connection *conn ) * @param milliseconds - Time to wait for data on file decriptor. * If set too zero STCP_TIMEOUT_QUANTUM is used. If timout is * set lower than quantum one timeout is performed. If greater several. - * @param stop_server - Pointer to int that can be set externally + * @param stop_server - Pointer to int that can be set externally * to stop the blocking operation. * * @return >0 success, -1 timeout, -2 stopped @@ -2947,51 +2779,46 @@ stcp_close_connection( struct stcp_connection *conn ) #ifndef _WIN32 int -stcp_poll( struct pollfd *pfd, - unsigned int n, - int mstimeout, - volatile int *stop_server ) +stcp_poll(struct pollfd *pfd, unsigned int n, int mstimeout, volatile int *stop_server) { - // Call poll, but only for a maximum time of a few seconds. - // This will allow to stop the server after some seconds, instead - // of having to wait for a long socket timeout. - int ms_now = STCP_TIMEOUT_QUANTUM; // Sleep quantum in ms - - do { - - int result; - - if ( *stop_server ) { - // Shut down signal - return -2; - } + // Call poll, but only for a maximum time of a few seconds. + // This will allow to stop the server after some seconds, instead + // of having to wait for a long socket timeout. + int ms_now = STCP_TIMEOUT_QUANTUM; // Sleep quantum in ms - // Set mstimeout to lowest value - if ( ( mstimeout >= 0 ) && - ( mstimeout < ms_now ) ) { - ms_now = mstimeout; - } + do { - result = poll( pfd, n, ms_now ); - if ( result != 0 ) { - // On success, a positive number is returned; this is the number of - // structures which have nonzero revents fields. Negative return value - // is error. Forward both to the caller. (0 == timeout) - return result; - } - - // Poll timed out (==0) + int result; - // Poll returned timeout (0). - if ( mstimeout > 0 ) { - mstimeout -= ms_now; - } + if (*stop_server) { + // Shut down signal + return -2; + } + // Set mstimeout to lowest value + if ((mstimeout >= 0) && (mstimeout < ms_now)) { + ms_now = mstimeout; } - while ( mstimeout > 0 ); - // timeout: return 0 - return 0; + result = poll(pfd, n, ms_now); + if (result != 0) { + // On success, a positive number is returned; this is the number of + // structures which have nonzero revents fields. Negative return value + // is error. Forward both to the caller. (0 == timeout) + return result; + } + + // Poll timed out (==0) + + // Poll returned timeout (0). + if (mstimeout > 0) { + mstimeout -= ms_now; + } + + } while (mstimeout > 0); + + // timeout: return 0 + return 0; } #endif @@ -3009,172 +2836,167 @@ stcp_poll( struct pollfd *pfd, // static int -stcp_push_inner( struct stcp_connection *conn, - FILE *fp, - const char *buf, - int len, - double timeout ) +stcp_push_inner(struct stcp_connection *conn, FILE *fp, const char *buf, int len, double timeout) { - uint64_t start = 0, now = 0, timeout_ns = 0; - int n, err; - unsigned ms_wait = STCP_TIMEOUT_QUANTUM; // Sleep quantum in ms + uint64_t start = 0, now = 0, timeout_ns = 0; + int n, err; + unsigned ms_wait = STCP_TIMEOUT_QUANTUM; // Sleep quantum in ms #ifdef _WIN32 - typedef int len_t; + typedef int len_t; #else - typedef size_t len_t; + typedef size_t len_t; #endif - if ( timeout > 0 ) { - now = stcp_get_current_time_ns(); - start = now; - timeout_ns = (uint64_t) (timeout * 1.0E9); - } + if (timeout > 0) { + now = stcp_get_current_time_ns(); + start = now; + timeout_ns = (uint64_t) (timeout * 1.0E9); + } - if (conn == NULL) { - return -2; - } + if (conn == NULL) { + return -2; + } - // Try to read until it succeeds, fails, times out, or the server - // shuts down. - for (;;) { - - if ( conn->ssl != NULL ) { - - n = SSL_write(conn->ssl, buf, len); - if (n <= 0) { - err = SSL_get_error(conn->ssl, n); - if ( ( SSL_ERROR_SYSCALL == err ) && ( -1 == n ) ) { - err = errno; - } - else if (( err == SSL_ERROR_WANT_READ ) || - ( err == SSL_ERROR_WANT_WRITE ) ) { - n = 0; - } - else { - //DEBUG_TRACE("SSL_write() failed, error %d", err); - return -2; - } - } - else { - err = 0; - } - } - else + // Try to read until it succeeds, fails, times out, or the server + // shuts down. + for (;;) { - if (fp != NULL) { - n = (int) fwrite(buf, 1, (size_t) len, fp); - if (ferror(fp)) { - n = -1; - err = errno; - } - else { - err = 0; - } + if (conn->ssl != NULL) { + + n = SSL_write(conn->ssl, buf, len); + if (n <= 0) { + err = SSL_get_error(conn->ssl, n); + if ((SSL_ERROR_SYSCALL == err) && (-1 == n)) { + err = errno; + } + else if ((err == SSL_ERROR_WANT_READ) || (err == SSL_ERROR_WANT_WRITE)) { + n = 0; } else { - n = (int)send( conn->client.sock, buf, (len_t)len, MSG_NOSIGNAL ); - err = (n < 0) ? errno : 0; + // DEBUG_TRACE("SSL_write() failed, error %d", err); + return -2; + } + } + else { + err = 0; + } + } + else + + if (fp != NULL) { + n = (int) fwrite(buf, 1, (size_t) len, fp); + if (ferror(fp)) { + n = -1; + err = errno; + } + else { + err = 0; + } + } + else { + n = (int) send(conn->client.sock, buf, (len_t) len, MSG_NOSIGNAL); + err = (n < 0) ? errno : 0; #ifdef _WIN32 - if (err == WSAEWOULDBLOCK) { - err = 0; - n = 0; - } - else if (err == EPIPE ) { // Broken pipe - conn->conn_state = STCP_CONN_STATE_UNDEFINED; - return -2; - } + if (err == WSAEWOULDBLOCK) { + err = 0; + n = 0; + } + else if (err == EPIPE) { // Broken pipe + conn->conn_state = STCP_CONN_STATE_UNDEFINED; + return -2; + } #else - if (err == EWOULDBLOCK) { - err = 0; - n = 0; - } - else if (err == EPIPE ) { // Broken pipe - conn->conn_state = STCP_CONN_STATE_UNDEFINED; - return -2; - } + if (err == EWOULDBLOCK) { + err = 0; + n = 0; + } + else if (err == EPIPE) { // Broken pipe + conn->conn_state = STCP_CONN_STATE_UNDEFINED; + return -2; + } #endif - if (n < 0) { - // shutdown of the socket at client side - return -2; - } - } + if (n < 0) { + // shutdown of the socket at client side + return -2; + } + } - if (conn->stop_flag) { - return -2; - } + if (conn->stop_flag) { + return -2; + } - if ( (n > 0) || ((n == 0) && (len == 0) ) ) { - // some data has been read, or no data was requested - return n; - } - if ( n < 0 ) { - // socket error - check errno - //DEBUG_TRACE("send() failed, error %d", err); - - // TODO (mid): error handling depending on the error code. - // These codes are different between Windows and Linux. - // Currently there is no problem with failing send calls, - // if there is a reproducible situation, it should be - // investigated in detail. - return -2; - } + if ((n > 0) || ((n == 0) && (len == 0))) { + // some data has been read, or no data was requested + return n; + } + if (n < 0) { + // socket error - check errno + // DEBUG_TRACE("send() failed, error %d", err); - // Only in case n=0 (timeout), repeat calling the write function + // TODO (mid): error handling depending on the error code. + // These codes are different between Windows and Linux. + // Currently there is no problem with failing send calls, + // if there is a reproducible situation, it should be + // investigated in detail. + return -2; + } - // If send failed, wait before retry - if ( fp != NULL ) { - // For files, just wait a fixed time, - // maybe an average disk seek time. - stcp_sleep(ms_wait > 10 ? 10 : ms_wait); - } - else { - // For sockets, wait for the socket using select - fd_set wfds; - struct timeval tv; - int sret; + // Only in case n=0 (timeout), repeat calling the write function + + // If send failed, wait before retry + if (fp != NULL) { + // For files, just wait a fixed time, + // maybe an average disk seek time. + stcp_sleep(ms_wait > 10 ? 10 : ms_wait); + } + else { + // For sockets, wait for the socket using select + fd_set wfds; + struct timeval tv; + int sret; #if defined(__GNUC__) || defined(__MINGW32__) - // GCC seems to have a flaw with it's own socket macros: - // http://www.linuxquestions.org/questions/programming-9/impossible-to-use-gcc-with-wconversion-and-standard-socket-macros-841935/ - // + // GCC seems to have a flaw with it's own socket macros: + // http://www.linuxquestions.org/questions/programming-9/impossible-to-use-gcc-with-wconversion-and-standard-socket-macros-841935/ + // #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wsign-conversion" #endif - FD_ZERO(&wfds); - FD_SET( conn->client.sock, &wfds); - tv.tv_sec = (time_t) (ms_wait / 1000); - tv.tv_usec = (long) ((ms_wait % 1000) * 1000); + FD_ZERO(&wfds); + FD_SET(conn->client.sock, &wfds); + tv.tv_sec = (time_t) (ms_wait / 1000); + tv.tv_usec = (long) ((ms_wait % 1000) * 1000); - sret = select( (int)conn->client.sock + 1, NULL, &wfds, NULL, &tv ); + sret = select((int) conn->client.sock + 1, NULL, &wfds, NULL, &tv); #if defined(__GNUC__) || defined(__MINGW32__) #pragma GCC diagnostic pop #endif - if ( sret > 0 ) { - // We got ready to write. Don't check the timeout - // but directly go back to write again. - continue; - } - } + if (sret > 0) { + // We got ready to write. Don't check the timeout + // but directly go back to write again. + continue; + } + } - if (timeout > 0 ) { - now = stcp_get_current_time_ns(); - if ( (now - start) > timeout_ns ) { - // Timeout - break; - } - } + if (timeout > 0) { + now = stcp_get_current_time_ns(); + if ((now - start) > timeout_ns) { + // Timeout + break; + } } + } - (void)err; /* - Avoid unused warning if NO_SSL is set and DEBUG_TRACE - is not used - */ + (void) err; /* + Avoid unused warning if NO_SSL is set and DEBUG_TRACE + is not used + */ - return -1; + return -1; } //////////////////////////////////////////////////////////////////////////////// @@ -3182,37 +3004,30 @@ stcp_push_inner( struct stcp_connection *conn, // static int64_t -stcp_push_all( struct stcp_connection *conn, - FILE *fp, - const char *buf, - int64_t len ) +stcp_push_all(struct stcp_connection *conn, FILE *fp, const char *buf, int64_t len) { - int64_t n, nwritten = 0; + int64_t n, nwritten = 0; - while ( ( len > 0 ) && ( conn->stop_flag == 0 ) ) { + while ((len > 0) && (conn->stop_flag == 0)) { - n = stcp_push_inner( conn, - fp, - buf + nwritten, - (int)len, - STCP_WRITE_TIMEOUT ); + n = stcp_push_inner(conn, fp, buf + nwritten, (int) len, STCP_WRITE_TIMEOUT); - if (n < 0) { - if (nwritten == 0) { - nwritten = n; /* Propagate the error */ - } - break; - } - else if (n == 0) { - break; /* No more data to write */ - } - else { - nwritten += n; - len -= n; - } + if (n < 0) { + if (nwritten == 0) { + nwritten = n; /* Propagate the error */ + } + break; + } + else if (n == 0) { + break; /* No more data to write */ } + else { + nwritten += n; + len -= n; + } + } - return nwritten; + return nwritten; } //////////////////////////////////////////////////////////////////////////////// @@ -3226,210 +3041,198 @@ stcp_push_all( struct stcp_connection *conn, // static int -stcp_pull_inner( FILE *fp, - struct stcp_connection *conn, - char *buf, - int len, - int mstimeout ) +stcp_pull_inner(FILE *fp, struct stcp_connection *conn, char *buf, int len, int mstimeout) { - int nread, err = 0; + int nread, err = 0; #ifdef _WIN32 - typedef int len_t; + typedef int len_t; #else - typedef size_t len_t; + typedef size_t len_t; #endif - int ssl_pending; + int ssl_pending; + // We need an additional wait loop around this, because in some cases + // with TLSwe may get data from the socket but not from SSL_read. + // In this case we need to repeat at least once. - // We need an additional wait loop around this, because in some cases - // with TLSwe may get data from the socket but not from SSL_read. - // In this case we need to repeat at least once. - - if ( fp != NULL ) { - // Use read() instead of fread(), because if we're reading from the - // CGI pipe, fread() may block until IO buffer is filled up. We - // cannot afford to block and must pass all read bytes immediately - // to the client. - nread = (int)read( fileno(fp), buf, (size_t)len ); - err = (nread < 0) ? errno : 0; - - if ( ( 0 == nread ) && ( len > 0 ) ) { - // Should get data, but got EOL - return -2; - } + if (fp != NULL) { + // Use read() instead of fread(), because if we're reading from the + // CGI pipe, fread() may block until IO buffer is filled up. We + // cannot afford to block and must pass all read bytes immediately + // to the client. + nread = (int) read(fileno(fp), buf, (size_t) len); + err = (nread < 0) ? errno : 0; + if ((0 == nread) && (len > 0)) { + // Should get data, but got EOL + return -2; + } + } + else if ((conn->ssl != NULL) && ((ssl_pending = SSL_pending(conn->ssl)) > 0)) { + // We already know there is no more data buffered in conn->buf + // but there is more available in the SSL layer. So don't poll + // conn->client.sock yet. + if (ssl_pending > len) { + ssl_pending = len; } - else if ( ( conn->ssl != NULL) && - ( ( ssl_pending = SSL_pending(conn->ssl) ) > 0 ) ) { - // We already know there is no more data buffered in conn->buf - // but there is more available in the SSL layer. So don't poll - // conn->client.sock yet. - if (ssl_pending > len) { - ssl_pending = len; - } - - nread = SSL_read( conn->ssl, buf, ssl_pending ); - if ( nread <= 0 ) { - err = SSL_get_error(conn->ssl, nread); - if ((err == SSL_ERROR_SYSCALL) && (nread == -1)) { - err = errno; - } - else if ( ( err == SSL_ERROR_WANT_READ ) || - ( err == SSL_ERROR_WANT_WRITE ) ) { - nread = 0; - } - else { - //DEBUG_TRACE("SSL_read() failed, error %d", err); - return -1; - } - } - else { - err = 0; - } + nread = SSL_read(conn->ssl, buf, ssl_pending); + if (nread <= 0) { + err = SSL_get_error(conn->ssl, nread); + if ((err == SSL_ERROR_SYSCALL) && (nread == -1)) { + err = errno; + } + else if ((err == SSL_ERROR_WANT_READ) || (err == SSL_ERROR_WANT_WRITE)) { + nread = 0; + } + else { + // DEBUG_TRACE("SSL_read() failed, error %d", err); + return -1; + } } + else { + err = 0; + } + } - // SSL read - else if ( conn->ssl != NULL ) { + // SSL read + else if (conn->ssl != NULL) { - struct pollfd pfd[1]; - int pollres; + struct pollfd pfd[1]; + int pollres; - pfd[0].fd = conn->client.sock; - pfd[0].events = POLLIN; - pollres = stcp_poll( pfd, 1, mstimeout, &(conn->stop_flag) ); + pfd[0].fd = conn->client.sock; + pfd[0].events = POLLIN; + pollres = stcp_poll(pfd, 1, mstimeout, &(conn->stop_flag)); - if ( conn->stop_flag ) { - return -2; - } + if (conn->stop_flag) { + return -2; + } - if ( pollres > 0 ) { - - nread = SSL_read( conn->ssl, buf, len ); - - if ( nread <= 0 ) { - err = SSL_get_error( conn->ssl, nread ); - if ( ( err == SSL_ERROR_SYSCALL ) && ( nread == -1 ) ) { - err = errno; - } - else if ( ( err == SSL_ERROR_WANT_READ ) || - ( err == SSL_ERROR_WANT_WRITE ) ) { - nread = 0; - } - else { - //DEBUG_TRACE("SSL_read() failed, error %d", err); TODO - return -2; - } - } - else { - err = 0; - } + if (pollres > 0) { + + nread = SSL_read(conn->ssl, buf, len); + if (nread <= 0) { + err = SSL_get_error(conn->ssl, nread); + if ((err == SSL_ERROR_SYSCALL) && (nread == -1)) { + err = errno; } - else if ( pollres < 0 ) { - // Error - return -2; + else if ((err == SSL_ERROR_WANT_READ) || (err == SSL_ERROR_WANT_WRITE)) { + nread = 0; } else { - // pollres = 0 means timeout - nread = 0; + // DEBUG_TRACE("SSL_read() failed, error %d", err); TODO + return -2; } - + } + else { + err = 0; + } + } + else if (pollres < 0) { + // Error + return -2; } - - // Non SSL read else { - struct pollfd pfd[1]; - int pollres; + // pollres = 0 means timeout + nread = 0; + } + } - pfd[0].fd = conn->client.sock; - pfd[0].events = POLLIN; - pollres = stcp_poll( pfd, 1, mstimeout, &( conn->stop_flag ) ); + // Non SSL read + else { + struct pollfd pfd[1]; + int pollres; - if ( conn->stop_flag ) { - return -2; - } + pfd[0].fd = conn->client.sock; + pfd[0].events = POLLIN; + pollres = stcp_poll(pfd, 1, mstimeout, &(conn->stop_flag)); - if ( pollres > 0 ) { - - nread = (int)recv( conn->client.sock, buf, (len_t)len, 0 ); - err = (nread < 0) ? errno : 0; - - if ( nread <= 0 ) { - // shutdown of the socket at client side - return -2; - } - } - else if ( pollres < 0 ) { - // error callint poll - return -2; - } - else { - // pollres = 0 means timeout - nread = 0; - } + if (conn->stop_flag) { + return -2; } - if ( conn->stop_flag ) { + if (pollres > 0) { + + nread = (int) recv(conn->client.sock, buf, (len_t) len, 0); + err = (nread < 0) ? errno : 0; + + if (nread <= 0) { + // shutdown of the socket at client side return -2; + } } - - if ( ( nread > 0 ) || ( ( nread == 0 ) && ( len == 0 ) ) ) { - // some data has been read, or no data was requested - return nread; + else if (pollres < 0) { + // error callint poll + return -2; + } + else { + // pollres = 0 means timeout + nread = 0; } + } + + if (conn->stop_flag) { + return -2; + } - if ( nread < 0 ) { - // socket error - check errno + if ((nread > 0) || ((nread == 0) && (len == 0))) { + // some data has been read, or no data was requested + return nread; + } + + if (nread < 0) { + // socket error - check errno #ifdef _WIN32 - if (err == WSAEWOULDBLOCK) { - // TODO (low): check if this is still required - // standard case if called from close_socket_gracefully - return -2; - } - else if (err == WSAETIMEDOUT) { - // TODO (low): check if this is still required - // timeout is handled by the while loop - return 0; - } - else if (err == WSAECONNABORTED) { - // See https://www.chilkatsoft.com/p/p_299.asp - return -2; - } - else { - //DEBUG_TRACE("recv() failed, error %d", err); - return -2; - } + if (err == WSAEWOULDBLOCK) { + // TODO (low): check if this is still required + // standard case if called from close_socket_gracefully + return -2; + } + else if (err == WSAETIMEDOUT) { + // TODO (low): check if this is still required + // timeout is handled by the while loop + return 0; + } + else if (err == WSAECONNABORTED) { + // See https://www.chilkatsoft.com/p/p_299.asp + return -2; + } + else { + // DEBUG_TRACE("recv() failed, error %d", err); + return -2; + } #else - // TODO: POSIX returns either EAGAIN or EWOULDBLOCK in both cases, - // if the timeout is reached and if the socket was set to non- - // blocking in close_socket_gracefully, so we can not distinguish - // here. We have to wait for the timeout in both cases for now. - - if ( ( err == EAGAIN ) || ( err == EWOULDBLOCK ) || ( err == EINTR ) ) { - // TODO (low): check if this is still required - // EAGAIN/EWOULDBLOCK: - // standard case if called from close_socket_gracefully - // => should return -1 - // or timeout occurred - // => the code must stay in the while loop - - // EINTR can be generated on a socket with a timeout set even - // when SA_RESTART is effective for all relevant signals - // (see signal(7)). - // => stay in the while loop - } - else { - //DEBUG_TRACE("recv() failed, error %d", err); - return -2; - } -#endif + // TODO: POSIX returns either EAGAIN or EWOULDBLOCK in both cases, + // if the timeout is reached and if the socket was set to non- + // blocking in close_socket_gracefully, so we can not distinguish + // here. We have to wait for the timeout in both cases for now. + + if ((err == EAGAIN) || (err == EWOULDBLOCK) || (err == EINTR)) { + // TODO (low): check if this is still required + // EAGAIN/EWOULDBLOCK: + // standard case if called from close_socket_gracefully + // => should return -1 + // or timeout occurred + // => the code must stay in the while loop + + // EINTR can be generated on a socket with a timeout set even + // when SA_RESTART is effective for all relevant signals + // (see signal(7)). + // => stay in the while loop } + else { + // DEBUG_TRACE("recv() failed, error %d", err); + return -2; + } +#endif + } - // Timeout occurred, but no data available. - return -1; + // Timeout occurred, but no data available. + return -1; } //////////////////////////////////////////////////////////////////////////////// @@ -3437,58 +3240,57 @@ stcp_pull_inner( FILE *fp, // // Read len bytes within timout set by conn.timeout // Will read until timeout or error/abort. -// +// // >= 0 Read data // < 0 - Error // // set mstimeout to -1 for no mstimeout static int -stcp_pull_all( FILE *fp, struct stcp_connection *conn, char *buf, int len, int mstimeout ) +stcp_pull_all(FILE *fp, struct stcp_connection *conn, char *buf, int len, int mstimeout) { - int n, nread = 0; - uint64_t start_time = 0, now = 0, timeout_ns = 0; + int n, nread = 0; + uint64_t start_time = 0, now = 0, timeout_ns = 0; - // Set timeout - if ( mstimeout >= 0 ) { - start_time = stcp_get_current_time_ns(); - timeout_ns = (uint64_t)((double)mstimeout * 1.0E6); - } + // Set timeout + if (mstimeout >= 0) { + start_time = stcp_get_current_time_ns(); + timeout_ns = (uint64_t) ((double) mstimeout * 1.0E6); + } - while ( ( len > 0 ) && ( 0 == conn->stop_flag ) ) { + while ((len > 0) && (0 == conn->stop_flag)) { - n = stcp_pull_inner( fp, conn, buf + nread, len, mstimeout ); - - if ( STCP_ERROR_STOPPED == n ) { - if ( 0 == nread ) { - nread = -1; // Propagate the error - } - break; - } - else if ( STCP_ERROR_TIMEOUT == n ) { - // timeout - if ( mstimeout >= 0 ) { - now = stcp_get_current_time_ns(); - if ( (now - start_time) <= timeout_ns ) { - continue; - } - } - break; - } - else if ( n == 0 ) { - break; // No more data to read - } - else { - nread += n; - len -= n; + n = stcp_pull_inner(fp, conn, buf + nread, len, mstimeout); + + if (STCP_ERROR_STOPPED == n) { + if (0 == nread) { + nread = -1; // Propagate the error + } + break; + } + else if (STCP_ERROR_TIMEOUT == n) { + // timeout + if (mstimeout >= 0) { + now = stcp_get_current_time_ns(); + if ((now - start_time) <= timeout_ns) { + continue; } + } + break; + } + else if (n == 0) { + break; // No more data to read + } + else { + nread += n; + len -= n; + } - } // while + } // while - return nread; + return nread; } - //////////////////////////////////////////////////////////////////////////////// // stcp_read_inner // @@ -3497,38 +3299,36 @@ stcp_pull_all( FILE *fp, struct stcp_connection *conn, char *buf, int len, int m // static int -stcp_read_inner( struct stcp_connection *conn, void *buf, size_t len, int mstimeout ) +stcp_read_inner(struct stcp_connection *conn, void *buf, size_t len, int mstimeout) { - int64_t n, nread; - int64_t len64 = - (int64_t) ((len > INT_MAX) ? INT_MAX : len); // since the return value is - // int, we may not read more - // bytes - if ( conn == NULL ) { - return -1; - } + int64_t n, nread; + int64_t len64 = (int64_t) ((len > INT_MAX) ? INT_MAX : len); // since the return value is + // int, we may not read more + // bytes + if (conn == NULL) { + return -1; + } - nread = 0; - - // Read new data from the remote socket. - if ( ( n = stcp_pull_all( NULL, conn, (char *)buf, (int)len64, mstimeout ) ) >= 0 ) { - nread += n; - } - else { - // (nread > 0) is always false here (https://github.com/BlueAndi) - // TODO: check if this is still required in next major version - nread = ( (nread > 0) ? nread : n); - } - - // Disconnected= - if ( -2 == nread) { - if (errno == EPIPE ) { // Broken pipe - conn->conn_state = STCP_CONN_STATE_UNDEFINED; - } + nread = 0; + + // Read new data from the remote socket. + if ((n = stcp_pull_all(NULL, conn, (char *) buf, (int) len64, mstimeout)) >= 0) { + nread += n; + } + else { + // (nread > 0) is always false here (https://github.com/BlueAndi) + // TODO: check if this is still required in next major version + nread = ((nread > 0) ? nread : n); } - return (int)nread; + // Disconnected= + if (-2 == nread) { + if (errno == EPIPE) { // Broken pipe + conn->conn_state = STCP_CONN_STATE_UNDEFINED; + } + } + return (int) nread; } //////////////////////////////////////////////////////////////////////////////// @@ -3536,19 +3336,18 @@ stcp_read_inner( struct stcp_connection *conn, void *buf, size_t len, int mstime // char -stcp_getc( struct stcp_connection *conn ) +stcp_getc(struct stcp_connection *conn) { - char c; - if ( NULL == conn ) { - return 0; - } - - if ( stcp_read_inner( conn, &c, 1, 0 ) <= 0 ) { - return (char) 0; - } + char c; + if (NULL == conn) { + return 0; + } - return c; + if (stcp_read_inner(conn, &c, 1, 0) <= 0) { + return (char) 0; + } + return c; } //////////////////////////////////////////////////////////////////////////////// @@ -3556,19 +3355,19 @@ stcp_getc( struct stcp_connection *conn ) // int -stcp_read( struct stcp_connection *conn, void *buf, size_t len, int mstimeout ) +stcp_read(struct stcp_connection *conn, void *buf, size_t len, int mstimeout) { - if ( len > INT_MAX ) { - len = INT_MAX; - } + if (len > INT_MAX) { + len = INT_MAX; + } - if ( ( conn == NULL ) || ( NULL == buf ) ) { - return -1; - } - - memset( buf, 0, len ); + if ((conn == NULL) || (NULL == buf)) { + return -1; + } + + memset(buf, 0, len); - return stcp_read_inner( conn, buf, len, mstimeout ); + return stcp_read_inner(conn, buf, len, mstimeout); } //////////////////////////////////////////////////////////////////////////////// @@ -3576,50 +3375,44 @@ stcp_read( struct stcp_connection *conn, void *buf, size_t len, int mstimeout ) // int -stcp_write( struct stcp_connection *conn, const void *buf, size_t len ) +stcp_write(struct stcp_connection *conn, const void *buf, size_t len) { - int64_t total; + int64_t total; - if ( ( NULL == conn ) || ( NULL == buf ) ) { - return 0; - } - - if ( 0 == len ) return 0; - - total = stcp_push_all( conn, - NULL, - (const char *)buf, - (int64_t)len); - - return (int)total; -} + if ((NULL == conn) || (NULL == buf)) { + return 0; + } + + if (0 == len) + return 0; + total = stcp_push_all(conn, NULL, (const char *) buf, (int64_t) len); + + return (int) total; +} // ----------------------------------------------------------------------------- // S E R V E R // ----------------------------------------------------------------------------- - - void -stcp_close_all_listening_sockets( struct server_context *srv_ctx ) +stcp_close_all_listening_sockets(struct server_context *srv_ctx) { - unsigned int i; - if ( !srv_ctx ) { - return; - } + unsigned int i; + if (!srv_ctx) { + return; + } - for ( i = 0; i < srv_ctx->num_listening_sockets; i++ ) { - closesocket( srv_ctx->listening_sockets[i].sock ); - srv_ctx->listening_sockets[i].sock = INVALID_SOCKET; - } - - free( srv_ctx->listening_sockets ); - srv_ctx->listening_sockets = NULL; - free( srv_ctx->listening_socket_fds ); - srv_ctx->listening_socket_fds = NULL; -} + for (i = 0; i < srv_ctx->num_listening_sockets; i++) { + closesocket(srv_ctx->listening_sockets[i].sock); + srv_ctx->listening_sockets[i].sock = INVALID_SOCKET; + } + free(srv_ctx->listening_sockets); + srv_ctx->listening_sockets = NULL; + free(srv_ctx->listening_socket_fds); + srv_ctx->listening_socket_fds = NULL; +} //////////////////////////////////////////////////////////////////////////////// // next_option @@ -3633,62 +3426,58 @@ stcp_close_all_listening_sockets( struct server_context *srv_ctx ) // static const char * -next_option( const char *list, struct msg *val, struct msg *eq_val ) +next_option(const char *list, struct msg *val, struct msg *eq_val) { - int end; + int end; reparse: - if (val == NULL || list == NULL || *list == '\0') { - // End of the list - return NULL; - } - - // Skip over leading LWS - while (*list == ' ' || *list == '\t') { - list++; - } - - val->ptr = list; - if ( ( list = strchr(val->ptr, ',') ) != NULL ) { - // Comma found. Store length and shift the list ptr - val->len = ((size_t)(list - val->ptr)); - list++; - } - else { - // This value is the last one - list = val->ptr + strlen(val->ptr); - val->len = ((size_t)(list - val->ptr)); - - // Adjust length for trailing LWS - end = (int)val->len - 1; - while ( ( end >= 0 ) && - ( ( val->ptr[end] == ' ' ) || - ( val->ptr[end] == '\t') ) ) - end--; - val->len = (size_t)(end + 1); - - if ( 0 == val->len ) { - // Ignore any empty entries. - goto reparse; - } - - if ( eq_val != NULL ) { - // Value has form "x=y", adjust pointers and lengths - // so that val points to "x", and eq_val points to "y". - eq_val->len = 0; - eq_val->ptr = (const char *)memchr(val->ptr, '=', val->len); - if ( eq_val->ptr != NULL ) { - eq_val->ptr++; // Skip over '=' character - eq_val->len = ((size_t)(val->ptr - eq_val->ptr)) + val->len; - val->len = ((size_t)(eq_val->ptr - val->ptr)) - 1; - } + if (val == NULL || list == NULL || *list == '\0') { + // End of the list + return NULL; + } - } + // Skip over leading LWS + while (*list == ' ' || *list == '\t') { + list++; + } + val->ptr = list; + if ((list = strchr(val->ptr, ',')) != NULL) { + // Comma found. Store length and shift the list ptr + val->len = ((size_t) (list - val->ptr)); + list++; + } + else { + // This value is the last one + list = val->ptr + strlen(val->ptr); + val->len = ((size_t) (list - val->ptr)); + + // Adjust length for trailing LWS + end = (int) val->len - 1; + while ((end >= 0) && ((val->ptr[end] == ' ') || (val->ptr[end] == '\t'))) + end--; + val->len = (size_t) (end + 1); + + if (0 == val->len) { + // Ignore any empty entries. + goto reparse; + } + + if (eq_val != NULL) { + // Value has form "x=y", adjust pointers and lengths + // so that val points to "x", and eq_val points to "y". + eq_val->len = 0; + eq_val->ptr = (const char *) memchr(val->ptr, '=', val->len); + if (eq_val->ptr != NULL) { + eq_val->ptr++; // Skip over '=' character + eq_val->len = ((size_t) (val->ptr - eq_val->ptr)) + val->len; + val->len = ((size_t) (eq_val->ptr - val->ptr)) - 1; + } } + } - return list; + return list; } //////////////////////////////////////////////////////////////////////////////// @@ -3711,551 +3500,482 @@ next_option( const char *list, struct msg *val, struct msg *eq_val ) // msg - [in] A message chunk // so - [out] IP Address // ip_version - [out] Version of IP-address (4/6/0==failure) -// +// // Returns: 0 = failure. 1 == success static int -parse_port_string( const struct msg *msg, - struct socket *so, - int *ip_version ) -{ - unsigned int a, b, c, d, port; - int ch, len; - const char *cb; - char buf[100] = {0}; - - // MacOS needs that. If we do not zero it, subsequent bind() will fail. - // Also, all-zeroes in the socket address means binding to all addresses - // for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT). - memset( so, 0, sizeof (*so) ); - so->lsa.sin.sin_family = AF_INET; +parse_port_string(const struct msg *msg, struct socket *so, int *ip_version) +{ + unsigned int a, b, c, d, port; + int ch, len; + const char *cb; + char buf[100] = { 0 }; + + // MacOS needs that. If we do not zero it, subsequent bind() will fail. + // Also, all-zeroes in the socket address means binding to all addresses + // for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT). + memset(so, 0, sizeof(*so)); + so->lsa.sin.sin_family = AF_INET; + *ip_version = 0; + + // Initialize port and len as invalid. + port = 0; + len = 0; + + // Test for different ways to format this string + if (5 == sscanf(msg->ptr, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len)) { + + // Bind to a specific IPv4 address, e.g. 192.168.1.5:8080 + so->lsa.sin.sin_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d); + so->lsa.sin.sin_port = htons((uint16_t) port); + *ip_version = 4; + } + else if ((2 == sscanf(msg->ptr, "[%49[^]]]:%u%n", buf, &port, &len)) && + stcp_inet_pton(AF_INET6, buf, &so->lsa.sin6, sizeof(so->lsa.sin6))) { + + // IPv6 address, examples: see above + // so->lsa.sin6.sin6_family = AF_INET6; already set by web_inet_pton + so->lsa.sin6.sin6_port = htons((uint16_t) port); + *ip_version = 6; + } + else if ((msg->ptr[0] == '+') && (1 == sscanf(msg->ptr + 1, "%u%n", &port, &len))) { + + // Port is specified with a +, bind to IPv6 and IPv4, INADDR_ANY + // Add 1 to len for the + character we skipped before + len++; + + // Set socket family to IPv6, do not use IPV6_V6ONLY + so->lsa.sin6.sin6_family = AF_INET6; + so->lsa.sin6.sin6_port = htons((uint16_t) port); + *ip_version = 4 + 6; + } + else if (sscanf(msg->ptr, "%u%n", &port, &len) == 1) { + // If only port is specified, bind to IPv4, INADDR_ANY + so->lsa.sin.sin_port = htons((uint16_t) port); + *ip_version = 4; + } + else if ((cb = strchr(msg->ptr, ':')) != NULL) { + // Could be a hostname + // Will only work for RFC 952 compliant hostnames, + // starting with a letter, containing only letters, + // digits and hyphen ('-'). Newer specs may allow + // more, but this is not guaranteed here, since it + // may interfere with rules for port option lists. + + *(char *) cb = 0; // Use a const cast here and modify the string. + // We are going to restore the string later. + + if (stcp_inet_pton(AF_INET, msg->ptr, &so->lsa.sin, sizeof(so->lsa.sin))) { + if (1 == sscanf(cb + 1, "%u%n", &port, &len)) { + *ip_version = 4; + so->lsa.sin.sin_family = AF_INET; + so->lsa.sin.sin_port = htons((uint16_t) port); + len += (int) (cb - msg->ptr) + 1; + } + else { + port = 0; + len = 0; + } + } + else if (stcp_inet_pton(AF_INET6, msg->ptr, &so->lsa.sin6, sizeof(so->lsa.sin6))) { + if (1 == sscanf(cb + 1, "%u%n", &port, &len)) { + *ip_version = 6; + so->lsa.sin6.sin6_family = AF_INET6; + so->lsa.sin.sin_port = htons((uint16_t) port); + len += (int) (cb - msg->ptr) + 1; + } + else { + port = 0; + len = 0; + } + } + + *(char *) cb = ':'; // restore the string + } + else { + // Parsing failure. + } + + // sscanf and the option splitting code ensure the following condition + if ((len < 0) && ((unsigned) len > (unsigned) msg->len)) { *ip_version = 0; + return 0; + } - // Initialize port and len as invalid. - port = 0; - len = 0; + ch = msg->ptr[len]; /* Next character after the port number */ + so->is_ssl = (ch == 's'); + // so->ssl_redir = (ch == 'r'); - // Test for different ways to format this string - if ( 5 == sscanf( msg->ptr, - "%u.%u.%u.%u:%u%n", - &a, &b, &c, &d, &port, &len ) ) { + // Make sure the port is valid and vector ends with 's', 'r' or ',' + if (is_valid_port(port) && ((ch == '\0') || (ch == 's') || (ch == 'r') || (ch == ','))) { + return 1; + } - // Bind to a specific IPv4 address, e.g. 192.168.1.5:8080 - so->lsa.sin.sin_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d ); - so->lsa.sin.sin_port = htons((uint16_t) port); - *ip_version = 4; + // Reset ip_version to 0 of there is an error + *ip_version = 0; + return 0; +} - } - else if ( ( 2 == sscanf( msg->ptr, "[%49[^]]]:%u%n", buf, &port, &len ) ) && - stcp_inet_pton( AF_INET6, buf, - &so->lsa.sin6, - sizeof(so->lsa.sin6) ) ) { +//////////////////////////////////////////////////////////////////////////////// +// init_listening_port +// - // IPv6 address, examples: see above - // so->lsa.sin6.sin6_family = AF_INET6; already set by web_inet_pton - so->lsa.sin6.sin6_port = htons((uint16_t) port); - *ip_version = 6; +int +stcp_listening(struct server_context *srv_ctx, const char *str_listening_port) +{ + const char *list; + int on = 1; + int off = 0; + struct msg msg; + struct socket so, *ptr; - } - else if ( ( msg->ptr[0] == '+') && - ( 1 == sscanf(msg->ptr + 1, "%u%n", &port, &len ) ) ) { + struct pollfd *pfd; + union usa usa; + socklen_t len; + int ip_version; - // Port is specified with a +, bind to IPv6 and IPv4, INADDR_ANY - // Add 1 to len for the + character we skipped before - len++; + int portsTotal = 0; + int portsOk = 0; - // Set socket family to IPv6, do not use IPV6_V6ONLY - so->lsa.sin6.sin6_family = AF_INET6; - so->lsa.sin6.sin6_port = htons((uint16_t) port); - *ip_version = 4 + 6; + /* Check pointers */ + if ((NULL == srv_ctx) || (NULL == str_listening_port)) { + return 0; + } - } - else if ( sscanf(msg->ptr, "%u%n", &port, &len ) == 1 ) { - // If only port is specified, bind to IPv4, INADDR_ANY - so->lsa.sin.sin_port = htons( (uint16_t) port ); - *ip_version = 4; - } - else if ( (cb = strchr(msg->ptr, ':')) != NULL ) { - // Could be a hostname - // Will only work for RFC 952 compliant hostnames, - // starting with a letter, containing only letters, - // digits and hyphen ('-'). Newer specs may allow - // more, but this is not guaranteed here, since it - // may interfere with rules for port option lists. - - *(char *)cb = 0; // Use a const cast here and modify the string. - // We are going to restore the string later. - - if ( stcp_inet_pton( AF_INET, - msg->ptr, - &so->lsa.sin, - sizeof (so->lsa.sin) ) ) { - if ( 1 == sscanf(cb + 1, "%u%n", &port, &len) ) { - *ip_version = 4; - so->lsa.sin.sin_family = AF_INET; - so->lsa.sin.sin_port = htons((uint16_t) port); - len += (int) (cb - msg->ptr) + 1; - } - else { - port = 0; - len = 0; - } + /* Init. defaults */ - } - else if ( stcp_inet_pton( AF_INET6, - msg->ptr, - &so->lsa.sin6, - sizeof( so->lsa.sin6 ) ) ) { - if ( 1 == sscanf( cb + 1, "%u%n", &port, &len ) ) { - *ip_version = 6; - so->lsa.sin6.sin6_family = AF_INET6; - so->lsa.sin.sin_port = htons( (uint16_t)port ); - len += (int)(cb - msg->ptr) + 1; - } - else { - port = 0; - len = 0; - } + memset(&so, 0, sizeof(so)); + memset(&usa, 0, sizeof(usa)); + len = sizeof(usa); + list = str_listening_port; - } + msg.ptr = str_listening_port; + msg.len = strlen(str_listening_port); - *(char *)cb = ':'; // restore the string + while ((list = next_option(list, &msg, NULL)) != NULL) { - } - else { - // Parsing failure. - } + portsTotal++; - // sscanf and the option splitting code ensure the following condition - if ( (len < 0) && ((unsigned) len > (unsigned) msg->len)) { - *ip_version = 0; - return 0; + if (!parse_port_string(&msg, &so, &ip_version)) { + stcp_report_error("%.*s: invalid port spec. Expecting list of: %s", + (int) msg.len, + msg.ptr, + "[IP_ADDRESS:]PORT[s]"); + return 0; } - ch = msg->ptr[len]; /* Next character after the port number */ - so->is_ssl = (ch == 's'); - //so->ssl_redir = (ch == 'r'); + if (so.is_ssl && (NULL == srv_ctx->ssl_ctx)) { + stcp_report_error("sockettcp: Cannot add SSL socket. Is -ssl_certificate " + "option set?"); + return 0; + } - // Make sure the port is valid and vector ends with 's', 'r' or ',' - if ( is_valid_port( port ) && - ( (ch == '\0') || (ch == 's') || (ch == 'r') || (ch == ',') ) ) { - return 1; + if (INVALID_SOCKET == (so.sock = (int) socket(so.lsa.sa.sa_family, SOCK_STREAM, 6))) { + stcp_report_error("sockettcp: cannot create socket"); + return 0; } - // Reset ip_version to 0 of there is an error - *ip_version = 0; - return 0; -} +#ifdef _WIN32 -//////////////////////////////////////////////////////////////////////////////// -// init_listening_port -// + // Windows SO_REUSEADDR lets many procs binds to a + // socket, SO_EXCLUSIVEADDRUSE makes the bind fail + // if someone already has the socket -- DTL */ + // NOTE: If SO_EXCLUSIVEADDRUSE is used, + // Windows might need a few seconds before + // the same port can be used again in the + // same process, so a short Sleep may be + // required between web_stop and web_start. + // + if (setsockopt(so.sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (SOCK_OPT_TYPE) &on, sizeof(on)) != 0) { -int -stcp_listening( struct server_context *srv_ctx, - const char *str_listening_port ) -{ - const char *list; - int on = 1; - int off = 0; - struct msg msg; - struct socket so, *ptr; - - struct pollfd *pfd; - union usa usa; - socklen_t len; - int ip_version; - - int portsTotal = 0; - int portsOk = 0; - - /* Check pointers */ - if ( ( NULL == srv_ctx ) || - ( NULL == str_listening_port ) ) { - return 0; + // Set reuse option, but don't abort on errors. + stcp_report_error("sockettcp: cannot set socket option SO_EXCLUSIVEADDRUSE"); } +#else + if (setsockopt(so.sock, SOL_SOCKET, SO_REUSEADDR, ((SOCK_OPT_TYPE) &on), sizeof(on)) != 0) { - /* Init. defaults */ + // Set reuse option, but don't abort on errors. + stcp_report_error("sockettcp: cannot set socket option SO_REUSEADDR "); + } +#endif - memset(&so, 0, sizeof(so)); - memset( &usa, 0, sizeof( usa ) ); - len = sizeof( usa ); - list = str_listening_port; + if (ip_version > 4) { /* Could be 6 for IPv6 only or 10 (4+6) for IPv4+IPv6 */ - msg.ptr = str_listening_port; - msg.len = strlen( str_listening_port ); - - while ( ( list = next_option( list, &msg, NULL) ) != NULL ) { + if (ip_version > 6) { - portsTotal++; + if ((AF_INET6 == so.lsa.sa.sa_family) && + setsockopt(so.sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &off, sizeof(off)) != 0) { - if ( !parse_port_string( &msg, &so, &ip_version ) ) { - stcp_report_error( "%.*s: invalid port spec. Expecting list of: %s", - (int) msg.len, - msg.ptr, - "[IP_ADDRESS:]PORT[s]"); - return 0; + /* Set IPv6 only option, but don't abort on errors. */ + stcp_report_error("sockettcp: cannot set socket option IPV6_V6ONLY=off (entry %i)", portsTotal); } + } + else { - if ( so.is_ssl && ( NULL == srv_ctx->ssl_ctx ) ) { - stcp_report_error( "sockettcp: Cannot add SSL socket. Is -ssl_certificate " - "option set?" ); - return 0; - } + if ((AF_INET6 == so.lsa.sa.sa_family) && + (setsockopt(so.sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &off, sizeof(off)) != 0)) { - if ( INVALID_SOCKET == - ( so.sock = (int)socket( so.lsa.sa.sa_family, SOCK_STREAM, 6 ) ) ) { - stcp_report_error( "sockettcp: cannot create socket" ); - return 0; + // Set IPv6 only option, but don't abort on errors. + stcp_report_error("sockettcp: cannot set socket option IPV6_V6ONLY"); } + } + } -#ifdef _WIN32 - - // Windows SO_REUSEADDR lets many procs binds to a - // socket, SO_EXCLUSIVEADDRUSE makes the bind fail - // if someone already has the socket -- DTL */ - // NOTE: If SO_EXCLUSIVEADDRUSE is used, - // Windows might need a few seconds before - // the same port can be used again in the - // same process, so a short Sleep may be - // required between web_stop and web_start. - // - if ( setsockopt(so.sock, - SOL_SOCKET, - SO_EXCLUSIVEADDRUSE, - (SOCK_OPT_TYPE) & on, - sizeof(on) ) != 0) { - - // Set reuse option, but don't abort on errors. - stcp_report_error( "sockettcp: cannot set socket option SO_EXCLUSIVEADDRUSE" ); - } -#else - if ( setsockopt( so.sock, - SOL_SOCKET, - SO_REUSEADDR, - ( (SOCK_OPT_TYPE)&on ), - sizeof( on ) ) != 0 ) { - - // Set reuse option, but don't abort on errors. - stcp_report_error( "sockettcp: cannot set socket option SO_REUSEADDR " ); - } -#endif + set_non_blocking_mode(so.sock); - if ( ip_version > 4 ) { /* Could be 6 for IPv6 only or 10 (4+6) for IPv4+IPv6 */ - - if ( ip_version > 6 ) { - - if ( ( AF_INET6 == so.lsa.sa.sa_family ) && - setsockopt( so.sock, - IPPROTO_IPV6, - IPV6_V6ONLY, - (void *)&off, - sizeof( off ) ) != 0 ) { - - /* Set IPv6 only option, but don't abort on errors. */ - stcp_report_error( "sockettcp: cannot set socket option IPV6_V6ONLY=off (entry %i)", - portsTotal ); - } - } - else { - - if ( ( AF_INET6 == so.lsa.sa.sa_family ) && - ( setsockopt( so.sock, - IPPROTO_IPV6, - IPV6_V6ONLY, - (void *)&off, - sizeof( off ) ) != 0 ) ) { - - // Set IPv6 only option, but don't abort on errors. - stcp_report_error( "sockettcp: cannot set socket option IPV6_V6ONLY" ); - } - - } + if (so.lsa.sa.sa_family == AF_INET) { - } - - set_non_blocking_mode( so.sock ); - - if ( so.lsa.sa.sa_family == AF_INET ) { - - len = sizeof( so.lsa.sin ); - if ( bind( so.sock, - &(so.lsa.sa), - len ) != 0) { - - stcp_report_error( "sockettcp: cannot bind to %.*s: %d (%s)", - (int)msg.len, - msg.ptr, - (int)ERRNO, - strerror( errno ) ); - closesocket( so.sock ); - so.sock = INVALID_SOCKET; - return 0; - } - } - else if ( so.lsa.sa.sa_family == AF_INET6 ) { - - len = sizeof( so.lsa.sin6 ); - if ( bind( so.sock, - &(so.lsa.sa), - len ) != 0 ) { - stcp_report_error( "sockettcp: cannot bind to IPv6 %.*s: %d (%s)", - (int)msg.len, - msg.ptr, - (int)ERRNO, - strerror( errno ) ); - closesocket( so.sock ); - so.sock = INVALID_SOCKET; - return 0; - } - } - else { - stcp_report_error( "sockettcp: cannot bind: address family not supported " ); - closesocket( so.sock ); - so.sock = INVALID_SOCKET; - return 0; - } + len = sizeof(so.lsa.sin); + if (bind(so.sock, &(so.lsa.sa), len) != 0) { - if ( listen( so.sock, SOMAXCONN ) != 0 ) { + stcp_report_error("sockettcp: cannot bind to %.*s: %d (%s)", + (int) msg.len, + msg.ptr, + (int) ERRNO, + strerror(errno)); + closesocket(so.sock); + so.sock = INVALID_SOCKET; + return 0; + } + } + else if (so.lsa.sa.sa_family == AF_INET6) { + + len = sizeof(so.lsa.sin6); + if (bind(so.sock, &(so.lsa.sa), len) != 0) { + stcp_report_error("sockettcp: cannot bind to IPv6 %.*s: %d (%s)", + (int) msg.len, + msg.ptr, + (int) ERRNO, + strerror(errno)); + closesocket(so.sock); + so.sock = INVALID_SOCKET; + return 0; + } + } + else { + stcp_report_error("sockettcp: cannot bind: address family not supported "); + closesocket(so.sock); + so.sock = INVALID_SOCKET; + return 0; + } - stcp_report_error( "sockettcp: cannot listen to %.*s: %d (%s)", - (int)msg.len, - msg.ptr, - (int)ERRNO, - strerror( errno ) ); - closesocket( so.sock ); - so.sock = INVALID_SOCKET; - return 0; - } + if (listen(so.sock, SOMAXCONN) != 0) { - if ( ( getsockname( so.sock, &(usa.sa), &len ) != 0 ) || - ( usa.sa.sa_family != so.lsa.sa.sa_family ) ) { - - int err = (int)ERRNO; - stcp_report_error( "sockettcp: call to getsockname failed %.*s: %d (%s)", - (int)msg.len, - msg.ptr, - err, - strerror( errno ) ); - closesocket( so.sock ); - so.sock = INVALID_SOCKET; - return 0; - } + stcp_report_error("sockettcp: cannot listen to %.*s: %d (%s)", + (int) msg.len, + msg.ptr, + (int) ERRNO, + strerror(errno)); + closesocket(so.sock); + so.sock = INVALID_SOCKET; + return 0; + } - /* Update lsa port in case of random free ports */ - if ( AF_INET6 == so.lsa.sa.sa_family ) { - so.lsa.sin6.sin6_port = usa.sin6.sin6_port; - } - else { - so.lsa.sin.sin_port = usa.sin.sin_port; - } - - if ( NULL == ( ptr = - (struct socket *)realloc( srv_ctx->listening_sockets, - ( srv_ctx->num_listening_sockets + 1 ) * - sizeof( srv_ctx->listening_sockets[0] ) ) ) ) { - - stcp_report_error( "sockettcp: Out of memory" ); - closesocket( so.sock ); - so.sock = INVALID_SOCKET; - continue; - } + if ((getsockname(so.sock, &(usa.sa), &len) != 0) || (usa.sa.sa_family != so.lsa.sa.sa_family)) { - if ( NULL == ( pfd = - (struct pollfd *)realloc( srv_ctx->listening_socket_fds, - ( srv_ctx->num_listening_sockets + 1 ) * - sizeof( srv_ctx->listening_socket_fds[0] ) ) ) ) { + int err = (int) ERRNO; + stcp_report_error("sockettcp: call to getsockname failed %.*s: %d (%s)", + (int) msg.len, + msg.ptr, + err, + strerror(errno)); + closesocket(so.sock); + so.sock = INVALID_SOCKET; + return 0; + } - stcp_report_error( "sockettcp: Out of memory" ); - closesocket( so.sock ); - so.sock = INVALID_SOCKET; - free(ptr); - continue; - } - - set_close_on_exec( so.sock ); - srv_ctx->listening_sockets = ptr; - srv_ctx->listening_sockets[ srv_ctx->num_listening_sockets ] = so; - srv_ctx->listening_socket_fds = pfd; - srv_ctx->num_listening_sockets++; - portsOk++; - - } // while - - if ( portsOk != portsTotal ) { - stcp_close_all_listening_sockets( srv_ctx ); - portsOk = 0; + /* Update lsa port in case of random free ports */ + if (AF_INET6 == so.lsa.sa.sa_family) { + so.lsa.sin6.sin6_port = usa.sin6.sin6_port; } - - return portsOk; -} + else { + so.lsa.sin.sin_port = usa.sin.sin_port; + } + + if (NULL == (ptr = (struct socket *) realloc(srv_ctx->listening_sockets, + (srv_ctx->num_listening_sockets + 1) * + sizeof(srv_ctx->listening_sockets[0])))) { + + stcp_report_error("sockettcp: Out of memory"); + closesocket(so.sock); + so.sock = INVALID_SOCKET; + continue; + } + + if (NULL == (pfd = (struct pollfd *) realloc(srv_ctx->listening_socket_fds, + (srv_ctx->num_listening_sockets + 1) * + sizeof(srv_ctx->listening_socket_fds[0])))) { + + stcp_report_error("sockettcp: Out of memory"); + closesocket(so.sock); + so.sock = INVALID_SOCKET; + free(ptr); + continue; + } + + set_close_on_exec(so.sock); + srv_ctx->listening_sockets = ptr; + srv_ctx->listening_sockets[srv_ctx->num_listening_sockets] = so; + srv_ctx->listening_socket_fds = pfd; + srv_ctx->num_listening_sockets++; + portsOk++; + + } // while + if (portsOk != portsTotal) { + stcp_close_all_listening_sockets(srv_ctx); + portsOk = 0; + } + + return portsOk; +} //////////////////////////////////////////////////////////////////////////////// // accept_new_connection // int -stcp_accept( struct server_context *srv_ctx, - const struct socket *listener, - struct socket *psocket ) +stcp_accept(struct server_context *srv_ctx, const struct socket *listener, struct socket *psocket) { - //char src_addr[ IP_ADDR_STR_LEN ]; - socklen_t len = sizeof( psocket->rsa ); - int on = 1; + // char src_addr[ IP_ADDR_STR_LEN ]; + socklen_t len = sizeof(psocket->rsa); + int on = 1; - /* Check pointers and listening socklet */ - if ( ( NULL == listener ) || ( NULL == psocket ) || !listener->sock ) { - return 0; - } - - if ( INVALID_SOCKET == - ( psocket->sock = (int)accept( listener->sock, &(psocket->rsa.sa), &len ) ) ) { - stcp_report_error( "sockettcp: accept() failed: %s", - strerror( ERRNO ) ) ; - return 0; - } - /*else if ( !check_acl( ctx, ntohl( *(uint32_t *)&psocket->rsa.sin.sin_addr ) ) ) { - sockaddr_to_string(src_addr, sizeof (src_addr), &psocket->rsa); - //web_cry(fc(ctx), "%s: %s is not allowed to connect", __func__, src_addr ); - closesocket( psocket->sock ); - }*/ - else { + /* Check pointers and listening socklet */ + if ((NULL == listener) || (NULL == psocket) || !listener->sock) { + return 0; + } - // Put so socket structure into the queue + if (INVALID_SOCKET == (psocket->sock = (int) accept(listener->sock, &(psocket->rsa.sa), &len))) { + stcp_report_error("sockettcp: accept() failed: %s", strerror(ERRNO)); + return 0; + } + /*else if ( !check_acl( ctx, ntohl( *(uint32_t *)&psocket->rsa.sin.sin_addr ) ) ) { + sockaddr_to_string(src_addr, sizeof (src_addr), &psocket->rsa); + //web_cry(fc(ctx), "%s: %s is not allowed to connect", __func__, src_addr ); + closesocket( psocket->sock ); + }*/ + else { + + // Put so socket structure into the queue #if defined(_WIN32) - (void)SetHandleInformation((HANDLE)(intptr_t)psocket->sock, HANDLE_FLAG_INHERIT, 0); + (void) SetHandleInformation((HANDLE) (intptr_t) psocket->sock, HANDLE_FLAG_INHERIT, 0); #else - if ( fcntl( psocket->sock, F_SETFD, FD_CLOEXEC ) != 0 ) { - // Failed TODO - } + if (fcntl(psocket->sock, F_SETFD, FD_CLOEXEC) != 0) { + // Failed TODO + } #endif - - // If listner is ssl this is to - psocket->is_ssl = listener->is_ssl; - if ( getsockname( psocket->sock, &psocket->lsa.sa, &len ) != 0 ) { - stcp_report_error( "sockettcp: getsockname() failed: %s", - strerror( ERRNO ) ) ; - } + // If listner is ssl this is to + psocket->is_ssl = listener->is_ssl; - // Set TCP keep-alive. This is needed because if HTTP-level - // keep-alive is enabled, and client resets the connection, server - // won't get TCP FIN or RST and will keep the connection open - // forever. With TCP keep-alive, next keep-alive handshake will - // figure out that the client is down and will close the server end. - // Thanks to Igor Klopov who suggested the patch. - if ( setsockopt( psocket->sock, - SOL_SOCKET, - SO_KEEPALIVE, - (SOCK_OPT_TYPE)&on, - sizeof( on ) ) != 0 ) { - stcp_report_error( "sockettcp: setsockopt(SOL_SOCKET SO_KEEPALIVE) failed: %s", - strerror( ERRNO ) ); - } + if (getsockname(psocket->sock, &psocket->lsa.sa, &len) != 0) { + stcp_report_error("sockettcp: getsockname() failed: %s", strerror(ERRNO)); + } - // Disable TCP Nagle's algorithm. Normally TCP packets are coalesced - // to effectively fill up the underlying IP packet payload and - // reduce the overhead of sending lots of small buffers. However - // this hurts the server's throughput (ie. operations per second) - // when HTTP 1.1 persistent connections are used and the responses - // are relatively small (eg. less than 1400 bytes). - // - if ( ( NULL != srv_ctx ) && srv_ctx->config_tcp_nodelay ) { - if ( set_tcp_nodelay( psocket->sock, 1 ) != 0 ) { - stcp_report_error( "sockettcp: setsockopt(IPPROTO_TCP TCP_NODELAY) failed: %s", - strerror( ERRNO ) ); - } - } + // Set TCP keep-alive. This is needed because if HTTP-level + // keep-alive is enabled, and client resets the connection, server + // won't get TCP FIN or RST and will keep the connection open + // forever. With TCP keep-alive, next keep-alive handshake will + // figure out that the client is down and will close the server end. + // Thanks to Igor Klopov who suggested the patch. + if (setsockopt(psocket->sock, SOL_SOCKET, SO_KEEPALIVE, (SOCK_OPT_TYPE) &on, sizeof(on)) != 0) { + stcp_report_error("sockettcp: setsockopt(SOL_SOCKET SO_KEEPALIVE) failed: %s", strerror(ERRNO)); + } - // We are using non-blocking sockets. Thus, the - // set_sock_timeout(so.sock, timeout); - // call is no longer required. + // Disable TCP Nagle's algorithm. Normally TCP packets are coalesced + // to effectively fill up the underlying IP packet payload and + // reduce the overhead of sending lots of small buffers. However + // this hurts the server's throughput (ie. operations per second) + // when HTTP 1.1 persistent connections are used and the responses + // are relatively small (eg. less than 1400 bytes). + // + if ((NULL != srv_ctx) && srv_ctx->config_tcp_nodelay) { + if (set_tcp_nodelay(psocket->sock, 1) != 0) { + stcp_report_error("sockettcp: setsockopt(IPPROTO_TCP TCP_NODELAY) failed: %s", strerror(ERRNO)); + } + } - // The "non blocking" property should already be - // inherited from the parent socket. Set it for - // non-compliant socket implementations. */ - set_non_blocking_mode( psocket->sock ); + // We are using non-blocking sockets. Thus, the + // set_sock_timeout(so.sock, timeout); + // call is no longer required. - } + // The "non blocking" property should already be + // inherited from the parent socket. Set it for + // non-compliant socket implementations. */ + set_non_blocking_mode(psocket->sock); + } - return 1; + return 1; } //////////////////////////////////////////////////////////////////////////////// // stcp_init_client_connection // -void stcp_init_client_connection( struct stcp_connection *conn, - struct stcp_secure_options *secure_opts ) +void +stcp_init_client_connection(struct stcp_connection *conn, struct stcp_secure_options *secure_opts) { - // Check conn pointer - if ( NULL == conn ) { - return; - } - - // If secure then secure options must be set - if ( conn->client.is_ssl && ( NULL == secure_opts ) ) { - return; - } - - conn->conn_state = STCP_CONN_STATE_CONNECTED; + // Check conn pointer + if (NULL == conn) { + return; + } - conn->birth = time( NULL ); + // If secure then secure options must be set + if (conn->client.is_ssl && (NULL == secure_opts)) { + return; + } - // Fill in IP, port info early so even if SSL setup below fails, - // error handler would have the corresponding info. - // Thanks to Johannes Winkelmann for the patch. - if ( AF_INET6 == conn->client.rsa.sa.sa_family ) { - conn->remote_port = ntohs( conn->client.rsa.sin6.sin6_port ); - } - else { - conn->remote_port = ntohs( conn->client.rsa.sin.sin_port ); - } + conn->conn_state = STCP_CONN_STATE_CONNECTED; - sockaddr_to_string( conn->remote_addr, - sizeof( conn->remote_addr ), - &conn->client.rsa ); + conn->birth = time(NULL); - if ( conn->client.is_ssl ) { + // Fill in IP, port info early so even if SSL setup below fails, + // error handler would have the corresponding info. + // Thanks to Johannes Winkelmann for the patch. + if (AF_INET6 == conn->client.rsa.sa.sa_family) { + conn->remote_port = ntohs(conn->client.rsa.sin6.sin6_port); + } + else { + conn->remote_port = ntohs(conn->client.rsa.sin.sin_port); + } - // Secure connection - if ( make_ssl( conn, - secure_opts, - conn->ssl_ctx, - SSL_accept, - &(conn->stop_flag ) ) ) { + sockaddr_to_string(conn->remote_addr, sizeof(conn->remote_addr), &conn->client.rsa); - // Get SSL client certificate information (if set) - ssl_get_client_cert_info( conn, secure_opts->srv_client_cert ); + if (conn->client.is_ssl) { - } + // Secure connection + if (make_ssl(conn, secure_opts, conn->ssl_ctx, SSL_accept, &(conn->stop_flag))) { + // Get SSL client certificate information (if set) + ssl_get_client_cert_info(conn, secure_opts->srv_client_cert); } - + } } //////////////////////////////////////////////////////////////////////////////// // stcp_socket_get_address // -int stcp_socket_get_address(struct stcp_connection *conn, char *buf, size_t len) +int +stcp_socket_get_address(struct stcp_connection *conn, char *buf, size_t len) { - struct sockaddr_storage addr; - socklen_t addrlen; - - memset(&addr, 0, sizeof(struct sockaddr_storage)); - addrlen = sizeof(addr); - if(!getpeername( conn->client.sock, (struct sockaddr *)&addr, &addrlen)){ - if(addr.ss_family == AF_INET){ - if(inet_ntop(AF_INET, &((struct sockaddr_in *)&addr)->sin_addr.s_addr, buf, len)){ - return 0; - } - }else if(addr.ss_family == AF_INET6){ - if(inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&addr)->sin6_addr.s6_addr, buf, len)){ - return 0; - } - } - } - return 1; + struct sockaddr_storage addr; + socklen_t addrlen; + + memset(&addr, 0, sizeof(struct sockaddr_storage)); + addrlen = sizeof(addr); + if (!getpeername(conn->client.sock, (struct sockaddr *) &addr, &addrlen)) { + if (addr.ss_family == AF_INET) { + if (inet_ntop(AF_INET, &((struct sockaddr_in *) &addr)->sin_addr.s_addr, buf, len)) { + return 0; + } + } + else if (addr.ss_family == AF_INET6) { + if (inet_ntop(AF_INET6, &((struct sockaddr_in6 *) &addr)->sin6_addr.s6_addr, buf, len)) { + return 0; + } + } + } + return 1; } diff --git a/src/vscp/common/clientlist.cpp b/src/vscp/common/clientlist.cpp index 34ff86e99..3b1a7cbd5 100644 --- a/src/vscp/common/clientlist.cpp +++ b/src/vscp/common/clientlist.cpp @@ -35,7 +35,7 @@ #include #endif -#include +#include #include #include #include diff --git a/src/vscp/common/userlist.cpp b/src/vscp/common/userlist.cpp index 946657f3f..8b3993d1f 100644 --- a/src/vscp/common/userlist.cpp +++ b/src/vscp/common/userlist.cpp @@ -27,7 +27,7 @@ // #ifdef __GNUG__ -//#pragma implementation +// #pragma implementation #endif #ifdef WIN32 @@ -48,7 +48,7 @@ #include "userlist.h" #include -#include +// #include #include #include // Needs C++11 -std=c++11 @@ -59,6 +59,8 @@ #include #include +// #include + #include #include #include @@ -70,7 +72,8 @@ using json = nlohmann::json; using namespace kainjow::mustache; // Forward declarations -void vscp_md5(char *digest, const unsigned char *buf, size_t len); +void +vscp_md5(char *digest, const unsigned char *buf, size_t len); /////////////////////////////////////////////////// // GLOBALS @@ -80,7 +83,7 @@ void vscp_md5(char *digest, const unsigned char *buf, size_t len); // Constructor // -CUserItem::CUserItem(void) +CUserItem::CUserItem(void) { m_userID = VSCP_ADD_USER_UNINITIALIZED; @@ -95,71 +98,81 @@ CUserItem::CUserItem(void) // Destructor // -CUserItem::~CUserItem(void) +CUserItem::~CUserItem(void) { m_listAllowedRemotes.clear(); m_listAllowedEvents.clear(); } /////////////////////////////////////////////////////////////////////////////// -// setPasswordFromClearText +// getPasswordHash // -bool CUserItem::setPasswordFromClearText(const std::string& strPassword) +std::string +CUserItem::getPasswordHash(void) { - std::string result; - std::string combined = m_user + ":" + strPassword; - if (!vscp_makePasswordHash(result, combined)) { - return false; - } - m_password = result; - return true; + return m_password; +}; + +/////////////////////////////////////////////////////////////////////////////// +// setPasswordHash +// + +void +CUserItem::setPasswordHash(const std::string &md5hash) +{ + m_password = md5hash; } /////////////////////////////////////////////////////////////////////////////// -// getPassword +// setPasswordFromClearText // -std::string CUserItem::getPassword(void) -{ - return m_password; -}; +void +CUserItem::setPasswordFromClearText(const std::string &strPassword) +{ + char pw[33]; + memset(pw, 0, 33); // Clear password + std::string combined = m_user + ":" + strPassword; + vscp_md5(pw, (const unsigned char *) combined.c_str(), combined.length()); + m_password = pw; +} /////////////////////////////////////////////////////////////////////////////// // validateUser // -bool CUserItem::validateUser(const std::string &password) +bool +CUserItem::validateUser(const std::string &password) { - std::string result; + char pw[33]; + memset(pw, 0, 33); // Clear password std::string combined = m_user + ":" + password; - if (!vscp_makePasswordHash(result, combined)) { - return false; - } - - return vscp_isPasswordValid(m_password, password); + vscp_md5(pw, (const unsigned char *) combined.c_str(), combined.length()); + return (0 == strcmp(pw, m_password.c_str())); } /////////////////////////////////////////////////////////////////////////////// // fixName // -void CUserItem::fixName(void) +void +CUserItem::fixName(void) { vscp_trim(m_user); // Works only for ASCII names. Should be fixed so // UTF8 names can be used TODO for (size_t i = 0; i < m_user.length(); i++) { - switch ((const char)m_user[i]) { - case ';': - case ':': - case '\'': - case '\"': - case ',': - case ' ': - m_user[i] = '_'; - break; + switch ((const char) m_user[i]) { + case ';': + case ':': + case '\'': + case '\"': + case ',': + case ' ': + m_user[i] = '_'; + break; } } } @@ -170,7 +183,8 @@ void CUserItem::fixName(void) // name;password;fullname;filtermask;rights;remotes;events;note // -bool CUserItem::setFromString(const std::string &userSettings) +bool +CUserItem::setFromString(const std::string &userSettings) { std::string strToken; std::deque tokens; @@ -193,7 +207,7 @@ bool CUserItem::setFromString(const std::string &userSettings) tokens.pop_front(); vscp_trim(strToken); if (strToken.length()) { - setPassword(strToken); + setPasswordHash(strToken); } } @@ -273,7 +287,8 @@ bool CUserItem::setFromString(const std::string &userSettings) // userid;name;password;fullname;filter;mask;rights;remotes;events;note // -bool CUserItem::getAsString(std::string &strUser) +bool +CUserItem::getAsString(std::string &strUser) { std::string str; strUser.clear(); @@ -282,11 +297,10 @@ bool CUserItem::getAsString(std::string &strUser) strUser += getUserName(); strUser += ";"; // Protect password - str = getPassword(); + str = getPasswordHash(); for (size_t i = 0; i < str.length(); i++) { strUser += "*"; } - // strUser += getPassword(); strUser += ";"; strUser += getFullname(); strUser += ";"; @@ -316,16 +330,17 @@ bool CUserItem::getAsString(std::string &strUser) // userid;name;password;fullname;filter;mask;rights;remotes;events;note // -bool CUserItem::getAsMap(std::map &mapUser) +bool +CUserItem::getAsMap(std::map &mapUser) { std::string str, wstr; mapUser["userid"] = vscp_str_format("%ld;", getUserID()); - mapUser["name"] = getUserName(); + mapUser["name"] = getUserName(); // Protect password wstr = ""; - str = getPassword(); + str = getPasswordHash(); for (size_t i = 0; i < str.length(); i++) { wstr += "*"; } @@ -336,10 +351,10 @@ bool CUserItem::getAsMap(std::map &mapUser) mapUser["filter"] = str; vscp_writeMaskToString(str, getUserFilter()); - mapUser["mask"] = str; - mapUser["rights"] = getUserRightsAsString(); + mapUser["mask"] = str; + mapUser["rights"] = getUserRightsAsString(); mapUser["remotes"] = getAllowedRemotesAsString(); - mapUser["events"] = getAllowedEventsAsString(); + mapUser["events"] = getAllowedEventsAsString(); str = getNote(); vscp_base64_std_encode(str); @@ -352,7 +367,8 @@ bool CUserItem::getAsMap(std::map &mapUser) // addUserRightsFromString // -bool CUserItem::addUserRightsFromString(const std::string &strRights) +bool +CUserItem::addUserRightsFromString(const std::string &strRights) { // Privileges if (strRights.length()) { @@ -368,39 +384,39 @@ bool CUserItem::addUserRightsFromString(const std::string &strRights) if (0 == strcasecmp(str.c_str(), "admin")) { // All rights m_userRights |= VSCP_ADMIN_DEFAULT_RIGHTS; - } + } else if (0 == strcasecmp(str.c_str(), "user")) { // A standard user m_userRights |= VSCP_USER_DEFAULT_RIGHTS; - } + } else if (0 == strcasecmp(str.c_str(), "web")) { // A standard driver m_userRights |= VSCP_WEB_DEFAULT_RIGHTS; - } + } else if (0 == strcasecmp(str.c_str(), "rest")) { // A standard driver m_userRights |= VSCP_REST_DEFAULT_RIGHTS; - } + } else if (0 == strcasecmp(str.c_str(), "tcp")) { // A standard driver m_userRights |= VSCP_TCPIP_DEFAULT_RIGHTS; - } + } else if (0 == strcasecmp(str.c_str(), "websockets")) { // A standard driver m_userRights |= VSCP_WEBSOCKETS_DEFAULT_RIGHTS; - } + } else if (0 == strcasecmp(str.c_str(), "mqtt")) { // A standard driver m_userRights |= VSCP_MQTT_DEFAULT_RIGHTS; - } + } else if (0 == strcasecmp(str.c_str(), "udp")) { // A standard driver m_userRights |= VSCP_UDP_DEFAULT_RIGHTS; - } + } else if (0 == strcasecmp(str.c_str(), "driver")) { // A standard driver m_userRights |= VSCP_DRIVER_DEFAULT_RIGHTS; - } + } else if (0 == strcasecmp(str.c_str(), "tcpip")) { m_userRights |= VSCP_USER_RIGHT_ALLOW_TCPIP; } @@ -433,37 +449,37 @@ bool CUserItem::addUserRightsFromString(const std::string &strRights) } else if (0 == strcasecmp(str.c_str(), "send-events")) { m_userRights |= VSCP_USER_RIGHT_ALLOW_SEND_EVENT; - } + } else if (0 == strcasecmp(str.c_str(), "receive-events")) { m_userRights |= VSCP_USER_RIGHT_ALLOW_RCV_EVENT; - } + } else if (0 == strcasecmp(str.c_str(), "l1ctrl-events")) { m_userRights |= VSCP_USER_RIGHT_ALLOW_SEND_L1CTRL_EVENT; - } + } else if (0 == strcasecmp(str.c_str(), "l2ctrl-events")) { m_userRights |= VSCP_USER_RIGHT_ALLOW_SEND_L2CTRL_EVENT; - } + } else if (0 == strcasecmp(str.c_str(), "hlo-events")) { m_userRights |= VSCP_USER_RIGHT_ALLOW_SEND_HLO_EVENT; - } + } else if (0 == strcasecmp(str.c_str(), "set-filter")) { m_userRights |= VSCP_USER_RIGHT_ALLOW_SETFILTER; - } + } else if (0 == strcasecmp(str.c_str(), "set-guid")) { m_userRights |= VSCP_USER_RIGHT_ALLOW_SETGUID; - } + } else if (0 == strcasecmp(str.c_str(), "shutdown")) { m_userRights |= VSCP_USER_RIGHT_ALLOW_SHUTDOWN; - } + } else if (0 == strcasecmp(str.c_str(), "restart")) { m_userRights |= VSCP_USER_RIGHT_ALLOW_RESTART; - } + } else if (0 == strcasecmp(str.c_str(), "interface")) { m_userRights |= VSCP_USER_RIGHT_ALLOW_INTERFACE; - } + } else if (0 == strcasecmp(str.c_str(), "test")) { m_userRights |= VSCP_USER_RIGHT_ALLOW_TEST; - } + } else { // Numerical uint32_t val = vscp_readStringValue(str); @@ -479,7 +495,8 @@ bool CUserItem::addUserRightsFromString(const std::string &strRights) // setUserRightsFromString // -bool CUserItem::setUserRightsFromString(const std::string &strRights) +bool +CUserItem::setUserRightsFromString(const std::string &strRights) { m_userRights = 0; return addUserRightsFromString(strRights); @@ -489,7 +506,8 @@ bool CUserItem::setUserRightsFromString(const std::string &strRights) // getAllowedEvent // -bool CUserItem::getAllowedEvent(size_t n, std::string &event) +bool +CUserItem::getAllowedEvent(size_t n, std::string &event) { if (!m_listAllowedEvents.size()) { return false; @@ -507,7 +525,8 @@ bool CUserItem::getAllowedEvent(size_t n, std::string &event) // setAllowedEvent // -bool CUserItem::setAllowedEvent(size_t n, const std::string &strEvent) +bool +CUserItem::setAllowedEvent(size_t n, const std::string &strEvent) { if (!m_listAllowedEvents.size()) { return false; @@ -517,9 +536,9 @@ bool CUserItem::setAllowedEvent(size_t n, const std::string &strEvent) return false; } - std::string str = strEvent; + std::string str = strEvent; uint16_t vscp_class = 0; - uint16_t vscp_type = 0; + uint16_t vscp_type = 0; vscp_trim(str); @@ -532,18 +551,18 @@ bool CUserItem::setAllowedEvent(size_t n, const std::string &strEvent) // Left wildcard if ('*' == strEvent[0]) { - str = vscp_str_right(str, str.length() - 2); - vscp_type = vscp_readStringValue(str); - str = vscp_str_format("*:%04X", vscp_type); + str = vscp_str_right(str, str.length() - 2); + vscp_type = vscp_readStringValue(str); + str = vscp_str_format("*:%04X", vscp_type); m_listAllowedEvents[n] = str; return true; } // Right wildcard if ('*' == str[str.length() - 1]) { - str = vscp_str_left(str, str.length() - 2); - vscp_class = vscp_readStringValue(str); - str = vscp_str_format("%04X:*", vscp_class); + str = vscp_str_left(str, str.length() - 2); + vscp_class = vscp_readStringValue(str); + str = vscp_str_format("%04X:*", vscp_class); m_listAllowedEvents[n] = str; return true; } @@ -552,9 +571,9 @@ bool CUserItem::setAllowedEvent(size_t n, const std::string &strEvent) vscp_class = vscp_readStringValue(str); size_t pos; if (std::string::npos != (pos = str.find(':'))) { - str = vscp_str_right(str, str.length() - pos - 1); - vscp_type = vscp_readStringValue(str); - str = vscp_str_format("%04X:%04X", vscp_class, vscp_type); + str = vscp_str_right(str, str.length() - pos - 1); + vscp_type = vscp_readStringValue(str); + str = vscp_str_format("%04X:%04X", vscp_class, vscp_type); m_listAllowedEvents[n] = str; return true; } @@ -566,11 +585,12 @@ bool CUserItem::setAllowedEvent(size_t n, const std::string &strEvent) // addAllowedEvent // -bool CUserItem::addAllowedEvent(const std::string &strEvent) +bool +CUserItem::addAllowedEvent(const std::string &strEvent) { - std::string str = strEvent; + std::string str = strEvent; uint16_t vscp_class = 0; - uint16_t vscp_type = 0; + uint16_t vscp_type = 0; vscp_trim(str); @@ -583,18 +603,18 @@ bool CUserItem::addAllowedEvent(const std::string &strEvent) // Left wildcard if ('*' == strEvent[0]) { - str = vscp_str_right(str, str.length() - 2); + str = vscp_str_right(str, str.length() - 2); vscp_type = vscp_readStringValue(str); - str = vscp_str_format("*:%04X", vscp_type); + str = vscp_str_format("*:%04X", vscp_type); m_listAllowedEvents.push_back(str); return true; } // Right wildcard if ('*' == str[str.length() - 1]) { - str = vscp_str_left(str, str.length() - 2); + str = vscp_str_left(str, str.length() - 2); vscp_class = vscp_readStringValue(str); - str = vscp_str_format("%04X:*", vscp_class); + str = vscp_str_format("%04X:*", vscp_class); m_listAllowedEvents.push_back(str); return true; } @@ -603,9 +623,9 @@ bool CUserItem::addAllowedEvent(const std::string &strEvent) vscp_class = vscp_readStringValue(str); size_t pos; if (std::string::npos != (pos = str.find(':'))) { - str = vscp_str_right(str, str.length() - pos - 1); + str = vscp_str_right(str, str.length() - pos - 1); vscp_type = vscp_readStringValue(str); - str = vscp_str_format("%04X:%04X", vscp_class, vscp_type); + str = vscp_str_format("%04X:%04X", vscp_class, vscp_type); m_listAllowedEvents.push_back(str); return true; } @@ -617,8 +637,8 @@ bool CUserItem::addAllowedEvent(const std::string &strEvent) // setAllowedEventsFromString // -bool CUserItem::setAllowedEventsFromString(const std::string &strEvents, - bool bClear) +bool +CUserItem::setAllowedEventsFromString(const std::string &strEvents, bool bClear) { std::string str; @@ -648,7 +668,8 @@ bool CUserItem::setAllowedEventsFromString(const std::string &strEvents, // getAllowedEventsAsString // -std::string CUserItem::getAllowedEventsAsString(void) +std::string +CUserItem::getAllowedEventsAsString(void) { std::string strAllowedEvents; @@ -668,7 +689,8 @@ std::string CUserItem::getAllowedEventsAsString(void) // setAllowedRemotesFromString // -bool CUserItem::setAllowedRemotesFromString(const std::string &strConnect) +bool +CUserItem::setAllowedRemotesFromString(const std::string &strConnect) { // Privileges if (strConnect.length()) { @@ -693,7 +715,8 @@ bool CUserItem::setAllowedRemotesFromString(const std::string &strConnect) // getAllowedRemotesAsString // -std::string CUserItem::getAllowedRemotesAsString(void) +std::string +CUserItem::getAllowedRemotesAsString(void) { size_t i; std::string strAllowedRemotes; @@ -714,7 +737,8 @@ std::string CUserItem::getAllowedRemotesAsString(void) // getAllowedRemote // -bool CUserItem::getAllowedRemote(size_t n, std::string &remote) +bool +CUserItem::getAllowedRemote(size_t n, std::string &remote) { if (!m_listAllowedRemotes.size()) { return false; @@ -732,7 +756,8 @@ bool CUserItem::getAllowedRemote(size_t n, std::string &remote) // setAllowedRemote // -bool CUserItem::setAllowedRemote(size_t n, std::string &remote) +bool +CUserItem::setAllowedRemote(size_t n, std::string &remote) { if (!m_listAllowedRemotes.size()) { return false; @@ -751,7 +776,8 @@ bool CUserItem::setAllowedRemote(size_t n, std::string &remote) // getUserRightsAsString // -std::string CUserItem::getUserRightsAsString(void) +std::string +CUserItem::getUserRightsAsString(void) { std::string strRights; @@ -768,7 +794,8 @@ std::string CUserItem::getUserRightsAsString(void) // // -int CUserItem::isAllowedToConnect(uint32_t remote_ip) +int +CUserItem::isAllowedToConnect(uint32_t remote_ip) { int allowed = '+'; int flag; @@ -777,15 +804,16 @@ int CUserItem::isAllowedToConnect(uint32_t remote_ip) remote_ip = htonl(remote_ip); // If the list is empty - allow all - if (0 == m_listAllowedRemotes.size()) return 1; + if (0 == m_listAllowedRemotes.size()) + return 1; for (size_t i = 0; i < m_listAllowedRemotes.size(); i++) { spdlog::debug("userlist: [isAllowedToConnect] Checking {}.", m_listAllowedRemotes[i]); - //fprintf(stderr,"%s\n",m_listAllowedRemotes[i].c_str()); + // fprintf(stderr,"%s\n",m_listAllowedRemotes[i].c_str()); flag = m_listAllowedRemotes[i].at(0); if ((flag != '+' && flag != '-') || - (0 == vscp_parse_ipv4_addr(m_listAllowedRemotes[i].substr(1).c_str(),&net, &mask))) { - spdlog::debug("userlist: [isAllowedToConnect] Format error {}.", m_listAllowedRemotes[i]); + (0 == vscp_parse_ipv4_addr(m_listAllowedRemotes[i].substr(1).c_str(), &net, &mask))) { + spdlog::debug("userlist: [isAllowedToConnect] Format error {}.", m_listAllowedRemotes[i]); return -1; } @@ -794,8 +822,7 @@ int CUserItem::isAllowedToConnect(uint32_t remote_ip) } // TODO replace with this method to enable IPv6 checks - //vscp_parse_match_net(const char* addr, const union usa *sa, 0); - + // vscp_parse_match_net(const char* addr, const union usa *sa, 0); } return ('+' == allowed) ? 1 : 0; @@ -805,8 +832,8 @@ int CUserItem::isAllowedToConnect(uint32_t remote_ip) // isUserAllowedToSendEvent // -bool CUserItem::isUserAllowedToSendEvent(const uint32_t vscp_class, - const uint32_t vscp_type) +bool +CUserItem::isUserAllowedToSendEvent(const uint32_t vscp_class, const uint32_t vscp_type) { unsigned int i; std::string str; @@ -818,31 +845,27 @@ bool CUserItem::isUserAllowedToSendEvent(const uint32_t vscp_class, } // Must be allowed to send Level I protocol events - if ( ( VSCP_CLASS1_PROTOCOL == vscp_class) && - !(VSCP_USER_RIGHT_ALLOW_SEND_L1CTRL_EVENT & m_userRights)) { - spdlog::debug("userlist: [isUserAllowedToSendEvent] User not allowed to send level I protocol events."); - return false; + if ((VSCP_CLASS1_PROTOCOL == vscp_class) && !(VSCP_USER_RIGHT_ALLOW_SEND_L1CTRL_EVENT & m_userRights)) { + spdlog::debug("userlist: [isUserAllowedToSendEvent] User not allowed to send level I protocol events."); + return false; } // Must be allowed to send Level II protocol events - if ( ( VSCP_CLASS2_LEVEL1_PROTOCOL == vscp_class) && - !(VSCP_USER_RIGHT_ALLOW_SEND_L1CTRL_EVENT & m_userRights)) { - spdlog::debug("userlist: [isUserAllowedToSendEvent] User not allowed to send level II protocol events."); - return false; + if ((VSCP_CLASS2_LEVEL1_PROTOCOL == vscp_class) && !(VSCP_USER_RIGHT_ALLOW_SEND_L1CTRL_EVENT & m_userRights)) { + spdlog::debug("userlist: [isUserAllowedToSendEvent] User not allowed to send level II protocol events."); + return false; } // Must be allowed to send Level I protocol events - if ( ( VSCP_CLASS2_PROTOCOL == vscp_class) && - !(VSCP_USER_RIGHT_ALLOW_SEND_L2CTRL_EVENT & m_userRights)) { - spdlog::debug("userlist: [isUserAllowedToSendEvent] User not allowed to send level I protocol events."); - return false; + if ((VSCP_CLASS2_PROTOCOL == vscp_class) && !(VSCP_USER_RIGHT_ALLOW_SEND_L2CTRL_EVENT & m_userRights)) { + spdlog::debug("userlist: [isUserAllowedToSendEvent] User not allowed to send level I protocol events."); + return false; } // Must be allowed to send HLO events - if ( ( VSCP_CLASS2_HLO == vscp_class) && - !(VSCP_USER_RIGHT_ALLOW_SEND_HLO_EVENT & m_userRights)) { - spdlog::debug("userlist: [isUserAllowedToSendEvent] User not allowed to send HLO events."); - return false; + if ((VSCP_CLASS2_HLO == vscp_class) && !(VSCP_USER_RIGHT_ALLOW_SEND_HLO_EVENT & m_userRights)) { + spdlog::debug("userlist: [isUserAllowedToSendEvent] User not allowed to send HLO events."); + return false; } // If empty all events allowed @@ -873,26 +896,18 @@ bool CUserItem::isUserAllowedToSendEvent(const uint32_t vscp_class, return false; } - - - // ---------------------------------------------------------------------------- - - - //***************************************************************************** // CUserList //***************************************************************************** - - - /////////////////////////////////////////////////////////////////////////////// // Constructor // -CUserList::CUserList() { +CUserList::CUserList() +{ m_cntUsers = 0; } @@ -900,12 +915,10 @@ CUserList::CUserList() { // Destructor // -CUserList::~CUserList(void) +CUserList::~CUserList(void) { { - for (std::map::iterator it = - m_grouphashmap.begin(); - it != m_grouphashmap.end(); ++it) { + for (std::map::iterator it = m_grouphashmap.begin(); it != m_grouphashmap.end(); ++it) { CGroupItem *pItem = it->second; if (NULL != pItem) { delete pItem; @@ -916,9 +929,7 @@ CUserList::~CUserList(void) m_grouphashmap.clear(); { - for (std::map::iterator it = - m_userhashmap.begin(); - it != m_userhashmap.end(); ++it) { + for (std::map::iterator it = m_userhashmap.begin(); it != m_userhashmap.end(); ++it) { CUserItem *pItem = it->second; if (NULL != pItem) { delete pItem; @@ -933,125 +944,121 @@ CUserList::~CUserList(void) // loadUsersFromFile // -bool CUserList::loadUsersFromFile(const std::string &path) +bool +CUserList::loadUsersFromFile(const std::string &path) { json j; spdlog::debug("[loadUsersFromFile] Reading in users from '{}'.", path); - try { - std::ifstream in(path, std::ifstream::in); - in >> j; + try { + std::ifstream in(path, std::ifstream::in); + in >> j; } catch (json::parse_error) { - spdlog::critical("[loadUsersFromFile] Failed to load/parse JSON user list."); - return false; + spdlog::critical("[loadUsersFromFile] Failed to load/parse JSON user list."); + return false; } // Add users - if (!j["users"].is_array()) { - spdlog::critical("[loadUsersFromFile] 'users' must be valid JSON array."); - return false; + if (!j["users"].is_array()) { + spdlog::critical("[loadUsersFromFile] 'users' must be valid JSON array."); + return false; } for (json::iterator it = j["users"].begin(); it != j["users"].end(); ++it) { - try { - //std::cout << (*it).dump() << '\n'; + try { + // std::cout << (*it).dump() << '\n'; - std::string name = (*it).value("name", ""); + std::string name = (*it).value("name", ""); std::string password = (*it).value("password", ""); std::string fullname = (*it).value("full-name", ""); - std::string note = (*it).value("note", ""); + std::string note = (*it).value("note", ""); vscp_trim(name); if (!name.length()) { - spdlog::warn("[vscpl2drv-tcpipsrv] Invalid username ('user' record skiped)."); - continue; + spdlog::warn("[vscpl2drv-tcpipsrv] Invalid username ('user' record skiped)."); + continue; } vscp_trim(password); if (!password.length()) { - spdlog::warn("[loadUsersFromFile] Invalid password for user '{}' ('user' record skiped).", name); - continue; + spdlog::warn("[loadUsersFromFile] Invalid password for user '{}' ('user' record skiped).", name); + continue; } vscpEventFilter receive_filter; memset(&receive_filter, 0, sizeof(vscpEventFilter)); if ((*it).contains("filter")) { - vscp_readFilterFromString(&receive_filter, (*it).value("filter", "")); + vscp_readFilterFromString(&receive_filter, (*it).value("filter", "")); } if ((*it).contains("mask")) { - vscp_readMaskFromString(&receive_filter, (*it).value("mask", "")); + vscp_readMaskFromString(&receive_filter, (*it).value("mask", "")); } // Rights std::string rights; if ((*it)["rights"].is_array()) { - for (json::iterator it_rights = (*it)["rights"].begin(); it_rights != (*it)["rights"].end(); ++it_rights) { - std::cout << (*it_rights).dump() << '\n'; - if (rights.length()) rights += ","; - rights += (*it_rights).get(); - } + for (json::iterator it_rights = (*it)["rights"].begin(); it_rights != (*it)["rights"].end(); ++it_rights) { + std::cout << (*it_rights).dump() << '\n'; + if (rights.length()) + rights += ","; + rights += (*it_rights).get(); + } } else if ((*it)["rights"].is_string()) { - rights = (*it).value("rights", "user"); + rights = (*it).value("rights", "user"); } else { - spdlog::debug("[loadUsersFromFile] rights tag is missing for user (set to 'user')."); - rights = "user"; + spdlog::debug("[loadUsersFromFile] rights tag is missing for user (set to 'user')."); + rights = "user"; } // ACL remotes std::string remotes; if ((*it)["remotes"].is_array()) { - for (json::iterator it_remotes = (*it)["remotes"].begin(); it_remotes != (*it)["remotes"].end(); ++it_remotes) { - std::cout << (*it_remotes).dump() << '\n'; - if (remotes.length()) remotes += ","; - remotes += (*it_remotes).get(); - } + for (json::iterator it_remotes = (*it)["remotes"].begin(); it_remotes != (*it)["remotes"].end(); ++it_remotes) { + std::cout << (*it_remotes).dump() << '\n'; + if (remotes.length()) + remotes += ","; + remotes += (*it_remotes).get(); + } } else if ((*it)["remotes"].is_string()) { - remotes = (*it).value("remotes", ""); + remotes = (*it).value("remotes", ""); } else { - spdlog::debug("[loadUsersFromFile] remotes tag is missing for user. All client hosts can connect."); + spdlog::debug("[loadUsersFromFile] remotes tag is missing for user. All client hosts can connect."); } // Allowed events std::string events; if ((*it)["allow-events"].is_array()) { - for (json::iterator it_events = (*it)["allow-events"].begin(); - it_events != (*it)["allow-events"].end(); - ++it_events) { - std::cout << (*it_events).dump() << '\n'; - if (events.length()) events += ","; - events += (*it_events).get(); - } + for (json::iterator it_events = (*it)["allow-events"].begin(); it_events != (*it)["allow-events"].end(); + ++it_events) { + std::cout << (*it_events).dump() << '\n'; + if (events.length()) + events += ","; + events += (*it_events).get(); + } } else if ((*it)["allow-events"].is_string()) { - events = (*it).value("allow-events", ""); + events = (*it).value("allow-events", ""); } else { - spdlog::debug("[loadUsersFromFile] allow-events tag is missing for user. All events can be sent."); + spdlog::debug("[loadUsersFromFile] allow-events tag is missing for user. All events can be sent."); } - if (!addUser(name, - password, - fullname, - note, - &receive_filter, - rights, - remotes, - events)) { - spdlog::debug("[userlist::loadUsersFromFile] Failed to add user {}.",(*it).dump()); + if (!addUser(name, password, fullname, note, &receive_filter, rights, remotes, events)) { + spdlog::debug("[userlist::loadUsersFromFile] Failed to add user {}.", (*it).dump()); } - - } catch (...) { - spdlog::debug("[userlist::loadUsersFromFile] Failed to read user data from file {}.",(*it).dump()); + } + catch (...) { + spdlog::debug("[userlist::loadUsersFromFile] Failed to read user data from file {}.", (*it).dump()); } - } // for + } // for return true; } @@ -1060,14 +1067,15 @@ bool CUserList::loadUsersFromFile(const std::string &path) // addUser // -bool CUserList::addUser(const std::string &user, - const std::string &password, - const std::string &fullname, - const std::string &strNote, - const vscpEventFilter *pFilter, - const std::string &userRights, - const std::string &allowedRemotes, - const std::string &allowedEvents ) +bool +CUserList::addUser(const std::string &user, + const std::string &password, + const std::string &fullname, + const std::string &strNote, + const vscpEventFilter *pFilter, + const std::string &userRights, + const std::string &allowedRemotes, + const std::string &allowedEvents) { // Cant add user with name that is already defined. if (NULL != m_userhashmap[user]) { @@ -1086,11 +1094,11 @@ bool CUserList::addUser(const std::string &user, } pItem->setUserID(m_cntUsers); - m_cntUsers++; // Update user id counter + m_cntUsers++; // Update user id counter pItem->setUserName(user); pItem->fixName(); - pItem->setPassword(password); + pItem->setPasswordHash(password); pItem->setFullname(fullname); pItem->setNote(strNote); pItem->setFilter(pFilter); @@ -1115,8 +1123,8 @@ bool CUserList::addUser(const std::string &user, // name;password;fullname;filter;mask;rights;remotes;events;note // -bool CUserList::addUser(const std::string &strUser, - bool bUnpackNote) +bool +CUserList::addUser(const std::string &strUser, bool bUnpackNote) { std::string strToken; std::string user; @@ -1185,28 +1193,22 @@ bool CUserList::addUser(const std::string &strUser, strNote = tokens.front(); tokens.pop_front(); vscp_base64_std_decode(strNote); - } + } else { strNote = tokens.front(); tokens.pop_front(); } } - return addUser(user, - password, - fullname, - strNote, - &filter, - userRights, - allowedRemotes, - allowedEvents); + return addUser(user, password, fullname, strNote, &filter, userRights, allowedRemotes, allowedEvents); } /////////////////////////////////////////////////////////////////////////////// // deleteUser // -bool CUserList::deleteUser(const std::string &user) +bool +CUserList::deleteUser(const std::string &user) { CUserItem *pUser = getUser(user); if (NULL == pUser) { @@ -1226,7 +1228,8 @@ bool CUserList::deleteUser(const std::string &user) // deleteUser // -bool CUserList::deleteUser(const long userid) +bool +CUserList::deleteUser(const long userid) { CUserItem *pUser = getUser(userid); if (NULL == pUser) { @@ -1243,7 +1246,8 @@ bool CUserList::deleteUser(const long userid) // getUser // -CUserItem *CUserList::getUser(const std::string &user) +CUserItem * +CUserList::getUser(const std::string &user) { return m_userhashmap[user]; } @@ -1252,10 +1256,12 @@ CUserItem *CUserList::getUser(const std::string &user) // getUser // -CUserItem *CUserList::getUser(const long userid) { +CUserItem * +CUserList::getUser(const long userid) +{ std::map::iterator it; for (it = m_userhashmap.begin(); it != m_userhashmap.end(); ++it) { - std::string key = it->first; + std::string key = it->first; CUserItem *pUserItem = it->second; if (userid == pUserItem->getUserID()) { return pUserItem; @@ -1271,8 +1277,8 @@ CUserItem *CUserList::getUser(const long userid) { // validateUser // -CUserItem *CUserList::validateUser(const std::string &user, - const std::string &password) +CUserItem * +CUserList::validateUser(const std::string &user, const std::string &password) { CUserItem *pUserItem; @@ -1280,31 +1286,31 @@ CUserItem *CUserList::validateUser(const std::string &user, if (NULL == pUserItem) { spdlog::error("userlist: " "validateUser: Failed to validate user '{}' - " - "User is not defined.", user); + "User is not defined.", + user); return NULL; } - std::string testpw = pUserItem->getUserName() + ":" + password; - if (!vscp_isPasswordValid(pUserItem->getPassword(), testpw)) { + std::string combined = pUserItem->getUserName() + ":" + password; + if (!pUserItem->validateUser(password)) { spdlog::error("userlist :" "validateUser: Failed to validate user '{}' - " - "Check username/password.", user); + "Check username/password.", + user); return NULL; } return pUserItem; } - - /////////////////////////////////////////////////////////////////////////////// // getUserAsString // // userid;name;password;fullname;filter;mask;rights;remotes;events;note // -bool CUserList::getUserAsString(CUserItem *pUserItem, - std::string & strUser) +bool +CUserList::getUserAsString(CUserItem *pUserItem, std::string &strUser) { std::string str; strUser.clear(); @@ -1326,7 +1332,8 @@ bool CUserList::getUserAsString(CUserItem *pUserItem, // userid;name;password;fullname;filter;mask;rights;remotes;events;note // -bool CUserList::getUserAsString(uint32_t idx, std::string & strUser) +bool +CUserList::getUserAsString(uint32_t idx, std::string &strUser) { std::string str; uint32_t i = 0; @@ -1335,11 +1342,11 @@ bool CUserList::getUserAsString(uint32_t idx, std::string & strUser) for (it = m_userhashmap.begin(); it != m_userhashmap.end(); ++it) { if (i == idx) { - std::string key = it->first; + std::string key = it->first; CUserItem *pUserItem = it->second; if (getUserAsString(pUserItem, strUser)) { return true; - } + } else { return false; } @@ -1357,14 +1364,15 @@ bool CUserList::getUserAsString(uint32_t idx, std::string & strUser) // userid;name;password;fullname;filter;mask;rights;remotes;events;note // -bool CUserList::getAllUsers(std::string & strAllusers) +bool +CUserList::getAllUsers(std::string &strAllusers) { std::string str; strAllusers.clear(); std::map::iterator it; for (it = m_userhashmap.begin(); it != m_userhashmap.end(); ++it) { - std::string key = it->first; + std::string key = it->first; CUserItem *pUserItem = it->second; if (getUserAsString(pUserItem, str)) { strAllusers += str; @@ -1380,7 +1388,8 @@ bool CUserList::getAllUsers(std::string & strAllusers) // // -bool CUserList::getAllUsers(std::deque & arrayUsers) +bool +CUserList::getAllUsers(std::deque &arrayUsers) { std::string str; @@ -1399,7 +1408,8 @@ bool CUserList::getAllUsers(std::deque & arrayUsers) // // -CUserItem *CUserList::getUserItemFromOrdinal(uint32_t idx) +CUserItem * +CUserList::getUserItemFromOrdinal(uint32_t idx) { std::string str; uint32_t i = 0; @@ -1408,7 +1418,7 @@ CUserItem *CUserList::getUserItemFromOrdinal(uint32_t idx) for (it = m_userhashmap.begin(); it != m_userhashmap.end(); ++it) { if (i == idx) { - std::string key = it->first; + std::string key = it->first; CUserItem *pUserItem = it->second; return pUserItem; } diff --git a/src/vscp/common/userlist.h b/src/vscp/common/userlist.h index ef5f6c06f..fb94049bb 100644 --- a/src/vscp/common/userlist.h +++ b/src/vscp/common/userlist.h @@ -274,13 +274,6 @@ class CUserItem { */ bool setAllowedRemotesFromString(const std::string &strConnect); - /*! - Check password for user - @param password Password to check - @return true ff password is correct - */ - bool checkPassword(const std::string &password) { return (getPassword() == password); }; - // * * * Getters/Setters * * * // UserID @@ -292,33 +285,31 @@ class CUserItem { void setUserName(const std::string &strUser) { m_user = strUser; }; /*! - Get Password - @return Return password hash on hex form (iv(16);pw(32)) - */ - std::string getPassword(void); - - /*! - Set clear text password - Password is hashed with vscp_makePasswordHash - over "username:passpword" - @param password Clear text password to set - @return true on success, false on failure. + Get Password hash + @return Return password hash */ - bool setPasswordFromClearText(const std::string &strPassword); + std::string getPasswordHash(void); /*! Set (already) hashed password. Typically used when loading user data from file. The hash should be done over "user:password" - @param password on (iv(16);pw(32)) hex form + @param pmd5hash Reference to string containing hashed password on hex format. + */ + void setPasswordHash(const std::string &md5hash); + + /*! + Set clear text password + The password is stored hashed with md5 over "username:password" + @param password Clear text password to set @return true on success, false on failure. */ - void setPassword(const std::string &strPassword) { m_password = strPassword; }; + void setPasswordFromClearText(const std::string &strPassword); /*! Validate user password @param password Clear text password to validate - @return true if user is a valied user + @return true if user is a valid user */ bool validateUser(const std::string &password); @@ -510,8 +501,7 @@ class CUserItem { std::string m_user; /*! - Password (iv(16);pw(32)) (user:password) - vscp_makePasswordHash(pw, password, NULL); + md5 hash over "user:password" */ std::string m_password; diff --git a/src/vscp/common/vscp-class.h b/src/vscp/common/vscp-class.h index 1e485abaf..40576b5cc 100644 --- a/src/vscp/common/vscp-class.h +++ b/src/vscp/common/vscp-class.h @@ -47,7 +47,7 @@ !!!!!!!!!!!!!!!!!!!! W A R N I N G !!!!!!!!!!!!!!!!!!!! This file is auto-generated see https://github.com/grodansparadis/vscp-classes - Generated: 2024-12-12 17:07:55.637062 + Generated: 2024-12-18 14:37:52.750599 */ #ifndef VSCP_CLASS_H diff --git a/src/vscp/common/vscp-hashclass.h b/src/vscp/common/vscp-hashclass.h index 3bda76a05..c304f014f 100644 --- a/src/vscp/common/vscp-hashclass.h +++ b/src/vscp/common/vscp-hashclass.h @@ -2,7 +2,7 @@ !!!!!!!!!!!!!!!!!!!! W A R N I N G !!!!!!!!!!!!!!!!!!!! This file is auto-generated see https://github.com/grodansparadis/vscp-classes - Generated: 2024-12-12 17:07:56.944066 + Generated: 2024-12-18 14:37:54.002606 */ m_hashClass[ 0 ] = _("CLASS1_PROTOCOL"); diff --git a/src/vscp/common/vscp-hashtype.h b/src/vscp/common/vscp-hashtype.h index b20464108..d456a0452 100644 --- a/src/vscp/common/vscp-hashtype.h +++ b/src/vscp/common/vscp-hashtype.h @@ -2,7 +2,7 @@ !!!!!!!!!!!!!!!!!!!! W A R N I N G !!!!!!!!!!!!!!!!!!!! This file is auto-generated see https://github.com/grodansparadis/vscp-classes - Generated: 2024-12-12 17:07:57.403175 + Generated: 2024-12-18 14:37:54.494955 */ diff --git a/src/vscp/common/vscp-type.h b/src/vscp/common/vscp-type.h index 719194a30..4e98ffc41 100644 --- a/src/vscp/common/vscp-type.h +++ b/src/vscp/common/vscp-type.h @@ -48,7 +48,7 @@ !!!!!!!!!!!!!!!!!!!! W A R N I N G !!!!!!!!!!!!!!!!!!!! This file is auto-generated see https://github.com/grodansparadis/vscp-classes - Generated: 2024-12-12 17:07:56.200428 + Generated: 2024-12-18 14:37:53.512118 */ #ifndef VSCP_TYPE_H