diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 14 | ||||
-rw-r--r-- | src/Makefile.in | 70 | ||||
-rw-r--r-- | src/cjson.c | 786 | ||||
-rw-r--r-- | src/cjson.h | 184 | ||||
-rw-r--r-- | src/dscp.c | 5 | ||||
-rw-r--r--[-rwxr-xr-x] | src/iperf.h | 0 | ||||
-rw-r--r-- | src/iperf3.1 | 6 | ||||
-rw-r--r--[-rwxr-xr-x] | src/iperf_api.c | 107 | ||||
-rw-r--r--[-rwxr-xr-x] | src/iperf_api.h | 9 | ||||
-rw-r--r-- | src/iperf_auth.c | 79 | ||||
-rw-r--r-- | src/iperf_auth.h | 1 | ||||
-rw-r--r-- | src/iperf_error.c | 6 | ||||
-rw-r--r-- | src/iperf_locale.c | 6 | ||||
-rw-r--r-- | src/iperf_locale.h | 6 | ||||
-rw-r--r-- | src/iperf_sctp.c | 58 | ||||
-rw-r--r-- | src/iperf_tcp.c | 1 | ||||
-rw-r--r-- | src/iperf_udp.c | 21 | ||||
-rw-r--r-- | src/main.c | 3 | ||||
-rw-r--r-- | src/net.c | 1 | ||||
-rw-r--r-- | src/private.pem | 27 | ||||
-rw-r--r-- | src/public.pem | 9 | ||||
-rw-r--r-- | src/t_api.c | 14 | ||||
-rw-r--r-- | src/t_auth.c | 125 | ||||
-rw-r--r-- | src/tcp_info.c | 4 | ||||
-rw-r--r-- | src/timer.h | 2 |
25 files changed, 1219 insertions, 325 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 5be8562..11d3e17 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,9 +1,9 @@ lib_LTLIBRARIES = libiperf.la # Build and install an iperf library bin_PROGRAMS = iperf3 # Build and install an iperf binary if ENABLE_PROFILING -noinst_PROGRAMS = t_timer t_units t_uuid t_api iperf3_profile # Build, but don't install the test programs and a profiled version of iperf3 +noinst_PROGRAMS = t_timer t_units t_uuid t_api t_auth iperf3_profile # Build, but don't install the test programs and a profiled version of iperf3 else -noinst_PROGRAMS = t_timer t_units t_uuid t_api # Build, but don't install the test programs +noinst_PROGRAMS = t_timer t_units t_uuid t_api t_auth # Build, but don't install the test programs endif include_HEADERS = iperf_api.h # Defines the headers that get installed with the program @@ -52,7 +52,7 @@ iperf3_LDADD = libiperf.la iperf3_LDFLAGS = -g if ENABLE_PROFILING -# If the iperf-profiled-binary is enabled (and this condition is true by default) +# If the iperf-profiled-binary is enabled # Specify the sources and various flags for the profiled iperf binary. This # binary recompiles all the source files to make sure they are all profiled. iperf3_profile_SOURCES = main.c \ @@ -84,6 +84,11 @@ t_api_CFLAGS = -g t_api_LDFLAGS = t_api_LDADD = libiperf.la +t_auth_SOURCES = t_auth.c +t_auth_CFLAGS = -g +t_auth_LDFLAGS = +t_auth_LDADD = libiperf.la + # Specify which tests to run during a "make check" @@ -91,6 +96,7 @@ TESTS = \ t_timer \ t_units \ t_uuid \ - t_api + t_api \ + t_auth dist_man_MANS = iperf3.1 libiperf.3 diff --git a/src/Makefile.in b/src/Makefile.in index 619cd45..271ef6c 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -93,15 +93,17 @@ host_triplet = @host@ bin_PROGRAMS = iperf3$(EXEEXT) @ENABLE_PROFILING_FALSE@noinst_PROGRAMS = t_timer$(EXEEXT) \ @ENABLE_PROFILING_FALSE@ t_units$(EXEEXT) t_uuid$(EXEEXT) \ -@ENABLE_PROFILING_FALSE@ t_api$(EXEEXT) +@ENABLE_PROFILING_FALSE@ t_api$(EXEEXT) t_auth$(EXEEXT) @ENABLE_PROFILING_TRUE@noinst_PROGRAMS = t_timer$(EXEEXT) \ @ENABLE_PROFILING_TRUE@ t_units$(EXEEXT) t_uuid$(EXEEXT) \ -@ENABLE_PROFILING_TRUE@ t_api$(EXEEXT) iperf3_profile$(EXEEXT) +@ENABLE_PROFILING_TRUE@ t_api$(EXEEXT) t_auth$(EXEEXT) \ +@ENABLE_PROFILING_TRUE@ iperf3_profile$(EXEEXT) TESTS = t_timer$(EXEEXT) t_units$(EXEEXT) t_uuid$(EXEEXT) \ - t_api$(EXEEXT) + t_api$(EXEEXT) t_auth$(EXEEXT) subdir = src ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/config/ax_check_openssl.m4 \ + $(top_srcdir)/config/iperf_config_static_bin.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) @@ -198,6 +200,12 @@ t_api_DEPENDENCIES = libiperf.la t_api_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(t_api_CFLAGS) $(CFLAGS) \ $(t_api_LDFLAGS) $(LDFLAGS) -o $@ +am_t_auth_OBJECTS = t_auth-t_auth.$(OBJEXT) +t_auth_OBJECTS = $(am_t_auth_OBJECTS) +t_auth_DEPENDENCIES = libiperf.la +t_auth_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(t_auth_CFLAGS) $(CFLAGS) \ + $(t_auth_LDFLAGS) $(LDFLAGS) -o $@ am_t_timer_OBJECTS = t_timer-t_timer.$(OBJEXT) t_timer_OBJECTS = $(am_t_timer_OBJECTS) t_timer_DEPENDENCIES = libiperf.la @@ -256,9 +264,10 @@ am__depfiles_remade = ./$(DEPDIR)/cjson.Plo ./$(DEPDIR)/dscp.Plo \ ./$(DEPDIR)/iperf_tcp.Plo ./$(DEPDIR)/iperf_time.Plo \ ./$(DEPDIR)/iperf_udp.Plo ./$(DEPDIR)/iperf_util.Plo \ ./$(DEPDIR)/net.Plo ./$(DEPDIR)/t_api-t_api.Po \ - ./$(DEPDIR)/t_timer-t_timer.Po ./$(DEPDIR)/t_units-t_units.Po \ - ./$(DEPDIR)/t_uuid-t_uuid.Po ./$(DEPDIR)/tcp_info.Plo \ - ./$(DEPDIR)/timer.Plo ./$(DEPDIR)/units.Plo + ./$(DEPDIR)/t_auth-t_auth.Po ./$(DEPDIR)/t_timer-t_timer.Po \ + ./$(DEPDIR)/t_units-t_units.Po ./$(DEPDIR)/t_uuid-t_uuid.Po \ + ./$(DEPDIR)/tcp_info.Plo ./$(DEPDIR)/timer.Plo \ + ./$(DEPDIR)/units.Plo am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) @@ -279,11 +288,12 @@ am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libiperf_la_SOURCES) $(iperf3_SOURCES) \ - $(iperf3_profile_SOURCES) $(t_api_SOURCES) $(t_timer_SOURCES) \ - $(t_units_SOURCES) $(t_uuid_SOURCES) + $(iperf3_profile_SOURCES) $(t_api_SOURCES) $(t_auth_SOURCES) \ + $(t_timer_SOURCES) $(t_units_SOURCES) $(t_uuid_SOURCES) DIST_SOURCES = $(libiperf_la_SOURCES) $(iperf3_SOURCES) \ $(am__iperf3_profile_SOURCES_DIST) $(t_api_SOURCES) \ - $(t_timer_SOURCES) $(t_units_SOURCES) $(t_uuid_SOURCES) + $(t_auth_SOURCES) $(t_timer_SOURCES) $(t_units_SOURCES) \ + $(t_uuid_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ @@ -294,8 +304,8 @@ man3dir = $(mandir)/man3 NROFF = nroff MANS = $(dist_man_MANS) HEADERS = $(include_HEADERS) -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ - $(LISP)iperf_config.h.in +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) \ + iperf_config.h.in # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. @@ -664,7 +674,7 @@ iperf3_CFLAGS = -g iperf3_LDADD = libiperf.la iperf3_LDFLAGS = -g -# If the iperf-profiled-binary is enabled (and this condition is true by default) +# If the iperf-profiled-binary is enabled # Specify the sources and various flags for the profiled iperf binary. This # binary recompiles all the source files to make sure they are all profiled. @ENABLE_PROFILING_TRUE@iperf3_profile_SOURCES = main.c \ @@ -691,6 +701,10 @@ t_api_SOURCES = t_api.c t_api_CFLAGS = -g t_api_LDFLAGS = t_api_LDADD = libiperf.la +t_auth_SOURCES = t_auth.c +t_auth_CFLAGS = -g +t_auth_LDFLAGS = +t_auth_LDADD = libiperf.la dist_man_MANS = iperf3.1 libiperf.3 all: iperf_config.h $(MAKE) $(AM_MAKEFLAGS) all-am @@ -852,6 +866,10 @@ t_api$(EXEEXT): $(t_api_OBJECTS) $(t_api_DEPENDENCIES) $(EXTRA_t_api_DEPENDENCIE @rm -f t_api$(EXEEXT) $(AM_V_CCLD)$(t_api_LINK) $(t_api_OBJECTS) $(t_api_LDADD) $(LIBS) +t_auth$(EXEEXT): $(t_auth_OBJECTS) $(t_auth_DEPENDENCIES) $(EXTRA_t_auth_DEPENDENCIES) + @rm -f t_auth$(EXEEXT) + $(AM_V_CCLD)$(t_auth_LINK) $(t_auth_OBJECTS) $(t_auth_LDADD) $(LIBS) + t_timer$(EXEEXT): $(t_timer_OBJECTS) $(t_timer_DEPENDENCIES) $(EXTRA_t_timer_DEPENDENCIES) @rm -f t_timer$(EXEEXT) $(AM_V_CCLD)$(t_timer_LINK) $(t_timer_OBJECTS) $(t_timer_LDADD) $(LIBS) @@ -904,6 +922,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf_util.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/net.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_api-t_api.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_auth-t_auth.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_timer-t_timer.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_units-t_units.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_uuid-t_uuid.Po@am__quote@ # am--include-marker @@ -1218,6 +1237,20 @@ t_api-t_api.obj: t_api.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_api_CFLAGS) $(CFLAGS) -c -o t_api-t_api.obj `if test -f 't_api.c'; then $(CYGPATH_W) 't_api.c'; else $(CYGPATH_W) '$(srcdir)/t_api.c'; fi` +t_auth-t_auth.o: t_auth.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_auth_CFLAGS) $(CFLAGS) -MT t_auth-t_auth.o -MD -MP -MF $(DEPDIR)/t_auth-t_auth.Tpo -c -o t_auth-t_auth.o `test -f 't_auth.c' || echo '$(srcdir)/'`t_auth.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_auth-t_auth.Tpo $(DEPDIR)/t_auth-t_auth.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='t_auth.c' object='t_auth-t_auth.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_auth_CFLAGS) $(CFLAGS) -c -o t_auth-t_auth.o `test -f 't_auth.c' || echo '$(srcdir)/'`t_auth.c + +t_auth-t_auth.obj: t_auth.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_auth_CFLAGS) $(CFLAGS) -MT t_auth-t_auth.obj -MD -MP -MF $(DEPDIR)/t_auth-t_auth.Tpo -c -o t_auth-t_auth.obj `if test -f 't_auth.c'; then $(CYGPATH_W) 't_auth.c'; else $(CYGPATH_W) '$(srcdir)/t_auth.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_auth-t_auth.Tpo $(DEPDIR)/t_auth-t_auth.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='t_auth.c' object='t_auth-t_auth.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_auth_CFLAGS) $(CFLAGS) -c -o t_auth-t_auth.obj `if test -f 't_auth.c'; then $(CYGPATH_W) 't_auth.c'; else $(CYGPATH_W) '$(srcdir)/t_auth.c'; fi` + t_timer-t_timer.o: t_timer.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_timer_CFLAGS) $(CFLAGS) -MT t_timer-t_timer.o -MD -MP -MF $(DEPDIR)/t_timer-t_timer.Tpo -c -o t_timer-t_timer.o `test -f 't_timer.c' || echo '$(srcdir)/'`t_timer.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_timer-t_timer.Tpo $(DEPDIR)/t_timer-t_timer.Po @@ -1594,6 +1627,13 @@ t_api.log: t_api$(EXEEXT) --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) +t_auth.log: t_auth$(EXEEXT) + @p='t_auth$(EXEEXT)'; \ + b='t_auth'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) .test.log: @p='$<'; \ $(am__set_b); \ @@ -1726,6 +1766,7 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/iperf_util.Plo -rm -f ./$(DEPDIR)/net.Plo -rm -f ./$(DEPDIR)/t_api-t_api.Po + -rm -f ./$(DEPDIR)/t_auth-t_auth.Po -rm -f ./$(DEPDIR)/t_timer-t_timer.Po -rm -f ./$(DEPDIR)/t_units-t_units.Po -rm -f ./$(DEPDIR)/t_uuid-t_uuid.Po @@ -1811,6 +1852,7 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/iperf_util.Plo -rm -f ./$(DEPDIR)/net.Plo -rm -f ./$(DEPDIR)/t_api-t_api.Po + -rm -f ./$(DEPDIR)/t_auth-t_auth.Po -rm -f ./$(DEPDIR)/t_timer-t_timer.Po -rm -f ./$(DEPDIR)/t_units-t_units.Po -rm -f ./$(DEPDIR)/t_uuid-t_uuid.Po diff --git a/src/cjson.c b/src/cjson.c index a31874d..bf0b8b4 100644 --- a/src/cjson.c +++ b/src/cjson.c @@ -23,23 +23,39 @@ /* cJSON */ /* JSON parser in C. */ +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + #ifdef __GNUC__ #pragma GCC visibility push(default) #endif +#if defined(_MSC_VER) +#pragma warning (push) +/* disable warning about single line comments in system headers */ +#pragma warning (disable : 4001) +#endif #include <string.h> #include <stdio.h> #include <math.h> #include <stdlib.h> -#include <float.h> #include <limits.h> #include <ctype.h> +#include <float.h> #ifdef HAVE_STDINT_H #include <stdint.h> #endif #include <sys/types.h> + +#ifdef ENABLE_LOCALES #include <locale.h> +#endif +#if defined(_MSC_VER) +#pragma warning (pop) +#endif #ifdef __GNUC__ #pragma GCC visibility pop #endif @@ -47,9 +63,28 @@ #include "cjson.h" /* define our own boolean type */ +#ifdef true +#undef true +#endif #define true ((cJSON_bool)1) + +#ifdef false +#undef false +#endif #define false ((cJSON_bool)0) +/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ +#ifndef isinf +#define isinf(d) (isnan((d - d)) && !isnan(d)) +#endif +#ifndef isnan +#define isnan(d) (d != d) +#endif + +#ifndef NAN +#define NAN 0.0/0.0 +#endif + typedef struct { const unsigned char *json; size_t position; @@ -68,8 +103,28 @@ CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) return (const char*) (global_error.json + global_error.position); } +CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item) +{ + if (!cJSON_IsString(item)) + { + return NULL; + } + + return item->valuestring; +} + +CJSON_PUBLIC(double) cJSON_GetNumberValue(cJSON *item) +{ + if (!cJSON_IsNumber(item)) + { + return NAN; + } + + return item->valuedouble; +} + /* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ -#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 5) || (CJSON_VERSION_PATCH != 2) +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 13) #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. #endif @@ -107,12 +162,35 @@ static int case_insensitive_strcmp(const unsigned char *string1, const unsigned typedef struct internal_hooks { - void *(*allocate)(size_t size); - void (*deallocate)(void *pointer); - void *(*reallocate)(void *pointer, size_t size); + void *(CJSON_CDECL *allocate)(size_t size); + void (CJSON_CDECL *deallocate)(void *pointer); + void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); } internal_hooks; -static internal_hooks global_hooks = { malloc, free, realloc }; +#if defined(_MSC_VER) +/* work around MSVC error C2322: '...' address of dllimport '...' is not static */ +static void * CJSON_CDECL internal_malloc(size_t size) +{ + return malloc(size); +} +static void CJSON_CDECL internal_free(void *pointer) +{ + free(pointer); +} +static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) +{ + return realloc(pointer, size); +} +#else +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc +#endif + +/* strlen of character literals resolved at compile time */ +#define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) + +static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) { @@ -125,7 +203,8 @@ static unsigned char* cJSON_strdup(const unsigned char* string, const internal_h } length = strlen((const char*)string) + sizeof(""); - if (!(copy = (unsigned char*)hooks->allocate(length))) + copy = (unsigned char*)hooks->allocate(length); + if (copy == NULL) { return NULL; } @@ -204,8 +283,12 @@ CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) /* get the decimal point character of the current locale */ static unsigned char get_decimal_point(void) { +#ifdef ENABLE_LOCALES struct lconv *lconv = localeconv(); return (unsigned char) lconv->decimal_point[0]; +#else + return '.'; +#endif } typedef struct @@ -219,7 +302,6 @@ typedef struct /* check if the given size is left to read in a given parse buffer (starting with 1) */ #define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) -#define cannot_read(buffer, size) (!can_read(buffer, size)) /* check if the buffer can be accessed at the given index (starting with 0) */ #define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) #define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) @@ -288,7 +370,7 @@ loop_end: { item->valueint = LLONG_MAX; } - else if (number <= LLONG_MIN) + else if (number <= (double)LLONG_MIN) { item->valueint = LLONG_MIN; } @@ -310,7 +392,7 @@ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) { object->valueint = LLONG_MAX; } - else if (number <= LLONG_MIN) + else if (number <= (double)LLONG_MIN) { object->valueint = LLONG_MIN; } @@ -322,6 +404,33 @@ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) return object->valuedouble = number; } +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) +{ + char *copy = NULL; + /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ + if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference)) + { + return NULL; + } + if (strlen(valuestring) <= strlen(object->valuestring)) + { + strcpy(object->valuestring, valuestring); + return object->valuestring; + } + copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks); + if (copy == NULL) + { + return NULL; + } + if (object->valuestring != NULL) + { + cJSON_free(object->valuestring); + } + object->valuestring = copy; + + return copy; +} + typedef struct { unsigned char *buffer; @@ -350,9 +459,9 @@ static unsigned char* ensure(printbuffer * const p, size_t needed) return NULL; } - if (needed > LLONG_MAX) + if (needed > SIZE_MAX) { - /* sizes bigger than LLONG_MAX are currently not supported */ + /* sizes bigger than SIZE_MAX are currently not supported */ return NULL; } @@ -367,12 +476,12 @@ static unsigned char* ensure(printbuffer * const p, size_t needed) } /* calculate new buffer size */ - if (needed > (LLONG_MAX / 2)) + if (needed > (SIZE_MAX / 2)) { - /* overflow of int, use LLONG_MAX if possible */ - if (needed <= LLONG_MAX) + /* overflow of int, use SIZE_MAX if possible */ + if (needed <= SIZE_MAX) { - newsize = LLONG_MAX; + newsize = SIZE_MAX; } else { @@ -388,6 +497,14 @@ static unsigned char* ensure(printbuffer * const p, size_t needed) { /* reallocate with realloc if available */ newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if (newbuffer == NULL) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } } else { @@ -426,6 +543,13 @@ static void update_offset(printbuffer * const buffer) buffer->offset += strlen((const char*)buffer_pointer); } +/* securely comparison of floating-point variables */ +static cJSON_bool compare_double(double a, double b) +{ + double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); + return (fabs(a - b) <= maxVal * DBL_EPSILON); +} + /* Render the number nicely from the given item into a string. */ static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) { @@ -433,9 +557,9 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out double d = item->valuedouble; int length = 0; size_t i = 0; - unsigned char number_buffer[26]; /* temporary buffer to print the number into */ + unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ unsigned char decimal_point = get_decimal_point(); - double test; + double test = 0.0; if (output_buffer == NULL) { @@ -443,7 +567,7 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out } /* This checks for NaN and Infinity */ - if ((d * 0) != 0) + if (isnan(d) || isinf(d)) { length = sprintf((char*)number_buffer, "null"); } @@ -453,21 +577,21 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out length = sprintf((char*)number_buffer, "%1.15g", d); /* Check whether the original double can be recovered */ - if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != d)) + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) { /* If not, print with 17 decimal places of precision */ length = sprintf((char*)number_buffer, "%1.17g", d); } } - /* sprintf failed or buffer overrun occured */ + /* sprintf failed or buffer overrun occurred */ if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) { return false; } /* reserve appropriate space in the output */ - output_pointer = ensure(output_buffer, (size_t)length); + output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); if (output_pointer == NULL) { return false; @@ -923,6 +1047,11 @@ static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) return NULL; } + if (cannot_access_at_index(buffer, 0)) + { + return buffer; + } + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) { buffer->offset++; @@ -936,9 +1065,40 @@ static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) return buffer; } -/* Parse an object - create a new root, and populate. */ +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) + { + return NULL; + } + + if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) + { + buffer->offset += 3; + } + + return buffer; +} + CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) { + size_t buffer_length; + + if (NULL == value) + { + return NULL; + } + + /* Adding null character size due to require_null_terminated. */ + buffer_length = strlen(value) + sizeof(""); + + return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) +{ parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; cJSON *item = NULL; @@ -946,13 +1106,13 @@ CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return global_error.json = NULL; global_error.position = 0; - if (value == NULL) + if (value == NULL || 0 == buffer_length) { goto fail; } buffer.content = (const unsigned char*)value; - buffer.length = strlen((const char*)value) + sizeof(""); + buffer.length = buffer_length; buffer.offset = 0; buffer.hooks = global_hooks; @@ -962,7 +1122,7 @@ CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return goto fail; } - if (!parse_value(item, buffer_skip_whitespace(&buffer))) + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) { /* parse failure. ep is set. */ goto fail; @@ -1009,10 +1169,8 @@ fail: { *return_parse_end = (const char*)local_error.json + local_error.position; } - else - { - global_error = local_error; - } + + global_error = local_error; } return NULL; @@ -1024,17 +1182,24 @@ CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) return cJSON_ParseWithOpts(value, 0, 0); } -#define cjson_min(a, b) ((a < b) ? a : b) +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) +{ + return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); +} + +#define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) { + static const size_t default_buffer_size = 256; printbuffer buffer[1]; unsigned char *printed = NULL; memset(buffer, 0, sizeof(buffer)); /* create buffer */ - buffer->buffer = (unsigned char*) hooks->allocate(256); + buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); + buffer->length = default_buffer_size; buffer->format = format; buffer->hooks = *hooks; if (buffer->buffer == NULL) @@ -1052,11 +1217,11 @@ static unsigned char *print(const cJSON * const item, cJSON_bool format, const i /* check if reallocate is available */ if (hooks->reallocate != NULL) { - printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->length); - buffer->buffer = NULL; + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); if (printed == NULL) { goto fail; } + buffer->buffer = NULL; } else /* otherwise copy the JSON over to a new buffer */ { @@ -1122,26 +1287,27 @@ CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON if (!print_value(item, &p)) { + global_hooks.deallocate(p.buffer); return NULL; } return (char*)p.buffer; } -CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt) +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) { printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; - if (len < 0) + if ((length < 0) || (buffer == NULL)) { return false; } - p.buffer = (unsigned char*)buf; - p.length = (size_t)len; + p.buffer = (unsigned char*)buffer; + p.length = (size_t)length; p.offset = 0; p.noalloc = true; - p.format = fmt; + p.format = format; p.hooks = global_hooks; return print_value(item, &p); @@ -1199,7 +1365,6 @@ static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buf return parse_object(item, input_buffer); } - return false; } @@ -1250,10 +1415,6 @@ static cJSON_bool print_value(const cJSON * const item, printbuffer * const outp size_t raw_length = 0; if (item->valuestring == NULL) { - if (!output_buffer->noalloc) - { - output_buffer->hooks.deallocate(output_buffer->buffer); - } return false; } @@ -1499,7 +1660,7 @@ static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_bu buffer_skip_whitespace(input_buffer); if (!parse_string(current_item, input_buffer)) { - goto fail; /* faile to parse name */ + goto fail; /* failed to parse name */ } buffer_skip_whitespace(input_buffer); @@ -1619,7 +1780,7 @@ static cJSON_bool print_object(const cJSON * const item, printbuffer * const out update_offset(output_buffer); /* print comma if not last */ - length = (size_t) ((output_buffer->format ? 1 : 0) + (current_item->next ? 1 : 0)); + length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); output_pointer = ensure(output_buffer, length + 1); if (output_pointer == NULL) { @@ -1663,17 +1824,25 @@ static cJSON_bool print_object(const cJSON * const item, printbuffer * const out /* Get Array size/item / object item. */ CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) { - cJSON *c = array->child; - size_t i = 0; - while(c) + cJSON *child = NULL; + size_t size = 0; + + if (array == NULL) + { + return 0; + } + + child = array->child; + + while(child != NULL) { - i++; - c = c->next; + size++; + child = child->next; } /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ - return (int)i; + return (int)size; } static cJSON* get_array_item(const cJSON *array, size_t index) @@ -1717,7 +1886,7 @@ static cJSON *get_object_item(const cJSON * const object, const char * const nam current_element = object->child; if (case_sensitive) { - while ((current_element != NULL) && (strcmp(name, current_element->string) != 0)) + while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) { current_element = current_element->next; } @@ -1730,6 +1899,10 @@ static cJSON *get_object_item(const cJSON * const object, const char * const nam } } + if ((current_element == NULL) || (current_element->string == NULL)) { + return NULL; + } + return current_element; } @@ -1758,88 +1931,263 @@ static void suffix_object(cJSON *prev, cJSON *item) /* Utility for handling references. */ static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) { - cJSON *ref = cJSON_New_Item(hooks); - if (!ref) + cJSON *reference = NULL; + if (item == NULL) + { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if (reference == NULL) { return NULL; } - memcpy(ref, item, sizeof(cJSON)); - ref->string = NULL; - ref->type |= cJSON_IsReference; - ref->next = ref->prev = NULL; - return ref; + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; } -/* Add item to array/object. */ -CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item) +static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) { cJSON *child = NULL; - if ((item == NULL) || (array == NULL)) + if ((item == NULL) || (array == NULL) || (array == item)) { - return; + return false; } child = array->child; - + /* + * To find the last item in array quickly, we use prev in array + */ if (child == NULL) { /* list is empty, start new one */ array->child = item; + item->prev = item; + item->next = NULL; } else { /* append to the end */ - while (child->next) + if (child->prev) { - child = child->next; + suffix_object(child->prev, item); + array->child->prev = item; + } + else + { + while (child->next) + { + child = child->next; + } + suffix_object(child, item); + array->child->prev = item; } - suffix_object(child, item); } + + return true; } -CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +/* Add item to array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) { - /* call cJSON_AddItemToObjectCS for code reuse */ - cJSON_AddItemToObjectCS(object, (char*)cJSON_strdup((const unsigned char*)string, &global_hooks), item); - /* remove cJSON_StringIsConst flag */ - item->type &= ~cJSON_StringIsConst; + return add_item_to_array(array, item); } -#if defined (__clang__) || ((__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) #pragma GCC diagnostic push #endif #ifdef __GNUC__ #pragma GCC diagnostic ignored "-Wcast-qual" #endif +/* helper function to cast away const */ +static void* cast_away_const(const void* string) +{ + return (void*)string; +} +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop +#endif + + +static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) +{ + char *new_key = NULL; + int new_type = cJSON_Invalid; + + if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) + { + return false; + } + + if (constant_key) + { + new_key = (char*)cast_away_const(string); + new_type = item->type | cJSON_StringIsConst; + } + else + { + new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); + if (new_key == NULL) + { + return false; + } + + new_type = item->type & ~cJSON_StringIsConst; + } + + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + hooks->deallocate(item->string); + } + + item->string = new_key; + item->type = new_type; + + return add_item_to_array(object, item); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + return add_item_to_object(object, string, item, &global_hooks, false); +} /* Add an item to an object with constant string as key */ -CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) { - if (!item) + return add_item_to_object(object, string, item, &global_hooks, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + if (array == NULL) { - return; + return false; + } + + return add_item_to_array(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +{ + if ((object == NULL) || (string == NULL)) + { + return false; } - if (!(item->type & cJSON_StringIsConst) && item->string) + + return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) +{ + cJSON *null = cJSON_CreateNull(); + if (add_item_to_object(object, name, null, &global_hooks, false)) { - global_hooks.deallocate(item->string); + return null; } - item->string = (char*)string; - item->type |= cJSON_StringIsConst; - cJSON_AddItemToArray(object, item); + + cJSON_Delete(null); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) +{ + cJSON *true_item = cJSON_CreateTrue(); + if (add_item_to_object(object, name, true_item, &global_hooks, false)) + { + return true_item; + } + + cJSON_Delete(true_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) +{ + cJSON *false_item = cJSON_CreateFalse(); + if (add_item_to_object(object, name, false_item, &global_hooks, false)) + { + return false_item; + } + + cJSON_Delete(false_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) +{ + cJSON *bool_item = cJSON_CreateBool(boolean); + if (add_item_to_object(object, name, bool_item, &global_hooks, false)) + { + return bool_item; + } + + cJSON_Delete(bool_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) +{ + cJSON *number_item = cJSON_CreateNumber(number); + if (add_item_to_object(object, name, number_item, &global_hooks, false)) + { + return number_item; + } + + cJSON_Delete(number_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) +{ + cJSON *string_item = cJSON_CreateString(string); + if (add_item_to_object(object, name, string_item, &global_hooks, false)) + { + return string_item; + } + + cJSON_Delete(string_item); + return NULL; } -#if defined (__clang__) || ((__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) - #pragma GCC diagnostic pop -#endif -CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) { - cJSON_AddItemToArray(array, create_reference(item, &global_hooks)); + cJSON *raw_item = cJSON_CreateRaw(raw); + if (add_item_to_object(object, name, raw_item, &global_hooks, false)) + { + return raw_item; + } + + cJSON_Delete(raw_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) +{ + cJSON *object_item = cJSON_CreateObject(); + if (add_item_to_object(object, name, object_item, &global_hooks, false)) + { + return object_item; + } + + cJSON_Delete(object_item); + return NULL; } -CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) { - cJSON_AddItemToObject(object, string, create_reference(item, &global_hooks)); + cJSON *array = cJSON_CreateArray(); + if (add_item_to_object(object, name, array, &global_hooks, false)) + { + return array; + } + + cJSON_Delete(array); + return NULL; } CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) @@ -1849,7 +2197,7 @@ CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const it return NULL; } - if (item->prev != NULL) + if (item != parent->child) { /* not the first element */ item->prev->next = item->next; @@ -1912,20 +2260,19 @@ CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const } /* Replace array/object items with new ones. */ -CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) { cJSON *after_inserted = NULL; if (which < 0) { - return; + return false; } after_inserted = get_array_item(array, (size_t)which); if (after_inserted == NULL) { - cJSON_AddItemToArray(array, newitem); - return; + return add_item_to_array(array, newitem); } newitem->next = after_inserted; @@ -1939,11 +2286,12 @@ CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newit { newitem->prev->next = newitem; } + return true; } CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) { - if ((parent == NULL) || (replacement == NULL)) + if ((parent == NULL) || (replacement == NULL) || (item == NULL)) { return false; } @@ -1960,14 +2308,20 @@ CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON { replacement->next->prev = replacement; } - if (replacement->prev != NULL) - { - replacement->prev->next = replacement; - } if (parent->child == item) { parent->child = replacement; } + else + { /* + * To find the last item in array quickly, we use prev in array. + * We can't modify the last item's next pointer where this item was the parent's child + */ + if (replacement->prev != NULL) + { + replacement->prev->next = replacement; + } + } item->next = NULL; item->prev = NULL; @@ -1976,24 +2330,42 @@ CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON return true; } -CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) { if (which < 0) { - return; + return false; + } + + return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) +{ + if ((replacement == NULL) || (string == NULL)) + { + return false; + } + + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) + { + cJSON_free(replacement->string); } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + replacement->type &= ~cJSON_StringIsConst; - cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); + return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); } -CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) { - cJSON_ReplaceItemViaPointer(object, cJSON_GetObjectItem(object, string), newitem); + return replace_item_in_object(object, string, newitem, false); } -CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) { - cJSON_ReplaceItemViaPointer(object, cJSON_GetObjectItemCaseSensitive(object, string), newitem); + return replace_item_in_object(object, string, newitem, true); } /* Create basic types: */ @@ -2030,12 +2402,12 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) return item; } -CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b) +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) { cJSON *item = cJSON_New_Item(&global_hooks); if(item) { - item->type = b ? cJSON_True : cJSON_False; + item->type = boolean ? cJSON_True : cJSON_False; } return item; @@ -2054,7 +2426,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) { item->valueint = LLONG_MAX; } - else if (num <= LLONG_MIN) + else if (num <= (double)LLONG_MIN) { item->valueint = LLONG_MIN; } @@ -2084,6 +2456,39 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) return item; } +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) + { + item->type = cJSON_String | cJSON_IsReference; + item->valuestring = (char*)cast_away_const(string); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Object | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Array | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) { cJSON *item = cJSON_New_Item(&global_hooks); @@ -2131,7 +2536,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) cJSON *p = NULL; cJSON *a = NULL; - if (count < 0) + if ((count < 0) || (numbers == NULL)) { return NULL; } @@ -2166,7 +2571,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) cJSON *p = NULL; cJSON *a = NULL; - if (count < 0) + if ((count < 0) || (numbers == NULL)) { return NULL; } @@ -2202,7 +2607,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) cJSON *p = NULL; cJSON *a = NULL; - if (count < 0) + if ((count < 0) || (numbers == NULL)) { return NULL; } @@ -2231,14 +2636,14 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) return a; } -CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count) +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) { size_t i = 0; cJSON *n = NULL; cJSON *p = NULL; cJSON *a = NULL; - if (count < 0) + if ((count < 0) || (strings == NULL)) { return NULL; } @@ -2347,63 +2752,96 @@ fail: return NULL; } -CJSON_PUBLIC(void) cJSON_Minify(char *json) +static void skip_oneline_comment(char **input) { - unsigned char *into = (unsigned char*)json; - while (*json) + *input += static_strlen("//"); + + for (; (*input)[0] != '\0'; ++(*input)) { - if (*json == ' ') - { - json++; - } - else if (*json == '\t') - { - /* Whitespace characters. */ - json++; + if ((*input)[0] == '\n') { + *input += static_strlen("\n"); + return; } - else if (*json == '\r') - { - json++; - } - else if (*json=='\n') + } +} + +static void skip_multiline_comment(char **input) +{ + *input += static_strlen("/*"); + + for (; (*input)[0] != '\0'; ++(*input)) + { + if (((*input)[0] == '*') && ((*input)[1] == '/')) { - json++; + *input += static_strlen("*/"); + return; } - else if ((*json == '/') && (json[1] == '/')) - { - /* double-slash comments, to end of line. */ - while (*json && (*json != '\n')) - { - json++; - } + } +} + +static void minify_string(char **input, char **output) { + (*output)[0] = (*input)[0]; + *input += static_strlen("\""); + *output += static_strlen("\""); + + + for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { + (*output)[0] = (*input)[0]; + + if ((*input)[0] == '\"') { + (*output)[0] = '\"'; + *input += static_strlen("\""); + *output += static_strlen("\""); + return; + } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { + (*output)[1] = (*input)[1]; + *input += static_strlen("\""); + *output += static_strlen("\""); } - else if ((*json == '/') && (json[1] == '*')) + } +} + +CJSON_PUBLIC(void) cJSON_Minify(char *json) +{ + char *into = json; + + if (json == NULL) + { + return; + } + + while (json[0] != '\0') + { + switch (json[0]) { - /* multiline comments. */ - while (*json && !((*json == '*') && (json[1] == '/'))) - { + case ' ': + case '\t': + case '\r': + case '\n': json++; - } - json += 2; - } - else if (*json == '\"') - { - /* string literals, which are \" sensitive. */ - *into++ = (unsigned char)*json++; - while (*json && (*json != '\"')) - { - if (*json == '\\') + break; + + case '/': + if (json[1] == '/') { - *into++ = (unsigned char)*json++; + skip_oneline_comment(&json); } - *into++ = (unsigned char)*json++; - } - *into++ = (unsigned char)*json++; - } - else - { - /* All other characters. */ - *into++ = (unsigned char)*json++; + else if (json[1] == '*') + { + skip_multiline_comment(&json); + } else { + json++; + } + break; + + case '\"': + minify_string(&json, (char**)&into); + break; + + default: + into[0] = json[0]; + json++; + into++; } } @@ -2550,7 +2988,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons return true; case cJSON_Number: - if (a->valuedouble == b->valuedouble) + if (compare_double(a->valuedouble, b->valuedouble)) { return true; } @@ -2585,16 +3023,22 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons b_element = b_element->next; } + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + return true; } case cJSON_Object: { cJSON *a_element = NULL; + cJSON *b_element = NULL; cJSON_ArrayForEach(a_element, a) { /* TODO This has O(n^2) runtime, which is horrible! */ - cJSON *b_element = get_object_item(b, a_element->string, case_sensitive); + b_element = get_object_item(b, a_element->string, case_sensitive); if (b_element == NULL) { return false; @@ -2606,6 +3050,22 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons } } + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) + { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) + { + return false; + } + } + return true; } diff --git a/src/cjson.h b/src/cjson.h index fa7cb73..03bc353 100644 --- a/src/cjson.h +++ b/src/cjson.h @@ -33,10 +33,60 @@ extern "C" { #endif +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif + +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +#define CJSON_CDECL __cdecl +#define CJSON_STDCALL __stdcall + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type CJSON_STDCALL +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL +#endif +#else /* !__WINDOWS__ */ +#define CJSON_CDECL +#define CJSON_STDCALL + +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + /* project version */ #define CJSON_VERSION_MAJOR 1 -#define CJSON_VERSION_MINOR 5 -#define CJSON_VERSION_PATCH 2 +#define CJSON_VERSION_MINOR 7 +#define CJSON_VERSION_PATCH 13 #include <stddef.h> @@ -79,55 +129,13 @@ typedef struct cJSON typedef struct cJSON_Hooks { - void *(*malloc_fn)(size_t sz); - void (*free_fn)(void *ptr); + /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ + void *(CJSON_CDECL *malloc_fn)(size_t sz); + void (CJSON_CDECL *free_fn)(void *ptr); } cJSON_Hooks; typedef int cJSON_bool; -#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) -#define __WINDOWS__ -#endif -#ifdef __WINDOWS__ - -/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 2 define options: - -CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols -CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) -CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol - -For *nix builds that support visibility attribute, you can define similar behavior by - -setting default visibility to hidden by adding --fvisibility=hidden (for gcc) -or --xldscope=hidden (for sun cc) -to CFLAGS - -then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does - -*/ - -/* export symbols by default, this is necessary for copy pasting the C and header file */ -#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) -#define CJSON_EXPORT_SYMBOLS -#endif - -#if defined(CJSON_HIDE_SYMBOLS) -#define CJSON_PUBLIC(type) type __stdcall -#elif defined(CJSON_EXPORT_SYMBOLS) -#define CJSON_PUBLIC(type) __declspec(dllexport) type __stdcall -#elif defined(CJSON_IMPORT_SYMBOLS) -#define CJSON_PUBLIC(type) __declspec(dllimport) type __stdcall -#endif -#else /* !WIN32 */ -#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) -#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type -#else -#define CJSON_PUBLIC(type) type -#endif -#endif - /* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. * This is to prevent stack overflows. */ #ifndef CJSON_NESTING_LIMIT @@ -143,6 +151,12 @@ CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); /* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ /* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated); + /* Render a cJSON entity to text for transfer/storage. */ CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); /* Render a cJSON entity to text for transfer/storage without any formatting. */ @@ -153,11 +167,11 @@ CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON /* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); /* Delete a cJSON entity and all subentities. */ -CJSON_PUBLIC(void) cJSON_Delete(cJSON *c); +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); /* Returns the number of items in an array (or object). */ CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); -/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ +/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); /* Get item "string" from object. Case insensitive. */ CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); @@ -166,6 +180,10 @@ CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *st /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); +/* Check item type and return its value */ +CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item); +CJSON_PUBLIC(double) cJSON_GetNumberValue(cJSON *item); + /* These functions check the type of an item */ CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); @@ -190,24 +208,33 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); -/* These utilities create an Array of count items. */ +/* Create a string where valuestring references a string so + * it will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); +/* Create an object/array that only references it's elements so + * they will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); + +/* These utilities create an Array of count items. + * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); -CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); /* Append item to the specified array/object. */ -CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item); -CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before * writing to `item->string` */ -CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); /* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ -CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); -CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); -/* Remove/Detatch items from Arrays/Objects. */ +/* Remove/Detach items from Arrays/Objects. */ CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); @@ -217,44 +244,47 @@ CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); /* Update array items. */ -CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); -CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); -CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); -CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); /* Duplicate a cJSON item */ CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); /* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will -need to be released. With recurse!=0, it will duplicate any children connected to the item. -The item->next and ->prev pointers are always zero on return from Duplicate. */ + * need to be released. With recurse!=0, it will duplicate any children connected to the item. + * The item->next and ->prev pointers are always zero on return from Duplicate. */ /* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); - -/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ -/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error. If not, then cJSON_GetErrorPtr() does the job. */ -CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); - +/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. + * The input pointer json cannot point to a read-only address area, such as a string constant, + * but should point to a readable and writable adress area. */ CJSON_PUBLIC(void) cJSON_Minify(char *json); -/* Macros for creating things quickly. */ -#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) -#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) -#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) -#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b)) -#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) -#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) -#define cJSON_AddRawToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateRaw(s)) +/* Helper functions for creating and adding items to an object at the same time. + * They return the added item or NULL on failure. */ +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); /* When assigning an integer value, it needs to be propagated to valuedouble too. */ #define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) /* helper for the cJSON_SetNumberValue macro */ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); #define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) +/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); -/* Macro for iterating over an array */ +/* Macro for iterating over an array or object */ #define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) /* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ @@ -27,6 +27,7 @@ #include <stdio.h> #include <string.h> +#include <strings.h> #include <stdlib.h> #include <inttypes.h> @@ -42,10 +43,10 @@ const char * iptos2str(int iptos); * Definitions for IP type of service (ip_tos) */ -#if HAVE_NETINET_IN_SYSTM_H +#ifdef HAVE_NETINET_IN_SYSTM_H #include <netinet/in_systm.h> #endif -#if HAVE_NETINET_IP_H +#ifdef HAVE_NETINET_IP_H #include <netinet/ip.h> #endif diff --git a/src/iperf.h b/src/iperf.h index 6ce77f5..6ce77f5 100755..100644 --- a/src/iperf.h +++ b/src/iperf.h diff --git a/src/iperf3.1 b/src/iperf3.1 index 8b08fcb..93fe7b9 100644 --- a/src/iperf3.1 +++ b/src/iperf3.1 @@ -1,4 +1,4 @@ -.TH IPERF3 1 "June 2018" ESnet "User Manuals" +.TH IPERF3 1 "June 2019" ESnet "User Manuals" .SH NAME iperf3 \- perform network throughput tests .SH SYNOPSIS @@ -278,6 +278,10 @@ number of parallel client streams to run. Note that iperf3 is single threaded, s reverse the direction of a test, so that the server sends data to the client .TP +.BR --bidir +test in both directions (normal and reverse), with both the client and +server sending and receiving data simultaneously +.TP .BR -w ", " --window " \fIn\fR[KM]" window size / socket buffer size (this gets sent to the server and used on that side too) .TP diff --git a/src/iperf_api.c b/src/iperf_api.c index e1bbfa5..ed643b7 100755..100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -1,5 +1,5 @@ /* - * iperf, Copyright (c) 2014-2019, The Regents of the University of + * iperf, Copyright (c) 2014-2020, The Regents of the University of * California, through Lawrence Berkeley National Laboratory (subject * to receipt of any required approvals from the U.S. Dept. of * Energy). All rights reserved. @@ -63,6 +63,10 @@ #include <sys/cpuset.h> #endif /* HAVE_CPUSET_SETAFFINITY */ +#if defined(__CYGWIN__) || defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__) +#define CPU_SETSIZE __CPU_SETSIZE +#endif /* __CYGWIN__, _WIN32, _WIN64, __WINDOWS__ */ + #if defined(HAVE_SETPROCESSAFFINITYMASK) #include <Windows.h> #endif /* HAVE_SETPROCESSAFFINITYMASK */ @@ -340,6 +344,12 @@ iperf_get_test_no_delay(struct iperf_test *ipt) return ipt->no_delay; } +int +iperf_get_test_connect_timeout(struct iperf_test *ipt) +{ + return ipt->settings->connect_timeout; +} + /************** Setter routines for some fields inside iperf_test *************/ void @@ -470,7 +480,9 @@ iperf_set_test_role(struct iperf_test *ipt, char role) { ipt->role = role; if (!ipt->reverse) { - if (role == 'c') + if (ipt->bidirectional) + ipt->mode = BIDIRECTIONAL; + else if (role == 'c') ipt->mode = SENDER; else if (role == 's') ipt->mode = RECEIVER; @@ -547,13 +559,13 @@ iperf_set_test_unit_format(struct iperf_test *ipt, char unit_format) void iperf_set_test_client_username(struct iperf_test *ipt, char *client_username) { - ipt->settings->client_username = client_username; + ipt->settings->client_username = strdup(client_username); } void iperf_set_test_client_password(struct iperf_test *ipt, char *client_password) { - ipt->settings->client_password = client_password; + ipt->settings->client_password = strdup(client_password); } void @@ -561,6 +573,18 @@ iperf_set_test_client_rsa_pubkey(struct iperf_test *ipt, char *client_rsa_pubkey { ipt->settings->client_rsa_pubkey = load_pubkey_from_base64(client_rsa_pubkey_base64); } + +void +iperf_set_test_server_authorized_users(struct iperf_test *ipt, char *server_authorized_users) +{ + ipt->server_authorized_users = strdup(server_authorized_users); +} + +void +iperf_set_test_server_rsa_privkey(struct iperf_test *ipt, char *server_rsa_privkey_base64) +{ + ipt->server_rsa_private_key = load_privkey_from_base64(server_rsa_privkey_base64); +} #endif // HAVE_SSL void @@ -590,7 +614,7 @@ iperf_set_test_tos(struct iperf_test *ipt, int tos) void iperf_set_test_extra_data(struct iperf_test *ipt, char *dat) { - ipt->extra_data = dat; + ipt->extra_data = strdup(dat); } void @@ -609,6 +633,13 @@ iperf_set_test_no_delay(struct iperf_test* ipt, int no_delay) ipt->no_delay = no_delay; } +void +iperf_set_test_connect_timeout(struct iperf_test* ipt, int ct) +{ + ipt->settings->connect_timeout = ct; +} + + /********************** Get/set test protocol structure ***********************/ struct protocol * @@ -755,7 +786,7 @@ iperf_on_connect(struct iperf_test *test) } } if (test->settings->rate) - iperf_printf(test, " Target Bitrate: %llu\n", test->settings->rate); + iperf_printf(test, " Target Bitrate: %"PRIu64"\n", test->settings->rate); } } @@ -1263,12 +1294,6 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) else if (iperf_getpass(&client_password, &s, stdin) < 0){ return -1; } - - if (strlen(client_username) > 20 || strlen(client_password) > 20){ - i_errno = IESETCLIENTAUTH; - return -1; - } - if (test_load_pubkey_from_file(client_rsa_public_key) < 0){ i_errno = IESETCLIENTAUTH; return -1; @@ -1397,7 +1422,7 @@ iperf_check_throttle(struct iperf_stream *sp, struct iperf_time *nowP) double seconds; uint64_t bits_per_second; - if (sp->test->done) + if (sp->test->done || sp->test->settings->rate == 0 || sp->test->settings->burst != 0) return; iperf_time_diff(&sp->result->start_time_fixed, nowP, &temp_time); seconds = iperf_time_in_secs(&temp_time); @@ -1442,8 +1467,7 @@ iperf_send(struct iperf_test *test, fd_set *write_setP) streams_active = 1; test->bytes_sent += r; ++test->blocks_sent; - if (test->settings->rate != 0 && test->settings->burst == 0) - iperf_check_throttle(sp, &now); + iperf_check_throttle(sp, &now); if (multisend > 1 && test->settings->bytes != 0 && test->bytes_sent >= test->settings->bytes) break; if (multisend > 1 && test->settings->blocks != 0 && test->blocks_sent >= test->settings->blocks) @@ -1559,15 +1583,18 @@ int test_is_authorized(struct iperf_test *test){ if (test->settings->authtoken){ char *username = NULL, *password = NULL; time_t ts; - decode_auth_setting(test->debug, test->settings->authtoken, test->server_rsa_private_key, &username, &password, &ts); + int rc = decode_auth_setting(test->debug, test->settings->authtoken, test->server_rsa_private_key, &username, &password, &ts); + if (rc) { + return -1; + } int ret = check_authentication(username, password, ts, test->server_authorized_users); if (ret == 0){ - iperf_printf(test, report_authetication_successed, username, ts); + iperf_printf(test, report_authentication_succeeded, username, ts); free(username); free(password); return 0; } else { - iperf_printf(test, report_authetication_failed, username, ts); + iperf_printf(test, report_authentication_failed, username, ts); free(username); free(password); return -1; @@ -1730,15 +1757,25 @@ send_parameters(struct iperf_test *test) if (test->repeating_payload) cJSON_AddNumberToObject(j, "repeating_payload", test->repeating_payload); #if defined(HAVE_SSL) - if (test->settings->client_username && test->settings->client_password && test->settings->client_rsa_pubkey){ - encode_auth_setting(test->settings->client_username, test->settings->client_password, test->settings->client_rsa_pubkey, &test->settings->authtoken); - cJSON_AddStringToObject(j, "authtoken", test->settings->authtoken); - } + /* Send authentication parameters */ + if (test->settings->client_username && test->settings->client_password && test->settings->client_rsa_pubkey){ + int rc = encode_auth_setting(test->settings->client_username, test->settings->client_password, test->settings->client_rsa_pubkey, &test->settings->authtoken); + + if (rc) { + cJSON_Delete(j); + i_errno = IESENDPARAMS; + return -1; + } + + cJSON_AddStringToObject(j, "authtoken", test->settings->authtoken); + } #endif // HAVE_SSL cJSON_AddStringToObject(j, "client_version", IPERF_VERSION); if (test->debug) { - printf("send_parameters:\n%s\n", cJSON_Print(j)); + char *str = cJSON_Print(j); + printf("send_parameters:\n%s\n", str); + cJSON_free(str); } if (JSON_write(test->ctrl_sck, j) < 0) { @@ -1768,7 +1805,7 @@ get_parameters(struct iperf_test *test) char *str; str = cJSON_Print(j); printf("get_parameters:\n%s\n", str ); - free(str); + cJSON_free(str); } if ((j_p = cJSON_GetObjectItem(j, "tcp")) != NULL) @@ -1833,8 +1870,8 @@ get_parameters(struct iperf_test *test) #endif //HAVE_SSL if (test->mode && test->protocol->id == Ptcp && has_tcpinfo_retransmits()) test->sender_has_retransmits = 1; - if (test->settings->rate) - cJSON_AddNumberToObject(test->json_start, "target_bitrate", test->settings->rate); + if (test->settings->rate) + cJSON_AddNumberToObject(test->json_start, "target_bitrate", test->settings->rate); cJSON_Delete(j); } return r; @@ -1935,7 +1972,7 @@ send_results(struct iperf_test *test) if (r == 0 && test->debug) { char *str = cJSON_Print(j); printf("send_results\n%s\n", str); - free(str); + cJSON_free(str); } if (r == 0 && JSON_write(test->ctrl_sck, j) < 0) { i_errno = IESENDRESULTS; @@ -1993,7 +2030,7 @@ get_results(struct iperf_test *test) if (test->debug) { char *str = cJSON_Print(j); printf("get_results\n%s\n", str); - free(str); + cJSON_free(str); } test->remote_cpu_util[0] = j_cpu_util_total->valuedouble; @@ -2130,7 +2167,7 @@ JSON_write(int fd, cJSON *json) if (Nwrite(fd, str, hsize, Ptcp) < 0) r = -1; } - free(str); + cJSON_free(str); } return r; } @@ -3417,7 +3454,9 @@ iperf_print_results(struct iperf_test *test) /* Print server output if we're on the client and it was requested/provided */ if (test->role == 'c' && iperf_get_test_get_server_output(test) && !test->json_output) { if (test->json_server_output) { - iperf_printf(test, "\nServer JSON output:\n%s\n", cJSON_Print(test->json_server_output)); + char *str = cJSON_Print(test->json_server_output); + iperf_printf(test, "\nServer JSON output:\n%s\n", str); + cJSON_free(str); cJSON_Delete(test->json_server_output); test->json_server_output = NULL; } @@ -3849,9 +3888,15 @@ diskfile_recv(struct iperf_stream *sp) void iperf_catch_sigend(void (*handler)(int)) { +#ifdef SIGINT signal(SIGINT, handler); +#endif +#ifdef SIGTERM signal(SIGTERM, handler); +#endif +#ifdef SIGHUP signal(SIGHUP, handler); +#endif } /** @@ -3995,6 +4040,8 @@ iperf_json_finish(struct iperf_test *test) return -1; fprintf(test->outfile, "%s\n", test->json_output_string); iflush(test); + cJSON_free(test->json_output_string); + test->json_output_string = NULL; cJSON_Delete(test->json_top); test->json_top = test->json_start = test->json_connected = test->json_intervals = test->json_server_output = test->json_end = NULL; return 0; diff --git a/src/iperf_api.h b/src/iperf_api.h index f9f964e..3770b37 100755..100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -1,5 +1,5 @@ /* - * iperf, Copyright (c) 2014-2019, The Regents of the University of + * iperf, Copyright (c) 2014-2020, The Regents of the University of * California, through Lawrence Berkeley National Laboratory (subject * to receipt of any required approvals from the U.S. Dept. of * Energy). All rights reserved. @@ -128,6 +128,7 @@ int iperf_get_test_tos( struct iperf_test* ipt ); char* iperf_get_extra_data( struct iperf_test* ipt ); char* iperf_get_iperf_version(void); int iperf_get_test_no_delay( struct iperf_test* ipt ); +int iperf_get_test_connect_timeout( struct iperf_test* ipt ); /* Setter routines for some fields inside iperf_test. */ void iperf_set_verbose( struct iperf_test* ipt, int verbose ); @@ -160,7 +161,7 @@ void iperf_set_test_bind_address( struct iperf_test* ipt, char *bind_address ); void iperf_set_test_udp_counters_64bit( struct iperf_test* ipt, int udp_counters_64bit ); void iperf_set_test_one_off( struct iperf_test* ipt, int one_off ); void iperf_set_test_tos( struct iperf_test* ipt, int tos ); -void iperf_set_extra_data( struct iperf_test* ipt, char *dat); +void iperf_set_test_extra_data( struct iperf_test* ipt, char *dat ); void iperf_set_test_bidirectional( struct iperf_test* ipt, int bidirectional); void iperf_set_test_no_delay( struct iperf_test* ipt, int no_delay); @@ -168,8 +169,12 @@ void iperf_set_test_no_delay( struct iperf_test* ipt, int no_delay); void iperf_set_test_client_username(struct iperf_test *ipt, char *client_username); void iperf_set_test_client_password(struct iperf_test *ipt, char *client_password); void iperf_set_test_client_rsa_pubkey(struct iperf_test *ipt, char *client_rsa_pubkey_base64); +void iperf_set_test_server_authorized_users(struct iperf_test *ipt, char *server_authorized_users); +void iperf_set_test_server_rsa_privkey(struct iperf_test *ipt, char *server_rsa_privkey_base64); #endif // HAVE_SSL +void iperf_set_test_connect_timeout(struct iperf_test *ipt, int ct); + /** * exchange_parameters - handles the param_Exchange part for client * diff --git a/src/iperf_auth.c b/src/iperf_auth.c index 9965e19..46211e0 100644 --- a/src/iperf_auth.c +++ b/src/iperf_auth.c @@ -1,5 +1,5 @@ /* - * iperf, Copyright (c) 2014-2018, The Regents of the University of + * iperf, Copyright (c) 2014-2020, The Regents of the University of * California, through Lawrence Berkeley National Laboratory (subject * to receipt of any required approvals from the U.S. Dept. of * Energy). All rights reserved. @@ -43,6 +43,9 @@ #include <openssl/pem.h> #include <openssl/sha.h> #include <openssl/buffer.h> +#include <openssl/err.h> + +const char *auth_text_format = "user: %s\npwd: %s\nts: %ld"; void sha256(const char *string, char outputBuffer[65]) { @@ -151,7 +154,6 @@ int Base64Decode(const char* b64message, unsigned char** buffer, size_t* length) return (0); //success } - EVP_PKEY *load_pubkey_from_file(const char *file) { BIO *key = NULL; EVP_PKEY *pkey = NULL; @@ -159,7 +161,7 @@ EVP_PKEY *load_pubkey_from_file(const char *file) { if (file) { key = BIO_new_file(file, "r"); pkey = PEM_read_bio_PUBKEY(key, NULL, NULL, NULL); - + BIO_free(key); } return (pkey); @@ -173,6 +175,7 @@ EVP_PKEY *load_pubkey_from_base64(const char *buffer) { BIO* bio = BIO_new(BIO_s_mem()); BIO_write(bio, key, key_len); EVP_PKEY *pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); + BIO_free(bio); return (pkey); } @@ -189,6 +192,17 @@ EVP_PKEY *load_privkey_from_file(const char *file) { return (pkey); } +EVP_PKEY *load_privkey_from_base64(const char *buffer) { + unsigned char *key = NULL; + size_t key_len; + Base64Decode(buffer, &key, &key_len); + + BIO* bio = BIO_new(BIO_s_mem()); + BIO_write(bio, key, key_len); + EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + BIO_free(bio); + return (pkey); +} int test_load_pubkey_from_file(const char *file){ EVP_PKEY *key = load_pubkey_from_file(file); @@ -227,6 +241,11 @@ int encrypt_rsa_message(const char *plaintext, EVP_PKEY *public_key, unsigned ch OPENSSL_free(rsa_buffer); BIO_free(bioBuff); + if (encryptedtext_len < 0) { + /* We probably shoudln't be printing stuff like this */ + fprintf(stderr, "%s\n", ERR_error_string(ERR_get_error(), NULL)); + } + return encryptedtext_len; } @@ -249,17 +268,36 @@ int decrypt_rsa_message(const unsigned char *encryptedtext, const int encryptedt OPENSSL_free(rsa_buffer); BIO_free(bioBuff); + if (plaintext_len < 0) { + /* We probably shoudln't be printing stuff like this */ + fprintf(stderr, "%s\n", ERR_error_string(ERR_get_error(), NULL)); + } + return plaintext_len; } int encode_auth_setting(const char *username, const char *password, EVP_PKEY *public_key, char **authtoken){ time_t t = time(NULL); time_t utc_seconds = mktime(localtime(&t)); - char text[150]; - sprintf (text, "user: %s\npwd: %s\nts: %ld", username, password, utc_seconds); + + /* + * Compute a pessimistic/conservative estimate of storage required. + * It's OK to allocate too much storage but too little is bad. + */ + const int text_len = strlen(auth_text_format) + strlen(username) + strlen(password) + 32; + char *text = (char *) calloc(text_len, sizeof(char)); + if (text == NULL) { + return -1; + } + snprintf(text, text_len, auth_text_format, username, password, utc_seconds); + unsigned char *encrypted = NULL; int encrypted_len; encrypted_len = encrypt_rsa_message(text, public_key, &encrypted); + free(text); + if (encrypted_len < 0) { + return -1; + } Base64Encode(encrypted, encrypted_len, authtoken); OPENSSL_free(encrypted); @@ -274,19 +312,36 @@ int decode_auth_setting(int enable_debug, char *authtoken, EVP_PKEY *private_key unsigned char *plaintext = NULL; int plaintext_len; plaintext_len = decrypt_rsa_message(encrypted_b64, encrypted_len_b64, private_key, &plaintext); - plaintext[plaintext_len] = '\0'; free(encrypted_b64); + if (plaintext_len < 0) { + return -1; + } + plaintext[plaintext_len] = '\0'; + + char *s_username, *s_password; + s_username = (char *) calloc(plaintext_len, sizeof(char)); + if (s_username == NULL) { + return -1; + } + s_password = (char *) calloc(plaintext_len, sizeof(char)); + if (s_password == NULL) { + free(s_username); + return -1; + } + + int rc = sscanf((char *) plaintext, auth_text_format, s_username, s_password, ts); + if (rc != 3) { + free(s_password); + free(s_username); + return -1; + } - char s_username[20], s_password[20]; - sscanf ((char *)plaintext,"user: %s\npwd: %s\nts: %ld", s_username, s_password, ts); if (enable_debug) { printf("Auth Token Content:\n%s\n", plaintext); printf("Auth Token Credentials:\n--> %s %s\n", s_username, s_password); } - *username = (char *) calloc(21, sizeof(char)); - *password = (char *) calloc(21, sizeof(char)); - strncpy(*username, s_username, 20); - strncpy(*password, s_password, 20); + *username = s_username; + *password = s_password; OPENSSL_free(plaintext); return (0); } diff --git a/src/iperf_auth.h b/src/iperf_auth.h index 0c6fb0f..0dd6c4c 100644 --- a/src/iperf_auth.h +++ b/src/iperf_auth.h @@ -34,6 +34,7 @@ int test_load_private_key_from_file(const char *private_keyfile); EVP_PKEY *load_pubkey_from_file(const char *file); EVP_PKEY *load_pubkey_from_base64(const char *buffer); EVP_PKEY *load_privkey_from_file(const char *file); +EVP_PKEY *load_privkey_from_base64(const char *buffer); int encode_auth_setting(const char *username, const char *password, EVP_PKEY *public_key, char **authtoken); int decode_auth_setting(int enable_debug, const char *authtoken, EVP_PKEY *private_key, char **username, char **password, time_t *ts); int check_authentication(const char *username, const char *password, const time_t ts, const char *filename); diff --git a/src/iperf_error.c b/src/iperf_error.c index fd3cccc..c6e5ee4 100644 --- a/src/iperf_error.c +++ b/src/iperf_error.c @@ -1,5 +1,5 @@ /* - * iperf, Copyright (c) 2014-2019, The Regents of the University of + * iperf, Copyright (c) 2014-2020, The Regents of the University of * California, through Lawrence Berkeley National Laboratory (subject * to receipt of any required approvals from the U.S. Dept. of * Energy). All rights reserved. @@ -134,10 +134,10 @@ iperf_strerror(int int_errno) snprintf(errstr, len, "bad TOS value (must be between 0 and 255 inclusive)"); break; case IESETCLIENTAUTH: - snprintf(errstr, len, "you must specify username (max 20 chars), password (max 20 chars) and a path to a valid public rsa client to be used"); + snprintf(errstr, len, "you must specify a username, password, and path to a valid RSA public key"); break; case IESETSERVERAUTH: - snprintf(errstr, len, "you must specify path to a valid private rsa server to be used and a user credential file"); + snprintf(errstr, len, "you must specify a path to a valid RSA private key and a user credential file"); break; case IEBADFORMAT: snprintf(errstr, len, "bad format specifier (valid formats are in the set [kmgtKMGT])"); diff --git a/src/iperf_locale.c b/src/iperf_locale.c index 0d8a7ec..2dd5a5b 100644 --- a/src/iperf_locale.c +++ b/src/iperf_locale.c @@ -1,5 +1,5 @@ /*--------------------------------------------------------------- - * iperf, Copyright (c) 2014-2018, The Regents of the University of + * iperf, Copyright (c) 2014-2020, The Regents of the University of * California, through Lawrence Berkeley National Laboratory (subject * to receipt of any required approvals from the U.S. Dept. of * Energy). All rights reserved. @@ -272,10 +272,10 @@ const char report_time[] = const char report_connecting[] = "Connecting to host %s, port %d\n"; -const char report_authetication_successed[] = +const char report_authentication_succeeded[] = "Authentication successed for user '%s' ts %ld\n"; -const char report_authetication_failed[] = +const char report_authentication_failed[] = "Authentication failed for user '%s' ts %ld\n"; const char report_reverse[] = diff --git a/src/iperf_locale.h b/src/iperf_locale.h index ad784a6..15bdcdb 100644 --- a/src/iperf_locale.h +++ b/src/iperf_locale.h @@ -1,5 +1,5 @@ /* - * iperf, Copyright (c) 2014-2018, The Regents of the University of + * iperf, Copyright (c) 2014-2020, The Regents of the University of * California, through Lawrence Berkeley National Laboratory (subject * to receipt of any required approvals from the U.S. Dept. of * Energy). All rights reserved. @@ -54,8 +54,8 @@ extern const char report_reverse[] ; extern const char report_accepted[] ; extern const char report_cookie[] ; extern const char report_connected[] ; -extern const char report_authetication_successed[] ; -extern const char report_authetication_failed[] ; +extern const char report_authentication_succeeded[] ; +extern const char report_authentication_failed[] ; extern const char report_window[] ; extern const char report_autotune[] ; extern const char report_omit_done[] ; diff --git a/src/iperf_sctp.c b/src/iperf_sctp.c index 06e1e23..0bc98ba 100644 --- a/src/iperf_sctp.c +++ b/src/iperf_sctp.c @@ -131,12 +131,14 @@ iperf_sctp_accept(struct iperf_test * test) if (Nread(s, cookie, COOKIE_SIZE, Psctp) < 0) { i_errno = IERECVCOOKIE; + close(s); return -1; } - if (strcmp(test->cookie, cookie) != 0) { + if (strncmp(test->cookie, cookie, COOKIE_SIZE) != 0) { if (Nwrite(s, (char*) &rbuf, sizeof(rbuf), Psctp) < 0) { i_errno = IESENDMESSAGE; + close(s); return -1; } close(s); @@ -189,6 +191,26 @@ iperf_sctp_listen(struct iperf_test *test) return -1; } + if ((opt = test->settings->socket_bufsize)) { + int saved_errno; + if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) { + saved_errno = errno; + close(s); + freeaddrinfo(res); + errno = saved_errno; + i_errno = IESETBUF; + return -1; + } + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) { + saved_errno = errno; + close(s); + freeaddrinfo(res); + errno = saved_errno; + i_errno = IESETBUF; + return -1; + } + } + #if defined(IPV6_V6ONLY) && !defined(__OpenBSD__) if (res->ai_family == AF_INET6 && (test->settings->domain == AF_UNSPEC || test->settings->domain == AF_INET6)) { @@ -220,9 +242,11 @@ iperf_sctp_listen(struct iperf_test *test) /* servers must call sctp_bindx() _instead_ of bind() */ if (!TAILQ_EMPTY(&test->xbind_addrs)) { - freeaddrinfo(res); - if (iperf_sctp_bindx(test, s, IPERF_SCTP_SERVER)) + if (iperf_sctp_bindx(test, s, IPERF_SCTP_SERVER)) { + close(s); + freeaddrinfo(res); return -1; + } } else if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) { saved_errno = errno; @@ -292,6 +316,26 @@ iperf_sctp_connect(struct iperf_test *test) return -1; } + if ((opt = test->settings->socket_bufsize)) { + int saved_errno; + if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) { + saved_errno = errno; + close(s); + freeaddrinfo(server_res); + errno = saved_errno; + i_errno = IESETBUF; + return -1; + } + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) { + saved_errno = errno; + close(s); + freeaddrinfo(server_res); + errno = saved_errno; + i_errno = IESETBUF; + return -1; + } + } + /* * Various ways to bind the local end of the connection. * 1. --bind (with or without --cport). @@ -433,8 +477,11 @@ iperf_sctp_connect(struct iperf_test *test) /* clients must call bind() followed by sctp_bindx() before connect() */ if (!TAILQ_EMPTY(&test->xbind_addrs)) { - if (iperf_sctp_bindx(test, s, IPERF_SCTP_CLIENT)) + if (iperf_sctp_bindx(test, s, IPERF_SCTP_CLIENT)) { + freeaddrinfo(server_res); + close(s); return -1; + } } /* TODO support sctp_connectx() to avoid heartbeating. */ @@ -446,12 +493,12 @@ iperf_sctp_connect(struct iperf_test *test) i_errno = IESTREAMCONNECT; return -1; } - freeaddrinfo(server_res); /* Send cookie for verification */ if (Nwrite(s, test->cookie, COOKIE_SIZE, Psctp) < 0) { saved_errno = errno; close(s); + freeaddrinfo(server_res); errno = saved_errno; i_errno = IESENDCOOKIE; return -1; @@ -475,6 +522,7 @@ iperf_sctp_connect(struct iperf_test *test) return -1; } + freeaddrinfo(server_res); return s; #else i_errno = IENOSCTP; diff --git a/src/iperf_tcp.c b/src/iperf_tcp.c index 232aaa1..47252d0 100644 --- a/src/iperf_tcp.c +++ b/src/iperf_tcp.c @@ -29,6 +29,7 @@ #include <string.h> #include <errno.h> #include <unistd.h> +#include <arpa/inet.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> diff --git a/src/iperf_udp.c b/src/iperf_udp.c index 3d37dab..ab6be5e 100644 --- a/src/iperf_udp.c +++ b/src/iperf_udp.c @@ -1,5 +1,5 @@ /* - * iperf, Copyright (c) 2014-2018, The Regents of the University of + * iperf, Copyright (c) 2014-2020, The Regents of the University of * California, through Lawrence Berkeley National Laboratory (subject * to receipt of any required approvals from the U.S. Dept. of * Energy). All rights reserved. @@ -30,6 +30,7 @@ #include <errno.h> #include <unistd.h> #include <assert.h> +#include <arpa/inet.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> @@ -65,6 +66,7 @@ iperf_udp_recv(struct iperf_stream *sp) uint64_t pcount; int r; int size = sp->settings->blksize; + int first_packet = 0; double transit = 0, d = 0; struct iperf_time sent_time, arrival_time, temp_time; @@ -80,9 +82,19 @@ iperf_udp_recv(struct iperf_stream *sp) /* Only count bytes received while we're in the correct state. */ if (sp->test->state == TEST_RUNNING) { + + /* + * For jitter computation below, it's important to know if this + * packet is the first packet received. + */ + if (sp->result->bytes_received == 0) { + first_packet = 1; + } + sp->result->bytes_received += r; sp->result->bytes_received_this_interval += r; + /* Dig the various counters out of the incoming UDP packet */ if (sp->test->udp_counters_64bit) { memcpy(&sec, sp->buffer, sizeof(sec)); memcpy(&usec, sp->buffer+4, sizeof(usec)); @@ -149,7 +161,7 @@ iperf_udp_recv(struct iperf_stream *sp) /* Log the out-of-order packet */ if (sp->test->debug) - fprintf(stderr, "OUT OF ORDER - incoming packet sequence %" PRIu64 " but expected sequence %d on stream %d", pcount, sp->packet_count, sp->socket); + fprintf(stderr, "OUT OF ORDER - incoming packet sequence %" PRIu64 " but expected sequence %d on stream %d", pcount, sp->packet_count + 1, sp->socket); } /* @@ -167,6 +179,11 @@ iperf_udp_recv(struct iperf_stream *sp) iperf_time_diff(&arrival_time, &sent_time, &temp_time); transit = iperf_time_in_secs(&temp_time); + + /* Hack to handle the first packet by initializing prev_transit. */ + if (first_packet) + sp->prev_transit = transit; + d = transit - sp->prev_transit; if (d < 0) d = -d; @@ -44,9 +44,10 @@ #include "iperf.h" #include "iperf_api.h" -#include "units.h" +#include "iperf_util.h" #include "iperf_locale.h" #include "net.h" +#include "units.h" static int run(struct iperf_test *test); @@ -29,6 +29,7 @@ #include <stdio.h> #include <unistd.h> #include <errno.h> +#include <arpa/inet.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> diff --git a/src/private.pem b/src/private.pem new file mode 100644 index 0000000..8b1c5b6 --- /dev/null +++ b/src/private.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAwVbvPf/eDIZKmEVth9+VPgSx1RkXOAPCJ5tl51bcYoy9P10N +noutsTK/64VclIuyUDUdAw81Vu5tYRcBeB8Jllp02xL8kPo0IROsT3wmMYbPziUG +/I2988sAP9mL8QbtKydnADHMikfadfyPkfxW2naFtquWT/vKKVkhC2LHJyTpmAVj +pp6R9LDDu/YY0/kb5DvYPpe62xgNWjNVIhABu3R+StAAL25SaLXuaGSVpDe6Sphn +TqVf+LmKCkZmSMSfQoozXomNyFpLU8ODoV3QyHYi6QSWWFtU6gu0uso/9pfRFjbN +GV9lxokKNz/cZGyu0SFddMiTGrt24NROi0RHwwIDAQABAoIBACVG+cHef5WyntdV +K5UzCrd2eEM6HzvxnZG9aJx+JufpcuOwsVuMWuT7f/2NLRiHBs5oLzvTxtkIB5bc +tK/QbCzNLBLBSmk5lKt7+5EnwsVx1MdOZFZ1jdZfoaCt9Ul3qGrVogprj6Bp0jlF +hPkEyko85/McilLJnWTzhmeHmBZ3tOJ9LWHgVdXGXZx/pBuxZ+UB/xgZdvdZK86F +xndWqD8lvfpoSsVwzCORdXvQWs78CtT2KXYvt8S6mrLMoIoHLO/1rwwKKoZfFuja +NouN36PGaSo4/9O1d11/s9zwdJH3ShozY0Fao8I3XhdH0uvTirmAEPh7U1Lo71pP +ksUvbUECgYEA/7p1Bg/XsSK72KgJ695B2hi8g9+wv0eKCpGwkKJQZKE338it8zIn ++JMaeDjObLhb4B2QP/3iL0mdU2mJzm0X8hYCKqBdyICb9wixznLLAej+uw2j60Ef +tlGenCqAkVL3pRS5txztNhFsXx3JwxCSj4zwmVm5oyxkpAeuwvZ1cPkCgYEAwYuC +6nPlRf6GEf/MuHCziaCCPrDm1u472uspxHn41crw6BaMl5xcmr0Z8O7heOtlVmMl +Mn3gfIbd/NGyjs8ejmWcO5mZHtESGM/zAaVyu7bn3ij37OEndbEpZsCpRtxvtaji +MTMwhdV2xteJ0+KCtg66ziSyJv3krQTEW8DZKZsCgYB8BcnLbtOErPu9T4HASsJV +K7oBmvL1UZS5G38uJgonQ6j9dy4lzCVmgLFNrP8v6xljz/Ktlkuj82fBlGWpH2+F +kPbsBWp2WylI3YaeQT4DZyRjQ3JEHglrOppZ0qMX180S2sJW9Eh2+Gw+lQvM9rSd +uhTVypYldNo6Ux+GnlDGwQKBgQC/6W4uvCyjcvXN8y8z08yysw1yzEaY6DFBqd0I +jUlH9Ktb9sABtXG9nbSTSssX85HQTw8bOeXWlISZo/TB1m4eFHMORgemnviq0cfL +4hoaOAtCJq1vnPJbqQe8c11mfj3mi0d+MZvzmO7ly+NGzlt92q0wqwJb13VgelGa +CWdL8QKBgQD1ABK8WJuMowihrQsLaUxNO/1hsN8CP/rjoi9D5NdSFt5zzcC3D/RA +m42ScOaAFIh44Js5aAROvbDqIlelhwRlmutP/lYyQRZDZm7X6u/bKuqK4H/sM6ZB +SICe2eWFvbKexI7QnreRltOWIFrNoZy9FdELM8DS+kePAlVt7c83Uw== +-----END RSA PRIVATE KEY----- diff --git a/src/public.pem b/src/public.pem new file mode 100644 index 0000000..71f4f13 --- /dev/null +++ b/src/public.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwVbvPf/eDIZKmEVth9+V +PgSx1RkXOAPCJ5tl51bcYoy9P10NnoutsTK/64VclIuyUDUdAw81Vu5tYRcBeB8J +llp02xL8kPo0IROsT3wmMYbPziUG/I2988sAP9mL8QbtKydnADHMikfadfyPkfxW +2naFtquWT/vKKVkhC2LHJyTpmAVjpp6R9LDDu/YY0/kb5DvYPpe62xgNWjNVIhAB +u3R+StAAL25SaLXuaGSVpDe6SphnTqVf+LmKCkZmSMSfQoozXomNyFpLU8ODoV3Q +yHYi6QSWWFtU6gu0uso/9pfRFjbNGV9lxokKNz/cZGyu0SFddMiTGrt24NROi0RH +wwIDAQAB +-----END PUBLIC KEY----- diff --git a/src/t_api.c b/src/t_api.c index 0669917..f5e0984 100644 --- a/src/t_api.c +++ b/src/t_api.c @@ -1,5 +1,5 @@ /* - * iperf, Copyright (c) 2017, The Regents of the University of + * iperf, Copyright (c) 2017-2020, The Regents of the University of * California, through Lawrence Berkeley National Laboratory (subject * to receipt of any required approvals from the U.S. Dept. of * Energy). All rights reserved. @@ -45,9 +45,21 @@ int main(int argc, char **argv) { const char *ver; + struct iperf_test *test; + int sint, gint; ver = iperf_get_iperf_version(); assert(strcmp(ver, IPERF_VERSION) == 0); + test = iperf_new_test(); + assert(test != NULL); + + iperf_defaults(test); + + sint = 10; + iperf_set_test_connect_timeout(test, sint); + gint = iperf_get_test_connect_timeout(test); + assert(sint == gint); + return 0; } diff --git a/src/t_auth.c b/src/t_auth.c new file mode 100644 index 0000000..ff9cffe --- /dev/null +++ b/src/t_auth.c @@ -0,0 +1,125 @@ +/* + * iperf, Copyright (c) 2020, The Regents of the University of + * California, through Lawrence Berkeley National Laboratory (subject + * to receipt of any required approvals from the U.S. Dept. of + * Energy). All rights reserved. + * + * If you have questions about your rights to use or distribute this + * software, please contact Berkeley Lab's Technology Transfer + * Department at TTD@lbl.gov. + * + * NOTICE. This software is owned by the U.S. Department of Energy. + * As such, the U.S. Government has been granted for itself and others + * acting on its behalf a paid-up, nonexclusive, irrevocable, + * worldwide license in the Software to reproduce, prepare derivative + * works, and perform publicly and display publicly. Beginning five + * (5) years after the date permission to assert copyright is obtained + * from the U.S. Department of Energy, and subject to any subsequent + * five (5) year renewals, the U.S. Government is granted for itself + * and others acting on its behalf a paid-up, nonexclusive, + * irrevocable, worldwide license in the Software to reproduce, + * prepare derivative works, distribute copies to the public, perform + * publicly and display publicly, and to permit others to do so. + * + * This code is distributed under a BSD style license, see the LICENSE + * file for complete information. + */ +#include "iperf_config.h" + +#include <assert.h> +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif +#include <stdio.h> +#include <string.h> + +#include "iperf.h" +#include "iperf_api.h" +#if defined(HAVE_SSL) +#include "iperf_auth.h" +#endif /* HAVE_SSL */ + +#include "version.h" + +#include "units.h" + +#if defined(HAVE_SSL) +int test_authtoken(const char *authUser, const char *authPassword, EVP_PKEY *pubkey, EVP_PKEY *privkey); + +int +main(int argc, char **argv) +{ + /* sha256 */ + void sha256(const char *string, char outputBuffer[65]); + const char sha256String[] = "This is a SHA256 test."; + const char sha256Digest[] = "4816482f8b4149f687a1a33d61a0de6b611364ec0fb7adffa59ff2af672f7232"; /* echo -n "This is a SHA256 test." | shasum -a256 */ + char sha256Output[65]; + + sha256(sha256String, sha256Output); + assert(strcmp(sha256Output, sha256Digest) == 0); + + /* Base64{Encode,Decode} */ + int Base64Encode(const unsigned char* buffer, const size_t length, char** b64text); + int Base64Decode(const char* b64message, unsigned char** buffer, size_t* length); + const char base64String[] = "This is a Base64 test."; + char *base64Text; + char *base64Decode; + size_t base64DecodeLength; + const char base64EncodeCheck[] = "VGhpcyBpcyBhIEJhc2U2NCB0ZXN0Lg=="; /* echo -n "This is a Base64 test." | b64encode -r - */ + + assert(Base64Encode((unsigned char *) base64String, strlen(base64String), &base64Text) == 0); + assert(strcmp(base64Text, base64EncodeCheck) == 0); + assert(Base64Decode(base64Text, (unsigned char **) &base64Decode, &base64DecodeLength) == 0); + assert(strcmp(base64String, base64Decode) == 0); + + /* public/private key tests */ + const char *pubkeyfile = "public.pem"; + const char *privkeyfile = "private.pem"; + + /* built-in tests */ + assert(test_load_pubkey_from_file(pubkeyfile) == 0); + assert(test_load_private_key_from_file(privkeyfile) == 0); + + /* load public key pair for use in further tests */ + EVP_PKEY *pubkey, *privkey; + pubkey = load_pubkey_from_file(pubkeyfile); + assert(pubkey); + privkey = load_privkey_from_file(privkeyfile); + assert(privkey); + + /* authentication token tests */ + assert(test_authtoken("kilroy", "fubar", pubkey, privkey) == 0); + + /* This should fail because the data is way too long for the RSA key */ + /* assert(test_authtoken("kilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroy", "fubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubar", pubkey, privkey) < 0); */ + + return 0; +} + +int +test_authtoken(const char *authUser, const char *authPassword, EVP_PKEY *pubkey, EVP_PKEY *privkey) { + char *authToken; + char *decodeUser; + char *decodePassword; + time_t decodeTime; + + assert(encode_auth_setting(authUser, authPassword, pubkey, &authToken) == 0); + assert(decode_auth_setting(0, authToken, privkey, &decodeUser, &decodePassword, &decodeTime) == 0); + + assert(strcmp(decodeUser, authUser) == 0); + assert(strcmp(decodePassword, authPassword) == 0); + + time_t now = time(NULL); + + assert(now - decodeTime >= 0); /* time has to go forwards */ + assert(now - decodeTime <= 1); /* shouldn't take more than a second to run */ + + return 0; +} +#else +int +main(int argc, char **argv) +{ + return 0; +} +#endif /* HAVE_SSL */ diff --git a/src/tcp_info.c b/src/tcp_info.c index bfbb9ea..d63e5b4 100644 --- a/src/tcp_info.c +++ b/src/tcp_info.c @@ -197,13 +197,13 @@ get_pmtu(struct iperf_interval_results *irp) void build_tcpinfo_message(struct iperf_interval_results *r, char *message) { -#if defined(linux) +#if defined(linux) && defined(TCP_INFO) sprintf(message, report_tcpInfo, r->tcpInfo.tcpi_snd_cwnd, r->tcpInfo.tcpi_snd_ssthresh, r->tcpInfo.tcpi_rcv_ssthresh, r->tcpInfo.tcpi_unacked, r->tcpInfo.tcpi_sacked, r->tcpInfo.tcpi_lost, r->tcpInfo.tcpi_retrans, r->tcpInfo.tcpi_fackets, r->tcpInfo.tcpi_rtt, r->tcpInfo.tcpi_reordering); #endif -#if defined(__FreeBSD__) +#if defined(__FreeBSD__) && defined(TCP_INFO) sprintf(message, report_tcpInfo, r->tcpInfo.tcpi_snd_cwnd, r->tcpInfo.tcpi_rcv_space, r->tcpInfo.tcpi_snd_ssthresh, r->tcpInfo.tcpi_rtt); #endif diff --git a/src/timer.h b/src/timer.h index 301cdf3..58c3db8 100644 --- a/src/timer.h +++ b/src/timer.h @@ -31,6 +31,8 @@ #define __TIMER_H #include <time.h> +#include <sys/time.h> + #include "iperf_time.h" /* TimerClientData is an opaque value that tags along with a timer. The |