New asynchronous MQTT API (see MQTTAsync.h) and

SSL support for both old and new APIs.
This commit is contained in:
Ian Craggs 2013-01-24 14:40:11 +00:00
parent 125edbb634
commit 97c7280470
36 changed files with 7127 additions and 464 deletions

View File

@ -6,38 +6,134 @@ ifndef MQTTCLIENT_DIR
MQTTCLIENT_DIR = $(CURDIR)
endif
MQTTLIB = mqttv3c
VPATH = ${MQTTCLIENT_DIR}
SOURCE_FILES = $(MQTTCLIENT_DIR)/*.c
HEADERS = $(MQTTCLIENT_DIR)/*.h
ifeq ($(OS),Windows_NT)
OSTYPE = $(OS)
else
OSTYPE = $(shell uname -s)
MACHINETYPE = $(shell uname -m)
ifndef SSL_DIR
SSL_DIR = ${MQTTCLIENT_DIR}/../../../../source/openssl-1.0.1c/
endif
ifndef SAMPLES_DIR
SAMPLES_DIR = ${SSL_DIR}/../com.ibm.mq.mqxr.listener/SDK/clients/c/samples/
endif
MQTTLIB = mqttv3c
VPATH = ${MQTTCLIENT_DIR}
ALL_SOURCE_FILES = $(wildcard $(MQTTCLIENT_DIR)/*.c)
SOURCE_FILES = $(filter-out $(MQTTCLIENT_DIR)/MQTTAsync.c $(MQTTCLIENT_DIR)/MQTTVersion.c $(MQTTCLIENT_DIR)/SSLSocket.c, $(ALL_SOURCE_FILES))
ALL_HEADERS = $(MQTTCLIENT_DIR)/*.h
HEADERS = $(filter-out $(MQTTCLIENT_DIR)/MQTTAsync.h, $(ALL_HEADERS))
MQTTLIB_S = mqttv3cs
SOURCE_FILES_S = $(filter-out $(MQTTCLIENT_DIR)/MQTTAsync.c $(MQTTCLIENT_DIR)/MQTTVersion.c, $(ALL_SOURCE_FILES))
MQTTLIB_A = mqttv3a
SOURCE_FILES_A = $(filter-out $(MQTTCLIENT_DIR)/MQTTClient.c $(MQTTCLIENT_DIR)/MQTTVersion.c $(MQTTCLIENT_DIR)/SSLSocket.c, $(ALL_SOURCE_FILES))
HEADERS_A = $(MQTTCLIENT_DIR)/*.h
MQTTLIB_AS = mqttv3as
SOURCE_FILES_AS = $(filter-out $(MQTTCLIENT_DIR)/MQTTClient.c $(MQTTCLIENT_DIR)/MQTTVersion.c, $(ALL_SOURCE_FILES))
# In most cases the targets will be built for the native platform. However, to allow for cross compiling,
# a target platform variable can be set to override the platform choice. If this variable is set to
# a recognised string (currently just 'Arm') the appropriate values of OSTYPE and MACHINETYPE are set.
ifeq ($(TARGET_PLATFORM),Arm)
OSTYPE = Linux
MACHINETYPE = Arm
else
ifeq ($(OS),Windows_NT)
OSTYPE = $(OS)
else
OSTYPE = $(shell uname -s)
MACHINETYPE = $(shell uname -m)
endif
endif
ifeq ($(OSTYPE),Linux)
ifeq ($(MACHINETYPE),x86_64)
CC = gcc
CC = gcc
CCFLAGS_SO = -fPIC -Os -Wall
CCFLAGS_EXE = -I ${MQTTCLIENT_DIR}
LDFLAGS = -shared -Wl,-soname,lib${MQTTLIB}.so
LDFLAGS_EXE = -lpthread
CCFLAGS_SO = -fPIC -Os -Wall -I ${SSL_DIR}/include
CCFLAGS_EXE = -I ${MQTTCLIENT_DIR}
LDFLAGS = -shared -Wl,-soname,lib${MQTTLIB}.so
LDFLAGS_S = -shared -Wl,-soname,lib${MQTTLIB_S}.so -ldl -Wl,-whole-archive -L${SSL_DIR}/output/lib -lcrypto -lssl -Wl,-no-whole-archive
LDFLAGS_A = -shared -Wl,-soname,lib${MQTTLIB_A}.so
LDFLAGS_AS = -shared -Wl,-soname,lib${MQTTLIB_AS}.so -ldl -Wl,-whole-archive -L${SSL_DIR}/output/lib -lcrypto -lssl -Wl,-no-whole-archive
LDFLAGS_EXE = -lpthread
MQTTLIB_IA64 = linux_ia64/lib${MQTTLIB}.so
all: ${MQTTLIB_IA64}
MQTTLIB_IA64 = linux_ia64/lib${MQTTLIB}.so
MQTTLIB_IA64_S = linux_ia64/lib${MQTTLIB_S}.so
MQTTLIB_IA64_A = linux_ia64/lib${MQTTLIB_A}.so
MQTTLIB_IA64_AS = linux_ia64/lib${MQTTLIB_AS}.so
ifdef BUILD_SAMPLES
all: linux_ia64 ${MQTTLIB_IA64} ${MQTTLIB_IA64_A} ${MQTTLIB_IA64_S} ${MQTTLIB_IA64_AS} samples
else
all: linux_ia64 ${MQTTLIB_IA64} ${MQTTLIB_IA64_A} ${MQTTLIB_IA64_S} ${MQTTLIB_IA64_AS}
endif
linux_ia64:
-mkdir linux_ia64
samples:
-mkdir ${SAMPLES_DIR}/linux_ia64
${CC} -m64 -I ${MQTTCLIENT_DIR} -L ${MQTTCLIENT_DIR}/linux_ia64 -o ${SAMPLES_DIR}/linux_ia64/MQTTV3Sample ${SAMPLES_DIR}/MQTTV3Sample.c -lmqttv3c -lpthread
${CC} -m64 -I ${MQTTCLIENT_DIR} -L ${MQTTCLIENT_DIR}/linux_ia64 -o ${SAMPLES_DIR}/linux_ia64/MQTTV3SSample ${SAMPLES_DIR}/MQTTV3SSample.c -lmqttv3cs -lpthread
${CC} -m64 -I ${MQTTCLIENT_DIR} -L ${MQTTCLIENT_DIR}/linux_ia64 -o ${SAMPLES_DIR}/linux_ia64/MQTTV3ASample ${SAMPLES_DIR}/MQTTV3ASample.c -lmqttv3a -lpthread
${CC} -m64 -I ${MQTTCLIENT_DIR} -L ${MQTTCLIENT_DIR}/linux_ia64 -o ${SAMPLES_DIR}/linux_ia64/MQTTV3ASSample ${SAMPLES_DIR}/MQTTV3ASSample.c -lmqttv3as -lpthread
${MQTTLIB_IA64}: ${SOURCE_FILES} ${HEADERS}
-mkdir linux_ia64
${CC} -m64 ${CCFLAGS_SO} ${LDFLAGS} -o $@ ${SOURCE_FILES}
strip $@
${MQTTLIB_IA64_S}: ${SOURCE_FILES_S} ${HEADERS}
${CC} -m64 ${CCFLAGS_SO} ${LDFLAGS_S} -o $@ ${SOURCE_FILES_S} -DOPENSSL
strip $@
${MQTTLIB_IA64_A}: ${SOURCE_FILES_A} ${HEADERS_A}
${CC} -m64 ${CCFLAGS_SO} ${LDFLAGS_A} -o $@ ${SOURCE_FILES_A}
strip $@
${MQTTLIB_IA64_AS}: ${SOURCE_FILES_AS} ${HEADERS_A}
${CC} -m64 ${CCFLAGS_SO} ${LDFLAGS_AS} -o $@ ${SOURCE_FILES_AS} -DOPENSSL
strip $@
else
ifeq ($(MACHINETYPE),ppc64)
CC = gcc
CCFLAGS_SO = -fPIC -Os -Wall -I ${SSL_DIR}/include
CCFLAGS_EXE = -I ${MQTTCLIENT_DIR}
LDFLAGS = -shared -Wl,-soname,lib${MQTTLIB}.so
LDFLAGS_S = -shared -Wl,-soname,lib${MQTTLIB_S}.so -ldl -Wl,-whole-archive -L${SSL_DIR}/output/lib -lcrypto -lssl -Wl,-no-whole-archive
LDFLAGS_A = -shared -Wl,-soname,lib${MQTTLIB_A}.so
LDFLAGS_AS = -shared -Wl,-soname,lib${MQTTLIB_AS}.so -ldl -Wl,-whole-archive -L${SSL_DIR}/output/lib -lcrypto -lssl -Wl,-no-whole-archive
LDFLAGS_EXE = -lpthread
MQTTLIB_PPC64 = linux_ppc64/lib${MQTTLIB}.so
MQTTLIB_PPC64_S = linux_ppc64/lib${MQTTLIB_S}.so
MQTTLIB_PPC64_A = linux_ppc64/lib${MQTTLIB_A}.so
MQTTLIB_PPC64_AS = linux_ppc64/lib${MQTTLIB_AS}.so
all: linux_ppc64 ${MQTTLIB_PPC64} ${MQTTLIB_PPC64_A} #${MQTTLIB_PPC64_S} ${MQTTLIB_PPC64_AS}
linux_ppc64:
-mkdir linux_ppc64
${MQTTLIB_PPC64}: ${SOURCE_FILES} ${HEADERS}
${CC} -m64 ${CCFLAGS_SO} ${LDFLAGS} -o $@ ${SOURCE_FILES}
strip $@
${MQTTLIB_PPC64_S}: ${SOURCE_FILES_S} ${HEADERS}
${CC} -m64 ${CCFLAGS_SO} ${LDFLAGS_S} -o $@ ${SOURCE_FILES_S} -DOPENSSL
strip $@
${MQTTLIB_PPC64_A}: ${SOURCE_FILES_A} ${HEADERS_A}
${CC} -m64 ${CCFLAGS_SO} ${LDFLAGS_A} -o $@ ${SOURCE_FILES_A}
strip $@
${MQTTLIB_PPC64_AS}: ${SOURCE_FILES_AS} ${HEADERS_A}
${CC} -m64 ${CCFLAGS_SO} ${LDFLAGS_AS} -o $@ ${SOURCE_FILES_AS} -DOPENSSL
strip $@
else
ifeq ($(MACHINETYPE),s390x)
CC = gcc
@ -57,35 +153,101 @@ ${MQTTLIB_390}: ${SOURCE_FILES} ${HEADERS}
strip $@
else
CC = gcc
ARM_GLIBC_PREFIX = arm-linux-
ARM_UCLIBC_PREFIX = ~/x-tools/arm-unknown-linux-uclibcgnueabi/bin/arm-unknown-linux-uclibcgnueabi-
ifeq ($(MACHINETYPE),Arm)
CCFLAGS_SO = -fPIC -Os -Wall
CCFLAGS_EXE = -I ${MQTTCLIENT_DIR}
LDFLAGS = -shared -Wl,-soname,lib${MQTTLIB}.so
LDFLAGS_EXE = -lpthread
CC = gcc
# ARM_GLIBC_CC = arm-linux-gcc
# ARM_UCLIBC_CC = ~/x-tools/arm-unknown-linux-uclibcgnueabi/bin/arm-unknown-linux-uclibcgnueabi-gcc
MQTTLIB_IA32 = linux_ia32/lib${MQTTLIB}.so
MQTTLIB_ARM_GLIBC = linux_ARM_glibc/lib${MQTTLIB}.so
MQTTLIB_ARM_UCLIBC = linux_ARM_uclibc/lib${MQTTLIB}.so
ifndef ARM_GLIBC_CC
ARM_GLIBC_CC = arm-linux-gcc
endif
ifndef ARM_UCLIBC_CC
ARM_UCLIBC_CC = arm-unknown-linux-uclibcgnueabi-gcc
endif
all: ${MQTTLIB_IA32} ${MQTTLIB_ARM_GLIBC} ${MQTTLIB_ARM_UCLIBC}
ifndef ARM_GLIBC_STRIP
ARM_GLIBC_STRIP = $(subst -gcc,-strip,${ARM_GLIBC_CC})
endif
ifndef ARM_UCLIBC_STRIP
ARM_UCLIBC_STRIP = $(subst -gcc,-strip,${ARM_UCLIBC_CC})
endif
CCFLAGS_SO = -fPIC -Os -Wall
CCFLAGS_EXE = -I ${MQTTCLIENT_DIR}
LDFLAGS = -shared -Wl,-soname,lib${MQTTLIB}.so
LDFLAGS_EXE = -lpthread
MQTTLIB_ARM_GLIBC = linux_ARM_glibc/lib${MQTTLIB}.so
MQTTLIB_ARM_UCLIBC = linux_ARM_uclibc/lib${MQTTLIB}.so
MQTTLIB_ARM_GLIBC_A = linux_ARM_glibc/lib${MQTTLIB_A}.so
MQTTLIB_ARM_UCLIBC_A = linux_ARM_uclibc/lib${MQTTLIB_A}.so
all: ${MQTTLIB_ARM_GLIBC} ${MQTTLIB_ARM_UCLIBC}
${MQTTLIB_IA32}: ${SOURCE_FILES} ${HEADERS}
-mkdir linux_ia32
${CC} -m32 ${CCFLAGS_SO} ${LDFLAGS} -o $@ ${SOURCE_FILES}
strip $@
${MQTTLIB_ARM_GLIBC}: ${SOURCE_FILES} ${HEADERS}
-mkdir linux_ARM_glibc
${ARM_GLIBC_PREFIX}gcc ${CCFLAGS_SO} ${LDFLAGS} -o $@ ${SOURCE_FILES}
${ARM_GLIBC_PREFIX}strip $@
${ARM_GLIBC_CC} ${CCFLAGS_SO} ${LDFLAGS} -o $@ ${SOURCE_FILES}
${ARM_GLIBC_STRIP} $@
${MQTTLIB_ARM_UCLIBC}: ${SOURCE_FILES} ${HEADERS}
-mkdir linux_ARM_uclibc
${ARM_UCLIBC_PREFIX}gcc ${CCFLAGS_SO} ${LDFLAGS} -o $@ ${SOURCE_FILES}
${ARM_UCLIBC_PREFIX}strip $@
${ARM_UCLIBC_CC} ${CCFLAGS_SO} ${LDFLAGS} -o $@ ${SOURCE_FILES}
${ARM_UCLIBC_STRIP} $@
else
CC = gcc
CCFLAGS_SO = -fPIC -Os -Wall -I ${SSL_DIR}/include
CCFLAGS_EXE = -I ${MQTTCLIENT_DIR}
LDFLAGS = -shared -Wl,-soname,lib${MQTTLIB}.so
LDFLAGS_S = -shared -Wl,-soname,lib${MQTTLIB_S}.so -ldl -Wl,-whole-archive -L${SSL_DIR}/output/lib -lcrypto -lssl -Wl,-no-whole-archive
LDFLAGS_A = -shared -Wl,-soname,lib${MQTTLIB_A}.so
LDFLAGS_AS = -shared -Wl,-soname,lib${MQTTLIB_AS}.so -ldl -Wl,-whole-archive -L/${SSL_DIR}/output/lib -lcrypto -lssl -Wl,-no-whole-archive
LDFLAGS_EXE = -lpthread
MQTTLIB_IA32 = linux_ia32/lib${MQTTLIB}.so
MQTTLIB_IA32_S = linux_ia32/lib${MQTTLIB_S}.so
MQTTLIB_IA32_A = linux_ia32/lib${MQTTLIB_A}.so
MQTTLIB_IA32_AS = linux_ia32/lib${MQTTLIB_AS}.so
ifdef BUILD_SAMPLES
all: linux_ia32 ${MQTTLIB_IA32_A} ${MQTTLIB_IA32} ${MQTTLIB_IA32_S} ${MQTTLIB_IA32_AS} samples
else
all: linux_ia32 ${MQTTLIB_IA32_A} ${MQTTLIB_IA32} ${MQTTLIB_IA32_S} ${MQTTLIB_IA32_AS}
endif
samples:
-mkdir ${SAMPLES_DIR}/linux_ia32
${CC} -m32 -I ${MQTTCLIENT_DIR} -L ${MQTTCLIENT_DIR}/linux_ia32 -o ${SAMPLES_DIR}/linux_ia32/MQTTV3Sample ${SAMPLES_DIR}/MQTTV3Sample.c -lmqttv3c -lpthread
${CC} -m32 -I ${MQTTCLIENT_DIR} -L ${MQTTCLIENT_DIR}/linux_ia32 -o ${SAMPLES_DIR}/linux_ia32/MQTTV3SSample ${SAMPLES_DIR}/MQTTV3SSample.c -lmqttv3cs -lpthread
${CC} -m32 -I ${MQTTCLIENT_DIR} -L ${MQTTCLIENT_DIR}/linux_ia32 -o ${SAMPLES_DIR}/linux_ia32/MQTTV3ASample ${SAMPLES_DIR}/MQTTV3ASample.c -lmqttv3a -lpthread
${CC} -m32 -I ${MQTTCLIENT_DIR} -L ${MQTTCLIENT_DIR}/linux_ia32 -o ${SAMPLES_DIR}/linux_ia32/MQTTV3ASSample ${SAMPLES_DIR}/MQTTV3ASSample.c -lmqttv3as -lpthread
linux_ia32:
-mkdir linux_ia32
${MQTTLIB_IA32}: ${SOURCE_FILES} ${HEADERS}
${CC} -m32 ${CCFLAGS_SO} ${LDFLAGS} -o $@ ${SOURCE_FILES}
strip $@
${MQTTLIB_IA32_S}: ${SOURCE_FILES_S} ${HEADERS}
${CC} -m32 ${CCFLAGS_SO} ${LDFLAGS_S} -o $@ ${SOURCE_FILES_S} -DOPENSSL
strip $@
${MQTTLIB_IA32_A}: ${SOURCE_FILES_A} ${HEADERS_A}
${CC} -m32 ${CCFLAGS_SO} ${LDFLAGS_A} -o $@ ${SOURCE_FILES_A}
strip $@
${MQTTLIB_IA32_AS}: ${SOURCE_FILES_AS} ${HEADERS_A}
${CC} -m32 ${CCFLAGS_SO} ${LDFLAGS_AS} -o $@ ${SOURCE_FILES_AS} -DOPENSSL
strip $@
endif
endif
endif
endif
@ -95,9 +257,9 @@ endif
ifeq ($(OSTYPE),AIX)
# applications must be linked with -I. -L. -lmqttv3c -Wl,-brtl -pthread
CC = gcc
CC = gxlc
CCFLAGS_SO = -fPIC -Os -Wall -shared -DREVERSED
CCFLAGS_SO = -fPIC -Os -shared -DREVERSED -DAIX
LDFLAGS = -Wl,-G
MQTTLIB_AIX = aix/lib${MQTTLIB}.so
@ -118,16 +280,20 @@ ifeq ($(OSTYPE),Windows_NT)
# compiler flags
CPPFLAGS = /D "WIN32" /D "_UNICODE" /D "UNICODE" /D "_CRT_SECURE_NO_WARNINGS"
CFLAGS = /nologo /c /O2 /W3 /Fd /MD /TC
CPPFLAGS_S = /D "OPENSSL" /I ${SSL_DIR}/include
# linker flags
LINKFLAGS = /nologo /machine:x86 /manifest
WINLIBS = kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib
WINLIBS_S = crypt32.lib ssleay32.lib libeay32.lib
SSLLIBPATH = /LIBPATH:${SSL_DIR}/output/lib
# Flags to link C libraries
LIBLINKFLAGS = /dll
IMP = /implib:$(@:.dll=.lib)
LIBPDB = /pdb:$(@:.dll=.pdb)
LIBMAP = /map:$(@:.dll=.map)
LIBLDFLAGS = $(LINKFLAGS) $(WINLIBS) $(LIBLINKFLAGS) $(IMP) $(LIBPDB) $(LIBMAP)
LIBLDFLAGS_S = $(LINKFLAGS) $(SSLLIBPATH) $(WINLIBS) $(WINLIBS_S) $(LIBLINKFLAGS) $(IMP) $(LIBPDB) $(LIBMAP)
# Flags to link C programs
MAINLINKFLAGS = /subsystem:console
EXEPDB = /pdb:$(@:.exe=.pdb)
@ -136,14 +302,64 @@ ifeq ($(OSTYPE),Windows_NT)
MQTTLDLIB = windows_ia32/$(MQTTLIB).lib
MQTTDLL = windows_ia32/$(MQTTLIB).dll
MQTTLDLIB_A = windows_ia32/$(MQTTLIB_A).lib
MQTTDLL_A = windows_ia32/$(MQTTLIB_A).dll
MQTTLDLIB_AS = windows_ia32/$(MQTTLIB_AS).lib
MQTTDLL_AS = windows_ia32/$(MQTTLIB_AS).dll
MQTTLDLIB_S = windows_ia32/$(MQTTLIB_S).lib
MQTTDLL_S = windows_ia32/$(MQTTLIB_S).dll
all: ${MQTTDLL}
ifdef BUILD_SAMPLES
all: windows_ia32 ${MQTTDLL} ${MQTTDLL_A} ${MQTTDLL_AS} ${MQTTDLL_S} samples
else
all: windows_ia32 ${MQTTDLL} ${MQTTDLL_A} ${MQTTDLL_AS} ${MQTTDLL_S}
endif
$(MQTTDLL): $(shell cygpath -au ${SOURCE_FILES}) $(shell cygpath -au ${HEADERS})
samples:
-mkdir ${SAMPLES_DIR}/windows_ia32
${CC} /nologo /D "WIN32" /I ${MQTTCLIENT_DIR} ${SAMPLES_DIR}/MQTTV3Sample.c /Fe${SAMPLES_DIR}/windows_ia32/ /link /LIBPATH:${MQTTCLIENT_DIR}/windows_ia32 /nologo /subsystem:console /manifest /machine:x86 mqttv3c.lib
${CC} /nologo /D "WIN32" /I ${MQTTCLIENT_DIR} ${SAMPLES_DIR}/MQTTV3CSSample.c /Fe${SAMPLES_DIR}/windows_ia32/ /link /LIBPATH:${MQTTCLIENT_DIR}/windows_ia32 /nologo /subsystem:console /manifest /machine:x86 mqttv3cs.lib
${CC} /nologo /D "WIN32" /I ${MQTTCLIENT_DIR} ${SAMPLES_DIR}/MQTTV3ASample.c /Fe${SAMPLES_DIR}/windows_ia32/ /link /LIBPATH:${MQTTCLIENT_DIR}/windows_ia32 /nologo /subsystem:console /manifest /machine:x86 mqttv3a.lib
${CC} /nologo /D "WIN32" /I ${MQTTCLIENT_DIR} ${SAMPLES_DIR}/MQTTV3ASSample.c /Fe${SAMPLES_DIR}/windows_ia32/ /link /LIBPATH:${MQTTCLIENT_DIR}/windows_ia32 /nologo /subsystem:console /manifest /machine:x86 mqttv3as.lib
windows_ia32:
-mkdir windows_ia32
${CC} ${CPPFLAGS} ${CFLAGS} /I $(shell cygpath -am ${MQTTCLIENT_DIR}) /I $(shell cygpath -am ${MQTTCLIENT_DIR})/.. /Fo $(shell cygpath -am ${SOURCE_FILES})
$(MQTTDLL): ${SOURCE_FILES} ${HEADERS}
-rm -f *.obj
${CC} ${CPPFLAGS} ${CFLAGS} /I ${MQTTCLIENT_DIR} /I ${MQTTCLIENT_DIR}/.. /Fo ${SOURCE_FILES}
$(LD) $(LIBLDFLAGS) *.obj /out:$(MQTTDLL)
mt -manifest windows_ia32/mqttv3c.dll.manifest -outputresource:$(MQTTDLL)\;2
$(MQTTDLL_A): ${SOURCE_FILES_A} ${HEADERS_A}
-rm -f *.obj
${CC} ${CPPFLAGS} ${CFLAGS} /I ${MQTTCLIENT_DIR} /I ${MQTTCLIENT_DIR}/.. /Fo ${SOURCE_FILES_A}
$(LD) $(LIBLDFLAGS) *.obj /out:$(MQTTDLL_A)
mt -manifest windows_ia32/mqttv3a.dll.manifest -outputresource:$(MQTTDLL_A)\;2
$(MQTTDLL_S): ${SOURCE_FILES_S} ${HEADERS}
-rm -f *.obj
${CC} ${CPPFLAGS} ${CFLAGS} ${CPPFLAGS_S} /I ${MQTTCLIENT_DIR} /I ${MQTTCLIENT_DIR}/.. /Fo ${SOURCE_FILES_S}
$(LD) $(LIBLDFLAGS_S) *.obj /out:$(MQTTDLL_S)
mt -manifest windows_ia32/mqttv3cs.dll.manifest -outputresource:$(MQTTDLL_S)\;2
$(MQTTDLL_AS): ${SOURCE_FILES} ${HEADERS}
-rm -f *.obj
${CC} ${CPPFLAGS} ${CFLAGS} ${CPPFLAGS_S} /I ${MQTTCLIENT_DIR} /I ${MQTTCLIENT_DIR}/.. /Fo ${SOURCE_FILES_AS}
$(LD) $(LIBLDFLAGS_S) *.obj /out:$(MQTTDLL_AS)
mt -manifest windows_ia32/mqttv3as.dll.manifest -outputresource:$(MQTTDLL_AS)\;2
endif
#########################################################################################################
# The following was how the C Clients were built using cygwin GNU make and has been left here as a reference
# This has been converted to use ming GNU make (which uses the native format of file names)
#
#$(MQTTDLL): $(shell cygpath -au ${SOURCE_FILES}) $(shell cygpath -au ${HEADERS})
# -mkdir windows_ia32
# ${CC} ${CPPFLAGS} ${CFLAGS} /I $(shell cygpath -am ${MQTTCLIENT_DIR}) /I $(shell cygpath -am ${MQTTCLIENT_DIR})/.. /Fo $(shell cygpath -am ${SOURCE_FILES})
# $(LD) $(LIBLDFLAGS) *.obj /out:$(MQTTDLL)
# mt -manifest windows_ia32/mqttv3c.dll.manifest -outputresource:$(MQTTDLL)\;2
#
#endif
#########################################################################################################

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -8,6 +8,7 @@
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs - add SSL support
*******************************************************************************/
/**
@ -46,5 +47,5 @@ int clientSocketCompare(void* a, void* b)
{
Clients* client = (Clients*)a;
/*printf("comparing %d with %d\n", (char*)a, (char*)b); */
return client->socket == *(int*)b;
return client->net.socket == *(int*)b;
}

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -8,12 +8,20 @@
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs - add SSL support
*******************************************************************************/
#if !defined(CLIENTS_H)
#define CLIENTS_H
#include <time.h>
#if defined(OPENSSL)
#if defined(WIN32)
#include "winsock2.h"
#endif
#include <openssl/ssl.h>
#endif
#include "MQTTClient.h"
#include "LinkedList.h"
#include "MQTTClientPersistence.h"
/*BE
@ -116,6 +124,7 @@ def CLIENTS
at 4 n8 bits 7:6 dec "connect_state"
at 8
n32 dec "socket"
n32 ptr "SSL"
n32 dec "msgID"
n32 dec "keepAliveInterval"
n32 dec "maxInflightMessages"
@ -132,6 +141,15 @@ defList(CLIENTS)
BE*/
typedef struct
{
int socket;
#if defined(OPENSSL)
SSL* ssl;
SSL_CTX* ctx;
#endif
} networkHandles;
/**
* Data related to one client
*/
@ -144,8 +162,8 @@ typedef struct
unsigned int connected : 1; /**< whether it is currently connected */
unsigned int good : 1; /**< if we have an error on the socket we turn this off */
unsigned int ping_outstanding : 1;
unsigned int connect_state : 2;
int socket;
int connect_state : 4;
networkHandles net;
int msgID;
int keepAliveInterval;
int retryInterval;
@ -155,9 +173,13 @@ typedef struct
List* inboundMsgs;
List* outboundMsgs; /**< in flight */
List* messageQueue;
unsigned int qentry_seqno;
void* phandle; /* the persistence handle */
MQTTClient_persistence* persistence; /* a persistence implementation */
int connectOptionsVersion;
#if defined(OPENSSL)
MQTTClient_SSLOptions *sslopts;
SSL_SESSION* session; /***< SSL session pointer for fast handhake */
#endif
} Clients;
int clientIDCompare(void* a, void* b);

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -8,9 +8,9 @@
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs - use tree data structure instead of list
*******************************************************************************/
/**
* @file
* \brief functions to manage the heap with the goal of eliminating memory leaks
@ -21,7 +21,7 @@
*
* */
#include "LinkedList.h"
#include "Tree.h"
#include "Log.h"
#include "StackTrace.h"
#include "Thread.h"
@ -30,6 +30,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stddef.h>
#include "Heap.h"
@ -37,20 +38,88 @@
#undef realloc
#undef free
static heap_info state = {0, 0};
#if defined(WIN32)
mutex_type heap_mutex;
#else
static pthread_mutex_t heap_mutex_store = PTHREAD_MUTEX_INITIALIZER;
static mutex_type heap_mutex = &heap_mutex_store;
#endif
int ListRemoveCurrentItem(List* aList);
static heap_info state = {0, 0}; /**< global heap state information */
static int eyecatcher = 0x88888888;
/**
* Each item on the heap is recorded with this structure.
*/
typedef struct
{
char* file;
int line;
void* ptr;
size_t size;
char* stack;
char* file; /**< the name of the source file where the storage was allocated */
int line; /**< the line no in the source file where it was allocated */
void* ptr; /**< pointer to the allocated storage */
int size; /**< size of the allocated storage */
} storageElement;
static List heap = {NULL, NULL, NULL};
static Tree heap; /**< Tree that holds the allocation records */
static char* errmsg = "Memory allocation error";
/**
* Round allocation size up to a multiple of the size of an int. Apart from possibly reducing fragmentation,
* on the old v3 gcc compilers I was hitting some weird behaviour, which might have been errors in
* sizeof() used on structures and related to packing. In any case, this fixes that too.
* @param size the size actually needed
* @return the rounded up size
*/
int roundup(int size)
{
static int multsize = 4*sizeof(int);
if (size % multsize != 0)
size += multsize - (size % multsize);
return size;
}
/**
* List callback function for comparing storage elements
* @param a pointer to the current content in the tree (storageElement*)
* @param b pointer to the memory to free
* @return boolean indicating whether a and b are equal
*/
int ptrCompare(void* a, void* b, int value)
{
a = ((storageElement*)a)->ptr;
if (value)
b = ((storageElement*)b)->ptr;
return (a > b) ? -1 : (a == b) ? 0 : 1;
}
void Heap_check(char* string, void* ptr)
{
return;
/*Node* curnode = NULL;
storageElement* prev, *s = NULL;
printf("Heap_check start %p\n", ptr);
while ((curnode = TreeNextElement(&heap, curnode)) != NULL)
{
prev = s;
s = (storageElement*)(curnode->content);
if (prev)
{
if (ptrCompare(s, prev, 1) != -1)
{
printf("%s: heap order error %d %p %p\n", string, ptrCompare(s, prev, 1), prev->ptr, s->ptr);
exit(99);
}
else
printf("%s: heap order good %d %p %p\n", string, ptrCompare(s, prev, 1), prev->ptr, s->ptr);
}
}*/
}
/**
* Allocates a block of memory. A direct replacement for malloc, but keeps track of items
@ -63,90 +132,61 @@ static List heap = {NULL, NULL, NULL};
*/
void* mymalloc(char* file, int line, size_t size)
{
ListElement* e = NULL;
storageElement* s = NULL;
static char* errmsg = "Memory allocation error";
int space = sizeof(storageElement);
int filenamelen = strlen(file)+1;
Thread_lock_mutex(heap_mutex);
size = roundup(size);
if ((s = malloc(sizeof(storageElement))) == NULL)
{
Log(LOG_ERROR, -1, errmsg);
Log(LOG_ERROR, 13, errmsg);
return NULL;
}
if ((s->file = malloc(strlen(file)+1)) == NULL)
s->size = size; /* size without eyecatchers */
if ((s->file = malloc(filenamelen)) == NULL)
{
Log(LOG_ERROR, -1, errmsg);
Log(LOG_ERROR, 13, errmsg);
free(s);
return NULL;
}
space += filenamelen;
strcpy(s->file, file);
s->line = line;
if ((s->ptr = malloc(size)) == NULL)
/* Add space for eyecatcher at each end */
if ((s->ptr = malloc(size + 2*sizeof(int))) == NULL)
{
Log(LOG_ERROR, -1, errmsg);
Log(LOG_ERROR, 13, errmsg);
free(s->file);
free(s);
return NULL;
}
s->size = size;
s->stack = NULL;
if (trace_settings.trace_level == TRACE_MAXIMUM)
{
if ((s->stack = StackTrace_get(Thread_getid())) == NULL)
{
Log(LOG_ERROR, -1, errmsg);
free(s->ptr);
free(s->file);
free(s);
return NULL;
}
else
{
Log(TRACE_MAX, -1, "Allocating %d bytes in heap at line %d of file %s, ptr %p, heap use now %d bytes",
s->size, line, file, s->ptr, state.current_size);
Log(TRACE_MAX, -1, "Stack trace is %s", s->stack);
}
}
if ((e = malloc(sizeof(ListElement))) == NULL)
{
Log(LOG_ERROR, -1, errmsg);
if (s->stack)
free(s->stack);
free(s->ptr);
free(s->file);
free(s);
return NULL;
}
ListAppendNoMalloc(&heap, s, e, sizeof(ListElement));
space += size + 2*sizeof(int);
*(int*)(s->ptr) = eyecatcher; /* start eyecatcher */
*(int*)(((char*)(s->ptr)) + (sizeof(int) + size)) = eyecatcher; /* end eyecatcher */
Log(TRACE_MAX, -1, "Allocating %d bytes in heap at file %s line %d ptr %p\n", size, file, line, s->ptr);
TreeAdd(&heap, s, space);
state.current_size += size;
if (state.current_size > state.max_size)
state.max_size = state.current_size;
return s->ptr;
Thread_unlock_mutex(heap_mutex);
return ((int*)(s->ptr)) + 1; /* skip start eyecatcher */
}
/**
* List callback function for comparing storage elements
* @param a first integer value
* @param b second integer value
* @return boolean indicating whether a and b are equal
*/
int ptrCompare(void* a, void* b)
void checkEyecatchers(char* file, int line, void* p, int size)
{
storageElement* s = (storageElement*)a;
return s->ptr == b;
}
int *sp = (int*)p;
char *cp = (char*)p;
int us;
static char* msg = "Invalid %s eyecatcher %d in heap item at file %s line %d";
if ((us = *--sp) != eyecatcher)
Log(LOG_ERROR, 13, msg, "start", us, file, line);
/**
* Utility to find an item in the heap. Lets you know if the heap already contains
* the memory location in question.
* @param p pointer to a memory location
* @return pointer to the storage element if found, or NULL
*/
void* Heap_findItem(void* p)
{
ListElement* e = ListFindItem(&heap, p, ptrCompare);
return (e == NULL) ? NULL : e->content;
cp += size;
if ((us = *(int*)cp) != eyecatcher)
Log(LOG_ERROR, 13, msg, "end", us, file, line);
}
@ -157,36 +197,28 @@ void* Heap_findItem(void* p)
* @param line use the __LINE__ macro to indicate which line this item was allocated at
* @param p pointer to the item to be removed
*/
void Internal_heap_unlink(char* file, int line, void* p)
int Internal_heap_unlink(char* file, int line, void* p)
{
ListElement* e = NULL;
Node* e = NULL;
int rc = 0;
if ((e = ListFindItem(&heap, p, ptrCompare)) == NULL)
Log(LOG_ERROR, -1, "Failed to remove heap item at file %s line %d", file, line);
e = TreeFind(&heap, ((int*)p)-1);
if (e == NULL)
Log(LOG_ERROR, 13, "Failed to remove heap item at file %s line %d", file, line);
else
{
storageElement* s = (storageElement*)(heap.current->content);
Log(TRACE_MAX, -1, "Freeing %d bytes in heap at file %s line %d, heap use now %d bytes",
storageElement* s = (storageElement*)(e->content);
Log(TRACE_MAX, -1, "Freeing %d bytes in heap at file %s line %d, heap use now %d bytes\n",
s->size, file, line, state.current_size);
checkEyecatchers(file, line, p, s->size);
//free(s->ptr);
free(s->file);
if (s->stack)
free(s->stack);
state.current_size -= s->size;
ListRemoveCurrentItem(&heap);
TreeRemoveNodeIndex(&heap, e, 0);
free(s);
rc = 1;
}
}
/**
* Remove an item from the recorded heap without actually freeing it.
* Use sparingly!
* @param file use the __FILE__ macro to indicate which file this item was allocated in
* @param line use the __LINE__ macro to indicate which line this item was allocated at
* @param p pointer to the item to be removed
*/
void Heap_unlink(char* file, int line, void* p)
{
Internal_heap_unlink(file, line, p);
return rc;
}
@ -199,8 +231,25 @@ void Heap_unlink(char* file, int line, void* p)
*/
void myfree(char* file, int line, void* p)
{
Thread_lock_mutex(heap_mutex);
if (Internal_heap_unlink(file, line, p))
free(((int*)p)-1);
Thread_unlock_mutex(heap_mutex);
}
/**
* Remove an item from the recorded heap without actually freeing it.
* Use sparingly!
* @param file use the __FILE__ macro to indicate which file this item was allocated in
* @param line use the __LINE__ macro to indicate which line this item was allocated at
* @param p pointer to the item to be removed
*/
void Heap_unlink(char* file, int line, void* p)
{
Thread_lock_mutex(heap_mutex);
Internal_heap_unlink(file, line, p);
free(p);
Thread_unlock_mutex(heap_mutex);
}
@ -208,6 +257,8 @@ void myfree(char* file, int line, void* p)
* Reallocates a block of memory. A direct replacement for realloc, but keeps track of items
* allocated in a list, so that free can check that a item is being freed correctly and that
* we can check that all memory is freed at shutdown.
* We have to remove the item from the tree, as the memory is in order and so it needs to
* be reinserted in the correct place.
* @param file use the __FILE__ macro to indicate which file this item was reallocated in
* @param line use the __LINE__ macro to indicate which line this item was reallocated at
* @param p pointer to the item to be reallocated
@ -217,56 +268,58 @@ void myfree(char* file, int line, void* p)
void *myrealloc(char* file, int line, void* p, size_t size)
{
void* rc = NULL;
ListElement* e = ListFindItem(&heap, p, ptrCompare);
if (e == NULL)
Log(LOG_ERROR, -1, "Failed to reallocate heap item at file %s line %d", file, line);
storageElement* s = NULL;
Thread_lock_mutex(heap_mutex);
s = TreeRemoveKey(&heap, ((int*)p)-1);
if (s == NULL)
Log(LOG_ERROR, 13, "Failed to reallocate heap item at file %s line %d", file, line);
else
{
storageElement* s = (storageElement*)(heap.current->content);
int space = sizeof(storageElement);
int filenamelen = strlen(file)+1;
checkEyecatchers(file, line, p, s->size);
size = roundup(size);
state.current_size += size - s->size;
if (state.current_size > state.max_size)
state.max_size = state.current_size;
rc = s->ptr = realloc(s->ptr, size);
if ((s->ptr = realloc(s->ptr, size + 2*sizeof(int))) == NULL)
{
Log(LOG_ERROR, 13, errmsg);
return NULL;
}
space += size + 2*sizeof(int) - s->size;
*(int*)(s->ptr) = eyecatcher; /* start eyecatcher */
*(int*)(((char*)(s->ptr)) + (sizeof(int) + size)) = eyecatcher; /* end eyecatcher */
s->size = size;
s->file = realloc(s->file, strlen(file)+1);
space -= strlen(s->file);
s->file = realloc(s->file, filenamelen);
space += filenamelen;
strcpy(s->file, file);
s->line = line;
if (s->stack)
{
free(s->stack);
s->stack = StackTrace_get(Thread_getid());
}
rc = s->ptr;
TreeAdd(&heap, s, space);
}
return rc;
Thread_unlock_mutex(heap_mutex);
return (rc == NULL) ? NULL : ((int*)(rc)) + 1; /* skip start eyecatcher */
}
/**
* Removes and frees the current item in a list. A list function only used by myfree.
* @param aList the list from which the item is to be removed
* @return 1=item removed, 0=item not removed
* Utility to find an item in the heap. Lets you know if the heap already contains
* the memory location in question.
* @param p pointer to a memory location
* @return pointer to the storage element if found, or NULL
*/
int ListRemoveCurrentItem(List* aList)
void* Heap_findItem(void* p)
{
ListElement* next = NULL;
Node* e = NULL;
if (aList->current->prev == NULL)
/* so this is the first element, and we have to update the "first" pointer */
aList->first = aList->current->next;
else
aList->current->prev->next = aList->current->next;
if (aList->current->next == NULL)
aList->last = aList->current->prev;
else
aList->current->next->prev = aList->current->prev;
next = aList->current->next;
free(aList->current->content);
free(aList->current);
aList->current = next;
return 1; /* successfully removed item */
Thread_lock_mutex(heap_mutex);
e = TreeFind(&heap, ((int*)p)-1);
Thread_unlock_mutex(heap_mutex);
return (e == NULL) ? NULL : e->content;
}
@ -274,19 +327,20 @@ int ListRemoveCurrentItem(List* aList)
* Scans the heap and reports any items currently allocated.
* To be used at shutdown if any heap items have not been freed.
*/
void HeapScan()
void HeapScan(int log_level)
{
ListElement* current = NULL;
Log(TRACE_MIN, -1, "Heap scan start, total %d bytes", state.current_size);
while (ListNextElement(&heap, &current))
Node* current = NULL;
Thread_lock_mutex(heap_mutex);
Log(log_level, -1, "Heap scan start, total %d bytes", state.current_size);
while ((current = TreeNextElement(&heap, current)) != NULL)
{
storageElement* s = (storageElement*)(current->content);
Log(TRACE_MIN, -1, "Heap element size %d, line %d, file %s, ptr %p", s->size, s->line, s->file, s->ptr);
Log(TRACE_MIN, -1, " Content %*.s", (10 > s->size) ? s->size : 10, (char*)s->ptr);
if (s->stack)
Log(TRACE_MIN, -1, " Stack trace: %s", s->stack);
Log(log_level, -1, "Heap element size %d, line %d, file %s, ptr %p", s->size, s->line, s->file, s->ptr);
Log(log_level, -1, " Content %*.s", (10 > current->size) ? s->size : 10, (char*)(((int*)s->ptr) + 1));
}
Log(TRACE_MIN, -1, "Heap scan end");
Log(log_level, -1, "Heap scan end");
Thread_unlock_mutex(heap_mutex);
}
@ -295,6 +349,8 @@ void HeapScan()
*/
int Heap_initialize()
{
TreeInitializeNoMalloc(&heap, ptrCompare);
heap.heap_tracking = 0; /* no recursive heap tracking! */
return 0;
}
@ -308,7 +364,7 @@ void Heap_terminate()
if (state.current_size > 20) /* One log list is freed after this function is called */
{
Log(LOG_ERROR, -1, "Some memory not freed at shutdown, possible memory leak");
HeapScan();
HeapScan(LOG_ERROR);
}
}
@ -326,29 +382,19 @@ heap_info* Heap_get_info()
/**
* Dump a string from the heap so that it can be displayed conveniently
* @param file file handle to dump the heap contents to
* @param str the string to dump
* @param str the string to dump, could be NULL
*/
int HeapDumpString(FILE* file, char* str)
{
int rc = 0;
int len = str ? strlen(str) + 1 : 0; /* include the trailing null */
if (str)
{
int* i = (int*)str;
int j;
int len = strlen(str);
if (fwrite(&(str), sizeof(int), 1, file) != 1)
rc = -1;
if (fwrite(&(len), sizeof(int), 1 ,file) != 1)
rc = -1;
for (j = 0; j < len; j++)
{
if (fwrite(i, sizeof(int), 1, file) != 1)
rc = -1;
i++;
}
}
if (fwrite(&(str), sizeof(char*), 1, file) != 1)
rc = -1;
else if (fwrite(&(len), sizeof(int), 1 ,file) != 1)
rc = -1;
else if (len > 0 && fwrite(str, len, 1, file) != 1)
rc = -1;
return rc;
}
@ -356,32 +402,58 @@ int HeapDumpString(FILE* file, char* str)
/**
* Dump the state of the heap
* @param file file handle to dump the heap contents to
* @return completion code, success == 0
*/
int HeapDump(FILE* file)
{
int rc = 0;
Node* current = NULL;
if (file != NULL)
while (rc == 0 && (current = TreeNextElement(&heap, current)))
{
ListElement* current = NULL;
while (ListNextElement(&heap, &current))
{
storageElement* s = (storageElement*)(current->content);
int* i = s->ptr;
unsigned int j;
storageElement* s = (storageElement*)(current->content);
if (fwrite(&(s->ptr), sizeof(int), 1, file) != 1)
rc = -1;
if (fwrite(&(s->size), sizeof(int), 1, file) != 1)
rc = -1;
for (j = 0; j < s->size; j++)
{
if (fwrite(i, sizeof(int), 1, file) != -1)
rc = -1;
i++;
}
}
if (fwrite(&(s->ptr), sizeof(s->ptr), 1, file) != 1)
rc = -1;
else if (fwrite(&(current->size), sizeof(current->size), 1, file) != 1)
rc = -1;
else if (fwrite(s->ptr, current->size, 1, file) != 1)
rc = -1;
}
return rc;
}
#if defined(HEAP_UNIT_TESTS)
void Log(int log_level, int msgno, char* format, ...)
{
printf("Log %s", format);
}
#define malloc(x) mymalloc(__FILE__, __LINE__, x)
#define realloc(a, b) myrealloc(__FILE__, __LINE__, a, b)
#define free(x) myfree(__FILE__, __LINE__, x)
int main(int argc, char *argv[])
{
char* h = NULL;
Heap_initialize();
h = malloc(12);
free(h);
printf("freed h\n");
h = malloc(12);
h = realloc(h, 14);
h = realloc(h, 25);
h = realloc(h, 255);
h = realloc(h, 2225);
h = realloc(h, 22225);
printf("freeing h\n");
free(h);
Heap_terminate();
printf("Finishing\n");
return 0;
}
#endif

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -8,19 +8,22 @@
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs - use tree data structure instead of list
*******************************************************************************/
#if !defined(HEAP_H)
#define HEAP_H
#include <stdio.h>
#if defined(HIGH_PERFORMANCE)
#define NO_HEAP_TRACKING 1
#endif
#include <stdio.h>
#include <memory.h>
#include <stdlib.h>
#define HEAP_TRACKING 1
#if HEAP_TRACKING
#if !defined(NO_HEAP_TRACKING)
/**
* redefines malloc to use "mymalloc" so that heap allocation can be tracked
* @param x the size of the item to be allocated
@ -44,23 +47,27 @@
#endif
/**
* Information about the state of the heap.
*/
typedef struct
{
int current_size; /**< current size of the heap in bytes */
int max_size; /**< max size the heap has reached in bytes */
} heap_info;
void* mymalloc(char*, int, size_t size);
void* myrealloc(char*, int, void* p, size_t size);
void myfree(char*, int, void* p);
void* Heap_findItem(void* p);
void Heap_unlink(char*, int, void* p);
typedef struct
{
int current_size;
int max_size;
} heap_info;
void HeapScan();
void Heap_scan(FILE* file);
int Heap_initialize(void);
void Heap_terminate(void);
heap_info* Heap_get_info(void);
int HeapDump(FILE* file);
int HeapDumpString(FILE* file, char* str);
void* Heap_findItem(void* p);
void Heap_unlink(char* file, int line, void* p);
#endif

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -8,6 +8,7 @@
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs - updates for the async client
*******************************************************************************/
/**
@ -253,9 +254,9 @@ int ListRemove(List* aList, void* content)
* @param aList the list from which the item is to be removed
* @return 1=item removed, 0=item not removed
*/
int ListRemoveHead(List* aList)
void* ListDetachHead(List* aList)
{
int rc = 0;
void *content = NULL;
if (aList->count > 0)
{
ListElement* first = aList->first;
@ -263,14 +264,26 @@ int ListRemoveHead(List* aList)
aList->current = first->next;
if (aList->last == first) /* i.e. no of items in list == 1 */
aList->last = NULL;
free(first->content);
content = first->content;
aList->first = aList->first->next;
if (aList->first)
aList->first->prev = NULL;
free(first);
--(aList->count);
}
return rc;
return content;
}
/**
* Removes and frees an the first item in a list.
* @param aList the list from which the item is to be removed
* @return 1=item removed, 0=item not removed
*/
int ListRemoveHead(List* aList)
{
free(ListDetachHead(aList));
return 0;
}

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -8,6 +8,7 @@
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs - updates for the async client
*******************************************************************************/
#if !defined(LINKEDLIST_H)
@ -74,6 +75,7 @@ void ListInsert(List* aList, void* content, int size, ListElement* index);
int ListRemove(List* aList, void* content);
int ListRemoveItem(List* aList, void* content, int(*callback)(void*, void*));
void* ListDetachHead(List* aList);
int ListRemoveHead(List* aList);
void* ListPopTail(List* aList);

132
src/Log.c
View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -8,6 +8,7 @@
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs - updates for the async client
*******************************************************************************/
#include "Log.h"
@ -17,6 +18,7 @@
#include "Messages.h"
#include "LinkedList.h"
#include "StackTrace.h"
#include "Thread.h"
#include <stdio.h>
#include <stdlib.h>
@ -26,6 +28,7 @@
#if !defined(WIN32)
#include <syslog.h>
#include <sys/stat.h>
#define GETTIMEOFDAY 1
#else
#define snprintf _snprintf
@ -37,6 +40,13 @@
#include <sys/timeb.h>
#endif
#if !defined(WIN32)
/**
* _unlink mapping for linux
*/
#define _unlink unlink
#endif
#if !defined(min)
#define min(A,B) ( (A) < (B) ? (A):(B))
@ -75,7 +85,13 @@ static traceEntry* trace_queue = NULL;
static int trace_queue_size = 0;
static FILE* trace_destination = NULL; /**< flag to indicate if trace is to be sent to a stream */
static char* trace_destination_name = NULL; /**< the name of the trace file */
static char* trace_destination_backup_name = NULL; /**< the name of the backup trace file */
static int lines_written = 0; /**< number of lines written to the current output file */
static int max_lines_per_file = 1000; /**< maximum number of lines to write to one trace file */
static int trace_output_level = -1;
static Log_traceCallback* trace_callback = NULL;
static void Log_output(int log_level, char* msg);
static int sametime_count = 0;
#if defined(GETTIMEOFDAY)
@ -85,8 +101,15 @@ struct timeb ts, last_ts;
#endif
static char msg_buf[512];
#if defined(WIN32)
mutex_type log_mutex;
#else
static pthread_mutex_t log_mutex_store = PTHREAD_MUTEX_INITIALIZER;
static mutex_type log_mutex = &log_mutex_store;
#endif
int Log_initialize()
int Log_initialize(Log_nameValue* info)
{
int rc = -1;
char* envval = NULL;
@ -97,8 +120,21 @@ int Log_initialize()
if ((envval = getenv("MQTT_C_CLIENT_TRACE")) != NULL && strlen(envval) > 0)
{
if (strcmp(envval, "ON") == 0 || (trace_destination = fopen(envval, "wt")) == NULL)
if (strcmp(envval, "ON") == 0 || (trace_destination = fopen(envval, "w")) == NULL)
trace_destination = stdout;
else
{
trace_destination_name = malloc(strlen(envval) + 1);
strcpy(trace_destination_name, envval);
trace_destination_backup_name = malloc(strlen(envval) + 3);
sprintf(trace_destination_backup_name, "%s.0", trace_destination_name);
}
}
if ((envval = getenv("MQTT_C_CLIENT_TRACE_MAX_LINES")) != NULL && strlen(envval) > 0)
{
max_lines_per_file = atoi(envval);
if (max_lines_per_file <= 0)
max_lines_per_file = 1000;
}
if ((envval = getenv("MQTT_C_CLIENT_TRACE_LEVEL")) != NULL && strlen(envval) > 0)
{
@ -110,11 +146,58 @@ int Log_initialize()
trace_settings.trace_level = TRACE_MINIMUM;
else if (strcmp(envval, "PROTOCOL") == 0 || strcmp(envval, "TRACE_PROTOCOL") == 0)
trace_output_level = TRACE_PROTOCOL;
else if (strcmp(envval, "ERROR") == 0 || strcmp(envval, "TRACE_ERROR") == 0)
trace_output_level = LOG_ERROR;
}
Log_output(TRACE_MINIMUM, "=========================================================");
Log_output(TRACE_MINIMUM, " Trace Output");
if (info)
{
while (info->name)
{
sprintf(msg_buf, "%s: %s", info->name, info->value);
Log_output(TRACE_MINIMUM, msg_buf);
info++;
}
}
#if !defined(WIN32)
struct stat buf;
if (stat("/proc/version", &buf) != -1)
{
FILE* vfile;
if ((vfile = fopen("/proc/version", "r")) != NULL)
{
int len;
strcpy(msg_buf, "/proc/version: ");
len = strlen(msg_buf);
if (fgets(&msg_buf[len], sizeof(msg_buf) - len, vfile))
Log_output(TRACE_MINIMUM, msg_buf);
fclose(vfile);
}
}
#endif
Log_output(TRACE_MINIMUM, "=========================================================");
return rc;
}
void Log_setTraceCallback(Log_traceCallback* callback)
{
trace_callback = callback;
}
void Log_setTraceLevel(enum LOG_LEVELS level)
{
if (level < TRACE_MINIMUM) /* the lowest we can go is TRACE_MINIMUM*/
trace_settings.trace_level = level;
trace_output_level = level;
}
void Log_terminate()
{
free(trace_queue);
@ -126,6 +209,10 @@ void Log_terminate()
fclose(trace_destination);
trace_destination = NULL;
}
if (trace_destination_name)
free(trace_destination_name);
if (trace_destination_backup_name)
free(trace_destination_backup_name);
start_index = -1;
next_index = 0;
trace_output_level = -1;
@ -222,13 +309,44 @@ static char* Log_formatTraceEntry(traceEntry* cur_entry)
}
static void Log_output(int log_level, char* msg)
{
if (trace_destination)
{
Thread_lock_mutex(log_mutex); /* need to lock around the fiddling with the log files */
fprintf(trace_destination, "%s\n", msg);
if (trace_destination != stdout && ++lines_written >= max_lines_per_file)
{
fclose(trace_destination);
_unlink(trace_destination_backup_name); /* remove any old backup trace file */
rename(trace_destination_name, trace_destination_backup_name); /* rename recently closed to backup */
trace_destination = fopen(trace_destination_name, "w"); /* open new trace file */
if (trace_destination == NULL)
trace_destination = stdout;
lines_written = 0;
}
else
fflush(trace_destination);
Thread_unlock_mutex(log_mutex);
}
if (trace_callback)
(*trace_callback)(log_level, msg);
}
static void Log_posttrace(int log_level, traceEntry* cur_entry)
{
if (trace_destination &&
((trace_output_level == -1) ? log_level >= trace_settings.trace_level : log_level >= trace_output_level))
if (((trace_output_level == -1) ? log_level >= trace_settings.trace_level : log_level >= trace_output_level))
{
fprintf(trace_destination, "%s\n", &Log_formatTraceEntry(cur_entry)[7]);
fflush(trace_destination);
char* msg = NULL;
if (trace_destination || trace_callback)
msg = &Log_formatTraceEntry(cur_entry)[7];
Log_output(log_level, msg);
}
}

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -8,6 +8,7 @@
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs - updates for the async client
*******************************************************************************/
#if !defined(LOG_H)
@ -27,7 +28,7 @@ map LOG_LEVELS
}
BE*/
enum {
enum LOG_LEVELS {
TRACE_MAXIMUM = 1,
TRACE_MEDIUM,
TRACE_MINIMUM,
@ -60,10 +61,20 @@ extern trace_settings_type trace_settings;
#define TRACE_MIN TRACE_MINIMUM
#define TRACE_MED TRACE_MEDIUM
int Log_initialize();
typedef struct
{
const char* name;
const char* value;
} Log_nameValue;
int Log_initialize(Log_nameValue*);
void Log_terminate();
void Log(int, int, char *, ...);
void Log_stackTrace(int, int, int, int, const char*, int, int*);
typedef void Log_traceCallback(enum LOG_LEVELS level, char* message);
void Log_setTraceCallback(Log_traceCallback* callback);
void Log_setTraceLevel(enum LOG_LEVELS level);
#endif

2542
src/MQTTAsync.c Normal file

File diff suppressed because it is too large Load Diff

1334
src/MQTTAsync.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -10,6 +10,7 @@
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs - bug 384016 - segv setting will message
* Ian Craggs - bug 384053 - v1.0.0.7 - stop MQTTClient_receive on socket error
* Ian Craggs, Allan Stockdill-Mander - add ability to connect with SSL
*******************************************************************************/
#include <stdlib.h>
@ -29,10 +30,14 @@
#include "StackTrace.h"
#include "Heap.h"
#if defined(OPENSSL)
#include <openssl/ssl.h>
#endif
#define URI_TCP "tcp://"
#define BUILD_TIMESTAMP __DATE__ " " __TIME__ /* __TIMESTAMP__ */
#define CLIENT_VERSION "1.0.0.7" /* __VERSION__ */
#define CLIENT_VERSION "1.0.0.8"
char* client_timestamp_eye = "MQTTClientV3_Timestamp " BUILD_TIMESTAMP;
char* client_version_eye = "MQTTClientV3_Version " CLIENT_VERSION;
@ -50,6 +55,8 @@ MQTTProtocol state;
#if defined(WIN32)
static mutex_type mqttclient_mutex = NULL;
extern mutex_type stack_mutex;
extern mutex_type heap_mutex;
extern mutex_type log_mutex;
BOOL APIENTRY DllMain(HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
@ -62,6 +69,8 @@ BOOL APIENTRY DllMain(HANDLE hModule,
{
mqttclient_mutex = CreateMutex(NULL, 0, NULL);
stack_mutex = CreateMutex(NULL, 0, NULL);
heap_mutex = CreateMutex(NULL, 0, NULL);
log_mutex = CreateMutex(NULL, 0, NULL);
}
case DLL_THREAD_ATTACH:
Log(TRACE_MAX, -1, "DLL thread attach");
@ -104,6 +113,9 @@ typedef struct
typedef struct
{
char* serverURI;
#if defined(OPENSSL)
int ssl;
#endif
Clients* c;
MQTTClient_connectionLost* cl;
MQTTClient_messageArrived* ma;
@ -119,7 +131,6 @@ typedef struct
} MQTTClients;
void MQTTClient_sleep(long milliseconds)
{
FUNC_ENTRY;
@ -210,10 +221,13 @@ int MQTTClient_create(MQTTClient* handle, char* serverURI, char* clientId,
#if defined(HEAP_H)
Heap_initialize();
#endif
Log_initialize();
Log_initialize(NULL);
bstate->clients = ListInitialize();
Socket_outInitialize();
handles = ListInitialize();
#if defined(OPENSSL)
SSLSocket_initialize();
#endif
initialized = 1;
}
m = malloc(sizeof(MQTTClients));
@ -221,6 +235,13 @@ int MQTTClient_create(MQTTClient* handle, char* serverURI, char* clientId,
memset(m, '\0', sizeof(MQTTClients));
if (strncmp(URI_TCP, serverURI, strlen(URI_TCP)) == 0)
serverURI += strlen(URI_TCP);
#if defined(OPENSSL)
else if (strncmp(URI_SSL, serverURI, strlen(URI_SSL)) == 0)
{
serverURI += strlen(URI_SSL);
m->ssl = 1;
}
#endif
m->serverURI = malloc(strlen(serverURI)+1);
strcpy(m->serverURI, serverURI);
ListAppend(handles, m, sizeof(MQTTClients));
@ -304,7 +325,7 @@ void MQTTClient_destroy(MQTTClient* handle)
if (m->c)
{
int saved_socket = m->c->socket;
int saved_socket = m->c->net.socket;
char* saved_clientid = malloc(strlen(m->c->clientID)+1);
strcpy(saved_clientid, m->c->clientID);
#if !defined(NO_PERSISTENCE)
@ -332,11 +353,6 @@ exit:
}
#if defined(HEAP_H)
#undef malloc
#undef realloc
#undef free
#endif
void MQTTClient_freeMessage(MQTTClient_message** message)
{
FUNC_ENTRY;
@ -354,13 +370,6 @@ void MQTTClient_free(void* memory)
FUNC_EXIT;
}
#if defined(HEAP_H)
#define malloc(x) mymalloc(__FILE__, __LINE__, x)
#define realloc(a, b) myrealloc(__FILE__, __LINE__, a, b)
#define free(x) myfree(__FILE__, __LINE__, x)
#endif
int MQTTClient_deliverMessage(int rc, MQTTClients* m, char** topicName, int* topicLen, MQTTClient_message** message)
{
@ -373,9 +382,6 @@ int MQTTClient_deliverMessage(int rc, MQTTClients* m, char** topicName, int* top
if (strlen(*topicName) != *topicLen)
rc = MQTTCLIENT_TOPICNAME_TRUNCATED;
ListRemove(m->c->messageQueue, m->c->messageQueue->first->content);
Heap_unlink(__FILE__, __LINE__, (*message)->payload);
Heap_unlink(__FILE__, __LINE__, *message);
Heap_unlink(__FILE__, __LINE__, *topicName);
FUNC_EXIT_RC(rc);
return rc;
}
@ -390,7 +396,7 @@ int MQTTClient_deliverMessage(int rc, MQTTClients* m, char** topicName, int* top
int clientSockCompare(void* a, void* b)
{
MQTTClients* m = (MQTTClients*)a;
return m->c->socket == *(int*)b;
return m->c->net.socket == *(int*)b;
}
@ -457,8 +463,6 @@ thread_return_type WINAPI MQTTClient_run(void* n)
{
qEntry* qe = (qEntry*)(m->c->messageQueue->first->content);
int topicLen = qe->topicLen;
void* payload_ptr = qe->msg->payload; /* saved so we can unlink it after a successful messageArrived call,
because it is held in a structure which might be freed */
if (strlen(qe->topicName) == topicLen)
topicLen = 0;
@ -473,12 +477,7 @@ thread_return_type WINAPI MQTTClient_run(void* n)
* so we must be careful how we use it.
*/
if (rc)
{
Heap_unlink(__FILE__, __LINE__, qe->topicName);
Heap_unlink(__FILE__, __LINE__, payload_ptr);
Heap_unlink(__FILE__, __LINE__, qe->msg);
ListRemove(m->c->messageQueue, qe);
}
else
Log(TRACE_MIN, -1, "False returned from messageArrived for client %s, message remains on queue",
m->c->clientID);
@ -509,11 +508,31 @@ thread_return_type WINAPI MQTTClient_run(void* n)
int error;
socklen_t len = sizeof(error);
if ((m->rc = getsockopt(m->c->socket, SOL_SOCKET, SO_ERROR, (char*)&error, &len)) == 0)
if ((m->rc = getsockopt(m->c->net.socket, SOL_SOCKET, SO_ERROR, (char*)&error, &len)) == 0)
m->rc = error;
Log(TRACE_MIN, -1, "Posting connect semaphore for client %s rc %d", m->c->clientID, m->rc);
Thread_post_sem(m->connect_sem);
}
#if defined(OPENSSL)
else if (m->c->connect_state == 2 && !Thread_check_sem(m->connect_sem))
{
rc = SSLSocket_connect(m->c->net.ssl, m->c->net.socket);
if (rc == 1)
{
if (!m->c->cleansession && m->c->session == NULL)
m->c->session = SSL_get1_session(m->c->net.ssl);
m->rc = rc;
Log(TRACE_MIN, -1, "Posting connect semaphore for SSL client %s rc %d", m->c->clientID, m->rc);
Thread_post_sem(m->connect_sem);
}
else if (rc == SSL_FATAL)
{
m->rc = rc;
//tostop = 1;
break;
}
}
#endif
}
}
run_id = 0;
@ -594,16 +613,23 @@ void MQTTProtocol_closeSession(Clients* client, int sendwill)
{
FUNC_ENTRY;
client->good = 0;
if (client->socket > 0)
if (client->net.socket > 0)
{
if (client->connected || client->connect_state)
{
MQTTPacket_send_disconnect(client->socket, client->clientID);
MQTTPacket_send_disconnect(&client->net, client->clientID);
client->connected = 0;
client->connect_state = 0;
}
Socket_close(client->socket);
client->socket = 0;
#if defined(OPENSSL)
if (client->net.ssl)
SSLSocket_close(client->net.ssl);
#endif
Socket_close(client->net.socket);
client->net.socket = 0;
#if defined(OPENSSL)
client->net.ssl = NULL;
#endif
}
if (client->cleansession)
@ -685,7 +711,7 @@ int MQTTClient_connect(MQTTClient handle, MQTTClient_connectOptions* options)
goto exit;
}
if (strncmp(options->struct_id, "MQTC", 4) != 0 || options->struct_version != 0)
if (strncmp(options->struct_id, "MQTC", 4) != 0 || (options->struct_version != 0 && options->struct_version != 1))
{
rc = MQTTCLIENT_BAD_STRUCTURE;
goto exit;
@ -699,6 +725,17 @@ int MQTTClient_connect(MQTTClient handle, MQTTClient_connectOptions* options)
goto exit;
}
}
#if defined(OPENSSL)
if (options->struct_version != 0 && options->ssl) /* check validity of SSL options structure */
{
if (strncmp(options->ssl->struct_id, "MQTS", 4) != 0 || options->ssl->struct_version != 0)
{
rc = MQTTCLIENT_BAD_STRUCTURE;
goto exit;
}
}
#endif
if ((options->username && !UTF8_validateString(options->username)) ||
(options->password && !UTF8_validateString(options->password)))
@ -726,20 +763,30 @@ int MQTTClient_connect(MQTTClient handle, MQTTClient_connectOptions* options)
if (options->will && options->will->struct_version == 0)
{
m->c->will = malloc(sizeof(willMessages));
m->c->will = malloc(sizeof(willMessages));
m->c->will->msg = options->will->message;
m->c->will->qos = options->will->qos;
m->c->will->retained = options->will->retained;
m->c->will->topic = options->will->topicName;
}
#if defined(OPENSSL)
if (options->struct_version != 0 && options->ssl)
m->c->sslopts = options->ssl;
#endif
m->c->username = options->username;
m->c->password = options->password;
m->c->retryInterval = options->retryInterval;
m->c->connectOptionsVersion = options->struct_version;
Log(TRACE_MIN, -1, "Connecting to serverURI %s", m->serverURI);
#if defined(OPENSSL)
rc = MQTTProtocol_connect(m->serverURI, m->c, m->ssl);
#else
rc = MQTTProtocol_connect(m->serverURI, m->c);
#endif
if (rc == SOCKET_ERROR)
goto exit;
if (m->c->connect_state == 0)
{
@ -747,7 +794,7 @@ int MQTTClient_connect(MQTTClient handle, MQTTClient_connectOptions* options)
goto exit;
}
if (m->c->connect_state == 1)
if (m->c->connect_state == 1) /* TCP connect started - wait for completion */
{
Thread_unlock_mutex(mqttclient_mutex);
MQTTClient_waitfor(handle, CONNECT, &rc, millisecsTimeout - MQTTClient_elapsed(start));
@ -757,16 +804,67 @@ int MQTTClient_connect(MQTTClient handle, MQTTClient_connectOptions* options)
rc = SOCKET_ERROR;
goto exit;
}
m->c->connect_state = 2; /* TCP connect completed, in which case send the MQTT connect packet */
#if defined(OPENSSL)
if (m->ssl)
{
if ((m->c->net.ssl = SSLSocket_setSocketForSSL(m->c->net.socket, m->c->sslopts)) != NULL)
{
if (m->c->session != NULL)
if ((rc = SSL_set_session(m->c->net.ssl, m->c->session)) != 1)
Log(TRACE_MIN, -1, "Failed to set SSL session with stored data, non critical");
rc = SSLSocket_connect(m->c->net.ssl, m->c->net.socket);
if (rc == -1)
m->c->connect_state = 2;
else if (rc == SSL_FATAL)
{
rc = SOCKET_ERROR;
goto exit;
}
else if (rc == 1 && !m->c->cleansession && m->c->session == NULL)
m->c->session = SSL_get1_session(m->c->net.ssl);
}
else
{
rc = SOCKET_ERROR;
goto exit;
}
}
else
{
#endif
m->c->connect_state = 3; /* TCP connect completed, in which case send the MQTT connect packet */
if (MQTTPacket_send_connect(m->c) == SOCKET_ERROR)
{
rc = SOCKET_ERROR;
goto exit;
}
#if defined(OPENSSL)
}
#endif
}
#if defined(OPENSSL)
if (m->c->connect_state == 2) /* SSL connect sent - wait for completion */
{
Thread_unlock_mutex(mqttclient_mutex);
MQTTClient_waitfor(handle, CONNECT, &rc, millisecsTimeout - MQTTClient_elapsed(start));
Thread_lock_mutex(mqttclient_mutex);
if (rc != 1)
{
rc = SOCKET_ERROR;
goto exit;
}
m->c->connect_state = 3; /* TCP connect completed, in which case send the MQTT connect packet */
if (MQTTPacket_send_connect(m->c) == SOCKET_ERROR)
{
rc = SOCKET_ERROR;
goto exit;
}
}
#endif
if (m->c->connect_state == 2)
if (m->c->connect_state == 3) /* MQTT connect sent - wait for CONNACK */
{
MQTTPacket* pack = NULL;
@ -778,11 +876,12 @@ int MQTTClient_connect(MQTTClient handle, MQTTClient_connectOptions* options)
else
{
Connack* connack = (Connack*)pack;
Log(LOG_PROTOCOL, 1, NULL, m->c->net.socket, m->c->clientID, connack->rc);
if ((rc = connack->rc) == MQTTCLIENT_SUCCESS)
{
m->c->connected = 1;
m->c->good = 1;
m->c->connect_state = 3;
m->c->connect_state = 0; //3;
time(&(m->c->lastContact));
if (m->c->cleansession)
rc = MQTTClient_cleanSession(m->c);
@ -813,11 +912,11 @@ int MQTTClient_connect(MQTTClient handle, MQTTClient_connectOptions* options)
}
exit:
if (m->c->will)
{
free(m->c->will);
m->c->will = NULL;
}
if (m->c->will)
{
free(m->c->will);
m->c->will = NULL;
}
Thread_unlock_mutex(mqttclient_mutex);
FUNC_EXIT_RC(rc);
return rc;
@ -935,6 +1034,12 @@ int MQTTClient_subscribeMany(MQTTClient handle, int count, char** topic, int* qo
rc = MQTTCLIENT_BAD_UTF8_STRING;
goto exit;
}
if(qos[i] < 0 || qos[i] > 2)
{
rc = MQTTCLIENT_BAD_QOS;
goto exit;
}
}
for (i = 0; i < count; i++)
@ -955,7 +1060,7 @@ int MQTTClient_subscribeMany(MQTTClient handle, int count, char** topic, int* qo
Thread_lock_mutex(mqttclient_mutex);
if (pack != NULL)
{
rc = MQTTProtocol_handleSubacks(pack, m->c->socket);
rc = MQTTProtocol_handleSubacks(pack, m->c->net.socket);
m->pack = NULL;
}
else
@ -1033,7 +1138,7 @@ int MQTTClient_unsubscribeMany(MQTTClient handle, int count, char** topic)
Thread_lock_mutex(mqttclient_mutex);
if (pack != NULL)
{
rc = MQTTProtocol_handleUnsubacks(pack, m->c->socket);
rc = MQTTProtocol_handleUnsubacks(pack, m->c->net.socket);
m->pack = NULL;
}
else
@ -1122,7 +1227,7 @@ int MQTTClient_publish(MQTTClient handle, char* topicName, int payloadlen, void*
*/
if (rc == TCPSOCKET_INTERRUPTED)
{
while (m->c->connected == 1 && SocketBuffer_getWrite(m->c->socket))
while (m->c->connected == 1 && SocketBuffer_getWrite(m->c->net.socket))
{
Thread_unlock_mutex(mqttclient_mutex);
MQTTClient_yield();
@ -1210,18 +1315,28 @@ MQTTPacket* MQTTClient_cycle(int* sock, unsigned long timeout, int* rc)
tp.tv_usec = (timeout % 1000) * 1000; /* this field is microseconds! */
}
/* 0 from getReadySocket indicates no work to do, -1 == error, but can happen normally */
*sock = Socket_getReadySocket(0, &tp);
#if defined(OPENSSL)
if ((*sock = SSLSocket_getPendingRead()) == -1)
{
/* 0 from getReadySocket indicates no work to do, -1 == error, but can happen normally */
#endif
*sock = Socket_getReadySocket(0, &tp);
#if defined(OPENSSL)
}
#endif
Thread_lock_mutex(mqttclient_mutex);
if (*sock > 0)
{
MQTTClients* m = NULL;
if (ListFindItem(handles, sock, clientSockCompare) != NULL)
m = (MQTTClient)(handles->current->content);
if (m != NULL && m->c->connect_state == 1)
*rc = 0; /* waiting for connect state to clear */
else
pack = MQTTPacket_Factory(*sock, rc);
if (m != NULL)
{
if (m->c->connect_state == 1 || m->c->connect_state == 2)
*rc = 0; /* waiting for connect state to clear */
else
pack = MQTTPacket_Factory(&m->c->net, rc);
}
if (pack)
{
int freed = 1;
@ -1257,7 +1372,7 @@ MQTTPacket* MQTTClient_cycle(int* sock, unsigned long timeout, int* rc)
}
MQTTClient_retry();
Thread_unlock_mutex(mqttclient_mutex);
FUNC_EXIT;
FUNC_EXIT_RC(*rc);
return pack;
}
@ -1299,7 +1414,7 @@ MQTTPacket* MQTTClient_waitfor(MQTTClient handle, int packet_type, int* rc, long
{
int sock = -1;
pack = MQTTClient_cycle(&sock, 100L, rc);
if (sock == m->c->socket)
if (sock == m->c->net.socket)
{
if (pack && (pack->header.bits.type == packet_type))
break;
@ -1308,10 +1423,22 @@ MQTTPacket* MQTTClient_waitfor(MQTTClient handle, int packet_type, int* rc, long
int error;
socklen_t len = sizeof(error);
if ((*rc = getsockopt(m->c->socket, SOL_SOCKET, SO_ERROR, (char*)&error, &len)) == 0)
if ((*rc = getsockopt(m->c->net.socket, SOL_SOCKET, SO_ERROR, (char*)&error, &len)) == 0)
*rc = error;
break;
}
#if defined(OPENSSL)
else if (m->c->connect_state == 2)
{
*rc = SSLSocket_connect(m->c->net.ssl, sock);
if (*rc == 1 || *rc == SSL_FATAL)
{
if (*rc == 1 && !m->c->cleansession && m->c->session == NULL)
m->c->session = SSL_get1_session(m->c->net.ssl);
break;
}
}
#endif
}
else if (MQTTClient_elapsed(start) > timeout)
{
@ -1359,15 +1486,13 @@ int MQTTClient_receive(MQTTClient handle, char** topicName, int* topicLen, MQTTC
{
int sock = 0;
MQTTClient_cycle(&sock, (timeout > elapsed) ? timeout - elapsed : 0L, &rc);
if (rc == SOCKET_ERROR)
{
if (ListFindItem(handles, &sock, clientSockCompare) && /* find client corresponding to socket */
(MQTTClient)(handles->current->content) == handle)
break; /* there was an error on the socket we are interested in */
}
elapsed = MQTTClient_elapsed(start);
}
while (elapsed < timeout && m->c->messageQueue->count == 0);
@ -1493,7 +1618,7 @@ int MQTTClient_getPendingDeliveryTokens(MQTTClient handle, MQTTClient_deliveryTo
int count = 0;
*tokens = malloc(sizeof(MQTTClient_deliveryToken) * (m->c->outboundMsgs->count + 1));
Heap_unlink(__FILE__, __LINE__, *tokens);
/*Heap_unlink(__FILE__, __LINE__, *tokens);*/
while (ListNextElement(m->c->outboundMsgs, &current))
{
Messages* m = (Messages*)(current->content);
@ -1507,3 +1632,35 @@ exit:
FUNC_EXIT_RC(rc);
return rc;
}
MQTTClient_nameValue* MQTTClient_getVersionInfo()
{
#define MAX_INFO_STRINGS 7
static MQTTClient_nameValue libinfo[MAX_INFO_STRINGS + 1];
int i = 0;
libinfo[i].name = "version";
libinfo[i++].value = CLIENT_VERSION;
libinfo[i].name = "build level";
libinfo[i++].value = BUILD_TIMESTAMP;
#if defined(OPENSSL)
libinfo[i].name = "OpenSSL version";
libinfo[i++].value = SSLeay_version(SSLEAY_VERSION);
libinfo[i].name = "OpenSSL flags";
libinfo[i++].value = SSLeay_version(SSLEAY_CFLAGS);
libinfo[i].name = "OpenSSL build timestamp";
libinfo[i++].value = SSLeay_version(SSLEAY_BUILT_ON);
libinfo[i].name = "OpenSSL platform";
libinfo[i++].value = SSLeay_version(SSLEAY_PLATFORM);
libinfo[i].name = "OpenSSL directory";
libinfo[i++].value = SSLeay_version(SSLEAY_DIR);
#endif
libinfo[i].name = NULL;
libinfo[i].value = NULL;
return libinfo;
}

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -8,19 +8,31 @@
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs, Allan Stockdill-Mander - SSL updates
*******************************************************************************/
/**
* @mainpage MQTT Client library for C
* &copy; Copyright IBM Corp. 2009, 2011
* &copy; Copyright IBM Corp. 2009, 2012
*
* @brief An MQTT client library in C.
*
* An MQTT client application connects to MQTT-capable servers.
* An MQTT client application connects to MQTT-capable servers.
* A typical client is responsible for collecting information from a telemetry
* device and publishing the information to the server. It can also subscribe
* to topics, receive messages, and use this information to control the
* telemetry device. MQTT clients implement the published MQTT v3 protocol.
* telemetry device.
*
* MQTT clients implement the published MQTT v3 protocol. You can write your own
* API to the MQTT protocol using the programming language and platform of your
* choice. This can be time-consuming and error-prone.
*
* To simplify writing MQTT client applications, WebSphere MQ Telemetry provides
* C and Java client libraries that encapsulate the MQTT v3 protocol for a
* number of platforms. If you incorporate these libraries in your MQTT
* applications, a fully functional MQTT client can be written in a few lines of
* code. The information presented here documents the API provided by the IBM
* MQTT Client library for C.
*
* <b>Using the client</b><br>
* Applications that use the client library typically use a similar structure:
@ -112,6 +124,10 @@
* and version number.
*/
#define MQTTCLIENT_BAD_STRUCTURE -8
/**
* Return code: A QoS value that falls outside of the acceptable range (0,1,2)
*/
#define MQTTCLIENT_BAD_QOS -9
/**
* A handle representing an MQTT client. A valid client handle is available
@ -129,6 +145,7 @@ typedef void* MQTTClient;
* MQTTClient_getPendingDeliveryTokens()).
*/
typedef int MQTTClient_deliveryToken;
typedef int MQTTClient_token;
/**
* A structure representing the payload and attributes of an MQTT message. The
@ -261,7 +278,6 @@ typedef void MQTTClient_deliveryComplete(void* context, MQTTClient_deliveryToken
*/
typedef void MQTTClient_connectionLost(void* context, char* cause);
/**
* This function sets the callback functions for a specific client.
* If your client application doesn't use a particular callback, set the
@ -290,7 +306,8 @@ typedef void MQTTClient_connectionLost(void* context, char* cause);
* ::MQTTCLIENT_FAILURE if an error occurred.
*/
DLLExport int MQTTClient_setCallbacks(MQTTClient handle, void* context, MQTTClient_connectionLost* cl,
MQTTClient_messageArrived* ma, MQTTClient_deliveryComplete* dc);
MQTTClient_messageArrived* ma, MQTTClient_deliveryComplete* dc);
/**
* This function creates an MQTT client ready for connection to the
@ -373,6 +390,57 @@ typedef struct
#define MQTTClient_willOptions_initializer { "MQTW", 0, NULL, NULL, 0, 0 }
/**
* MQTTClient_sslProperties defines the settings to establish an SSL/TLS connection using the
* OpenSSL library. It covers the following scenarios:
* - Server authentication: The client needs the digital certificate of the server. It is included
* in a store containting trusted material (also known as "trust store").
* - Mutual authentication: Both client and server are authenticated during the SSL handshake. In
* addition to the digital certificate of the server in a trust store, the client will need its own
* digital certificate and the private key used to sign its digital certificate stored in a "key store".
* - Anonymous connection: Both client and server do not get authenticated and no credentials are needed
* to establish an SSL connection. Note that this scenario is not fully secure since it is subject to
* man-in-the-middle attacks.
*/
typedef struct
{
/** The eyecatcher for this structure. Must be MQTS */
char struct_id[4];
/** The version number of this structure. Must be 0 */
int struct_version;
/** The file in PEM format containing the public digital certificates trusted by the client. */
char* trustStore;
/** The file in PEM format containing the public certificate chain of the client. It may also include
* the client's private key.
*/
char* keyStore;
/** If not included in the sslKeyStore, this setting points to the file in PEM format containing
* the client's private key.
*/
char* privateKey;
/** The password to load the client's privateKey if encrypted. */
char* privateKeyPassword;
/**
* The list of cipher suites that the client will present to the server during the SSL handshake. For a
* full explanation of the cipher list format, please see the OpenSSL on-line documentation:
* http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT
* If this setting is ommitted, its default value will be "ALL", that is, all the cipher suites -excluding
* those offering no encryption- will be considered.
* This setting can be used to set an SSL anonymous connection ("aNULL" string value, for instance).
*/
char* enabledCipherSuites;
/** True/False option to enable verification of the server certificate **/
int enableServerCertAuth;
} MQTTClient_SSLOptions;
#define MQTTClient_SSLOptions_initializer { "MQTS", 0, NULL, NULL, NULL, NULL, NULL, 1 }
/**
* MQTTClient_connectOptions defines several settings that control the way the
* client connects to an MQTT server.
@ -391,7 +459,7 @@ typedef struct
{
/** The eyecatcher for this structure. must be MQTC. */
char struct_id[4];
/** The version number of this structure. Must be 0 */
/** The version number of this structure. Must be 0 or 1. 0 signifies no SSL options */
int struct_version;
/** The "keep alive" interval, measured in seconds, defines the maximum time
* that should pass without communication between the client and the server
@ -461,9 +529,34 @@ typedef struct
* The time interval in seconds
*/
int retryInterval;
/**
* This is a pointer to an MQTTClient_SSLOptions structure. If your
* application does not make use of SSL, set this pointer to NULL.
*/
MQTTClient_SSLOptions* ssl;
} MQTTClient_connectOptions;
#define MQTTClient_connectOptions_initializer { "MQTC", 0, 60, 1, 1, NULL, NULL, NULL, 30, 20 }
#define MQTTClient_connectOptions_initializer { "MQTC", 1, 60, 1, 1, NULL, NULL, NULL, 30, 20, NULL }
/**
* MQTTClient_libraryInfo is used to store details relating to the currently used
* library such as the version in use, the time it was built and relevant openSSL
* options.
* There is one static instance of this struct in MQTTClient.c
*/
typedef struct
{
const char* name;
const char* value;
} MQTTClient_nameValue;
/**
* This function returns version information about the library.
* no trace information will be returned.
* @return an array of strings describing the library. The last entry is a NULL pointer.
*/
DLLExport MQTTClient_nameValue* MQTTClient_getVersionInfo();
/**
* This function attempts to connect a previously-created client (see
@ -602,7 +695,6 @@ DLLExport int MQTTClient_unsubscribeMany(MQTTClient handle, int count, char** to
*/
DLLExport int MQTTClient_publish(MQTTClient handle, char* topicName, int payloadlen, void* payload, int qos, int retained,
MQTTClient_deliveryToken* dt);
/**
* This function attempts to publish a message to a given topic (see also
* MQTTClient_publish()). An ::MQTTClient_deliveryToken is issued when

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -8,6 +8,7 @@
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs, Allan Stockdill-Mander - SSL updates
*******************************************************************************/
/**
@ -44,6 +45,8 @@ static char* packet_names[] =
"PINGREQ", "PINGRESP", "DISCONNECT"
};
char** MQTTClient_packet_names = packet_names;
/**
* Converts an MQTT packet code into its name
@ -84,7 +87,7 @@ pf new_packets[] =
* @param error pointer to the error code which is completed if no packet is returned
* @return the packet structure or NULL if there was an error
*/
void* MQTTPacket_Factory(int socket, int* error)
void* MQTTPacket_Factory(networkHandles* net, int* error)
{
char* data = NULL;
static Header header;
@ -96,15 +99,26 @@ void* MQTTPacket_Factory(int socket, int* error)
*error = SOCKET_ERROR; /* indicate whether an error occurred, or not */
/* read the packet data from the socket */
if ((*error = Socket_getch(socket, &(header.byte))) != TCPSOCKET_COMPLETE) /* first byte is the header byte */
#if defined(OPENSSL)
*error = (net->ssl) ? SSLSocket_getch(net->ssl, net->socket, &header.byte) : Socket_getch(net->socket, &header.byte);
#else
*error = Socket_getch(net->socket, &header.byte);
#endif
if (*error != TCPSOCKET_COMPLETE) /* first byte is the header byte */
goto exit; /* packet not read, *error indicates whether SOCKET_ERROR occurred */
/* now read the remaining length, so we know how much more to read */
if ((*error = MQTTPacket_decode(socket, &remaining_length)) != TCPSOCKET_COMPLETE)
if ((*error = MQTTPacket_decode(net, &remaining_length)) != TCPSOCKET_COMPLETE)
goto exit; /* packet not read, *error indicates whether SOCKET_ERROR occurred */
/* now read the rest, the variable header and payload */
if ((data = Socket_getdata(socket, remaining_length, &actual_len)) == NULL)
#if defined(OPENSSL)
data = (net->ssl) ? SSLSocket_getdata(net->ssl, net->socket, remaining_length, &actual_len) :
Socket_getdata(net->socket, remaining_length, &actual_len);
#else
data = Socket_getdata(net->socket, remaining_length, &actual_len);
#endif
if (data == NULL)
{
*error = SOCKET_ERROR;
goto exit; /* socket error */
@ -128,7 +142,7 @@ void* MQTTPacket_Factory(int socket, int* error)
char *buf = malloc(10);
buf[0] = header.byte;
buf0len = 1 + MQTTPacket_encode(&buf[1], remaining_length);
*error = MQTTPersistence_put(socket, buf, buf0len, 1,
*error = MQTTPersistence_put(net->socket, buf, buf0len, 1,
&data, &remaining_length, header.bits.type, ((Publish *)pack)->msgId, 1);
free(buf);
}
@ -149,7 +163,7 @@ exit:
* @param buflen the length of the data in buffer to be written
* @return the completion code (TCPSOCKET_COMPLETE etc)
*/
int MQTTPacket_send(int socket, Header header, char* buffer, int buflen)
int MQTTPacket_send(networkHandles* net, Header header, char* buffer, int buflen)
{
int rc, buf0len;
char *buf;
@ -163,11 +177,18 @@ int MQTTPacket_send(int socket, Header header, char* buffer, int buflen)
{
char* ptraux = buffer;
int msgId = readInt(&ptraux);
rc = MQTTPersistence_put(socket, buf, buf0len, 1, &buffer, &buflen,
rc = MQTTPersistence_put(net->socket, buf, buf0len, 1, &buffer, &buflen,
header.bits.type, msgId, 0);
}
#endif
rc = Socket_putdatas(socket, buf, buf0len, 1, &buffer, &buflen);
#if defined(OPENSSL)
if (net->ssl)
rc = SSLSocket_putdatas(net->ssl, net->socket, buf, buf0len, 1, &buffer, &buflen);
else
#endif
rc = Socket_putdatas(net->socket, buf, buf0len, 1, &buffer, &buflen);
if (rc != TCPSOCKET_INTERRUPTED)
free(buf);
@ -185,7 +206,7 @@ int MQTTPacket_send(int socket, Header header, char* buffer, int buflen)
* @param buflens the lengths of the data in the array of buffers to be written
* @return the completion code (TCPSOCKET_COMPLETE etc)
*/
int MQTTPacket_sends(int socket, Header header, int count, char** buffers, int* buflens)
int MQTTPacket_sends(networkHandles* net, Header header, int count, char** buffers, int* buflens)
{
int i, rc, buf0len, total = 0;
char *buf;
@ -201,11 +222,16 @@ int MQTTPacket_sends(int socket, Header header, int count, char** buffers, int*
{ /* persist PUBLISH QoS1 and Qo2 */
char *ptraux = buffers[2];
int msgId = readInt(&ptraux);
rc = MQTTPersistence_put(socket, buf, buf0len, count, buffers, buflens,
rc = MQTTPersistence_put(net->socket, buf, buf0len, count, buffers, buflens,
header.bits.type, msgId, 0);
}
#endif
rc = Socket_putdatas(socket, buf, buf0len, count, buffers, buflens);
#if defined(OPENSSL)
if (net->ssl)
rc = SSLSocket_putdatas(net->ssl, net->socket, buf, buf0len, count, buffers, buflens);
else
#endif
rc = Socket_putdatas(net->socket, buf, buf0len, count, buffers, buflens);
if (rc != TCPSOCKET_INTERRUPTED)
free(buf);
FUNC_EXIT_RC(rc);
@ -244,7 +270,7 @@ int MQTTPacket_encode(char* buf, int length)
* @param value the decoded length returned
* @return the number of bytes read from the socket
*/
int MQTTPacket_decode(int socket, int* value)
int MQTTPacket_decode(networkHandles* net, int* value)
{
int rc = SOCKET_ERROR;
char c;
@ -261,8 +287,13 @@ int MQTTPacket_decode(int socket, int* value)
rc = SOCKET_ERROR; /* bad data */
goto exit;
}
if ((rc = Socket_getch(socket, &c)) != TCPSOCKET_COMPLETE)
goto exit;
#if defined(OPENSSL)
rc = (net->ssl) ? SSLSocket_getch(net->ssl, net->socket, &c) : Socket_getch(net->socket, &c);
#else
rc = Socket_getch(net->socket, &c);
#endif
if (rc != TCPSOCKET_COMPLETE)
goto exit;
*value += (c & 127) * multiplier;
multiplier *= 128;
} while ((c & 128) != 0);
@ -409,7 +440,7 @@ void* MQTTPacket_header_only(unsigned char aHeader, char* data, int datalen)
* @param socket the open socket to send the data to
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
*/
int MQTTPacket_send_disconnect(int socket, char* clientID)
int MQTTPacket_send_disconnect(networkHandles *net, char* clientID)
{
Header header;
int rc = 0;
@ -417,8 +448,8 @@ int MQTTPacket_send_disconnect(int socket, char* clientID)
FUNC_ENTRY;
header.byte = 0;
header.bits.type = DISCONNECT;
rc = MQTTPacket_send(socket, header, NULL, 0);
Log(LOG_PROTOCOL, 28, NULL, socket, clientID, rc);
rc = MQTTPacket_send(net, header, NULL, 0);
Log(LOG_PROTOCOL, 28, NULL, net->socket, clientID, rc);
FUNC_EXIT_RC(rc);
return rc;
}
@ -479,7 +510,7 @@ void MQTTPacket_freePublish(Publish* pack)
* @param socket the open socket to send the data to
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
*/
int MQTTPacket_send_ack(int type, int msgid, int dup, int socket)
int MQTTPacket_send_ack(int type, int msgid, int dup, networkHandles *net)
{
Header header;
int rc;
@ -491,7 +522,7 @@ int MQTTPacket_send_ack(int type, int msgid, int dup, int socket)
header.bits.type = type;
header.bits.dup = dup;
writeInt(&ptr, msgid);
if ((rc = MQTTPacket_send(socket, header, buf, 2)) != TCPSOCKET_INTERRUPTED)
if ((rc = MQTTPacket_send(net, header, buf, 2)) != TCPSOCKET_INTERRUPTED)
free(buf);
FUNC_EXIT_RC(rc);
return rc;
@ -504,13 +535,13 @@ int MQTTPacket_send_ack(int type, int msgid, int dup, int socket)
* @param socket the open socket to send the data to
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
*/
int MQTTPacket_send_puback(int msgid, int socket, char* clientID)
int MQTTPacket_send_puback(int msgid, networkHandles* net, char* clientID)
{
int rc = 0;
FUNC_ENTRY;
rc = MQTTPacket_send_ack(PUBACK, msgid, 0, socket);
Log(LOG_PROTOCOL, 12, NULL, socket, clientID, msgid, rc);
rc = MQTTPacket_send_ack(PUBACK, msgid, 0, net);
Log(LOG_PROTOCOL, 12, NULL, net->socket, clientID, msgid, rc);
FUNC_EXIT_RC(rc);
return rc;
}
@ -536,13 +567,13 @@ void MQTTPacket_freeSuback(Suback* pack)
* @param socket the open socket to send the data to
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
*/
int MQTTPacket_send_pubrec(int msgid, int socket, char* clientID)
int MQTTPacket_send_pubrec(int msgid, networkHandles* net, char* clientID)
{
int rc = 0;
FUNC_ENTRY;
rc = MQTTPacket_send_ack(PUBREC, msgid, 0, socket);
Log(LOG_PROTOCOL, 13, NULL, socket, clientID, msgid, rc);
rc = MQTTPacket_send_ack(PUBREC, msgid, 0, net);
Log(LOG_PROTOCOL, 13, NULL, net->socket, clientID, msgid, rc);
FUNC_EXIT_RC(rc);
return rc;
}
@ -555,13 +586,13 @@ int MQTTPacket_send_pubrec(int msgid, int socket, char* clientID)
* @param socket the open socket to send the data to
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
*/
int MQTTPacket_send_pubrel(int msgid, int dup, int socket, char* clientID)
int MQTTPacket_send_pubrel(int msgid, int dup, networkHandles* net, char* clientID)
{
int rc = 0;
FUNC_ENTRY;
rc = MQTTPacket_send_ack(PUBREL, msgid, dup, socket);
Log(LOG_PROTOCOL, 16, NULL, socket, clientID, msgid, rc);
rc = MQTTPacket_send_ack(PUBREL, msgid, dup, net);
Log(LOG_PROTOCOL, 16, NULL, net->socket, clientID, msgid, rc);
FUNC_EXIT_RC(rc);
return rc;
}
@ -573,13 +604,13 @@ int MQTTPacket_send_pubrel(int msgid, int dup, int socket, char* clientID)
* @param socket the open socket to send the data to
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
*/
int MQTTPacket_send_pubcomp(int msgid, int socket, char* clientID)
int MQTTPacket_send_pubcomp(int msgid, networkHandles* net, char* clientID)
{
int rc = 0;
FUNC_ENTRY;
rc = MQTTPacket_send_ack(PUBCOMP, msgid, 0, socket);
Log(LOG_PROTOCOL, 18, NULL, socket, clientID, msgid, rc);
rc = MQTTPacket_send_ack(PUBCOMP, msgid, 0, net);
Log(LOG_PROTOCOL, 18, NULL, net->socket, clientID, msgid, rc);
FUNC_EXIT_RC(rc);
return rc;
}
@ -614,7 +645,7 @@ void* MQTTPacket_ack(unsigned char aHeader, char* data, int datalen)
* @param socket the open socket to send the data to
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
*/
int MQTTPacket_send_publish(Publish* pack, int dup, int qos, int retained, int socket, char* clientID)
int MQTTPacket_send_publish(Publish* pack, int dup, int qos, int retained, networkHandles* net, char* clientID)
{
Header header;
char *topiclen;
@ -636,7 +667,7 @@ int MQTTPacket_send_publish(Publish* pack, int dup, int qos, int retained, int s
writeInt(&ptr, pack->msgId);
ptr = topiclen;
writeInt(&ptr, lens[1]);
rc = MQTTPacket_sends(socket, header, 4, bufs, lens);
rc = MQTTPacket_sends(net, header, 4, bufs, lens);
if (rc != TCPSOCKET_INTERRUPTED)
free(buf);
}
@ -646,14 +677,14 @@ int MQTTPacket_send_publish(Publish* pack, int dup, int qos, int retained, int s
char* bufs[3] = {topiclen, pack->topic, pack->payload};
int lens[3] = {2, strlen(pack->topic), pack->payloadlen};
writeInt(&ptr, lens[1]);
rc = MQTTPacket_sends(socket, header, 3, bufs, lens);
rc = MQTTPacket_sends(net, header, 3, bufs, lens);
}
if (rc != TCPSOCKET_INTERRUPTED)
free(topiclen);
if (qos == 0)
Log(LOG_PROTOCOL, 27, NULL, socket, clientID, retained, rc);
Log(LOG_PROTOCOL, 27, NULL, net->socket, clientID, retained, rc);
else
Log(LOG_PROTOCOL, 10, NULL, socket, clientID, pack->msgId, qos, retained, rc,
Log(LOG_PROTOCOL, 10, NULL, net->socket, clientID, pack->msgId, qos, retained, rc,
min(20, pack->payloadlen), pack->payload);
FUNC_EXIT_RC(rc);
return rc;

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -8,12 +8,16 @@
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs, Allan Stockdill-Mander - SSL updates
*******************************************************************************/
#if !defined(MQTTPACKET_H)
#define MQTTPACKET_H
#include "Socket.h"
#if defined(OPENSSL)
#include "SSLSocket.h"
#endif
#include "LinkedList.h"
#include "Clients.h"
@ -191,7 +195,7 @@ typedef Ack Pubcomp;
typedef Ack Unsuback;
int MQTTPacket_encode(char* buf, int length);
int MQTTPacket_decode(int socket, int* value);
int MQTTPacket_decode(networkHandles* net, int* value);
int readInt(char** pptr);
char* readUTF(char** pptr, char* enddata);
char readChar(char** pptr);
@ -201,23 +205,23 @@ void writeUTF(char** pptr, char* string);
char* MQTTPacket_name(int ptype);
void* MQTTPacket_Factory(int socket, int* error);
int MQTTPacket_send(int socket, Header header, char* buffer, int buflen);
int MQTTPacket_sends(int socket, Header header, int count, char** buffers, int* buflens);
void* MQTTPacket_Factory(networkHandles* net, int* error);
int MQTTPacket_send(networkHandles* net, Header header, char* buffer, int buflen);
int MQTTPacket_sends(networkHandles* net, Header header, int count, char** buffers, int* buflens);
void* MQTTPacket_header_only(unsigned char aHeader, char* data, int datalen);
int MQTTPacket_send_disconnect(int socket, char* clientID);
int MQTTPacket_send_disconnect(networkHandles* net, char* clientID);
void* MQTTPacket_publish(unsigned char aHeader, char* data, int datalen);
void MQTTPacket_freePublish(Publish* pack);
int MQTTPacket_send_publish(Publish* pack, int dup, int qos, int retained, int socket, char* clientID);
int MQTTPacket_send_puback(int msgid, int socket, char* clientID);
int MQTTPacket_send_publish(Publish* pack, int dup, int qos, int retained, networkHandles* net, char* clientID);
int MQTTPacket_send_puback(int msgid, networkHandles* net, char* clientID);
void* MQTTPacket_ack(unsigned char aHeader, char* data, int datalen);
void MQTTPacket_freeSuback(Suback* pack);
int MQTTPacket_send_pubrec(int msgid, int socket, char* clientID);
int MQTTPacket_send_pubrel(int msgid, int dup, int socket, char* clientID);
int MQTTPacket_send_pubcomp(int msgid, int socket, char* clientID);
int MQTTPacket_send_pubrec(int msgid, networkHandles* net, char* clientID);
int MQTTPacket_send_pubrel(int msgid, int dup, networkHandles* net, char* clientID);
int MQTTPacket_send_pubcomp(int msgid, networkHandles* net, char* clientID);
void MQTTPacket_free_packet(MQTTPacket* pack);

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -8,6 +8,7 @@
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs, Allan Stockdill-Mander - SSL updates
*******************************************************************************/
/**
@ -83,8 +84,8 @@ int MQTTPacket_send_connect(Clients* client)
if (client->password)
writeUTF(&ptr, client->password);
rc = MQTTPacket_send(client->socket, packet.header, buf, len);
Log(LOG_PROTOCOL, 0, NULL, client->socket, client->clientID, client->cleansession, rc);
rc = MQTTPacket_send(&client->net, packet.header, buf, len);
Log(LOG_PROTOCOL, 0, NULL, client->net.socket, client->clientID, client->cleansession, rc);
free(buf);
FUNC_EXIT_RC(rc);
return rc;
@ -118,7 +119,7 @@ void* MQTTPacket_connack(unsigned char aHeader, char* data, int datalen)
* @param clientID the string client identifier, only used for tracing
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
*/
int MQTTPacket_send_pingreq(int socket, char* clientID)
int MQTTPacket_send_pingreq(networkHandles* net, char* clientID)
{
Header header;
int rc = 0;
@ -126,7 +127,7 @@ int MQTTPacket_send_pingreq(int socket, char* clientID)
FUNC_ENTRY;
header.byte = 0;
header.bits.type = PINGREQ;
rc = MQTTPacket_send(socket, header, NULL, 0);
rc = MQTTPacket_send(net, header, NULL, 0);
Log(LOG_PROTOCOL, 20, NULL, socket, clientID, rc);
FUNC_EXIT_RC(rc);
return rc;
@ -143,7 +144,7 @@ int MQTTPacket_send_pingreq(int socket, char* clientID)
* @param clientID the string client identifier, only used for tracing
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
*/
int MQTTPacket_send_subscribe(List* topics, List* qoss, int msgid, int dup, int socket, char* clientID)
int MQTTPacket_send_subscribe(List* topics, List* qoss, int msgid, int dup, networkHandles* net, char* clientID)
{
Header header;
char *data, *ptr;
@ -170,8 +171,8 @@ int MQTTPacket_send_subscribe(List* topics, List* qoss, int msgid, int dup, int
writeUTF(&ptr, (char*)(elem->content));
writeChar(&ptr, *(int*)(qosElem->content));
}
rc = MQTTPacket_send(socket, header, data, datalen);
Log(LOG_PROTOCOL, 22, NULL, socket, clientID, msgid, rc);
rc = MQTTPacket_send(net, header, data, datalen);
Log(LOG_PROTOCOL, 22, NULL, net->socket, clientID, msgid, rc);
free(data);
FUNC_EXIT_RC(rc);
return rc;
@ -215,7 +216,7 @@ void* MQTTPacket_suback(unsigned char aHeader, char* data, int datalen)
* @param clientID the string client identifier, only used for tracing
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
*/
int MQTTPacket_send_unsubscribe(List* topics, int msgid, int dup, int socket, char* clientID)
int MQTTPacket_send_unsubscribe(List* topics, int msgid, int dup, networkHandles* net, char* clientID)
{
Header header;
char *data, *ptr;
@ -238,8 +239,8 @@ int MQTTPacket_send_unsubscribe(List* topics, int msgid, int dup, int socket, ch
elem = NULL;
while (ListNextElement(topics, &elem))
writeUTF(&ptr, (char*)(elem->content));
rc = MQTTPacket_send(socket, header, data, datalen);
Log(LOG_PROTOCOL, 25, NULL, socket, clientID, msgid, rc);
rc = MQTTPacket_send(net, header, data, datalen);
Log(LOG_PROTOCOL, 25, NULL, net->socket, clientID, msgid, rc);
free(data);
FUNC_EXIT_RC(rc);
return rc;

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -8,6 +8,7 @@
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs, Allan Stockdill-Mander - SSL updates
*******************************************************************************/
#if !defined(MQTTPACKETOUT_H)
@ -18,11 +19,11 @@
int MQTTPacket_send_connect(Clients* client);
void* MQTTPacket_connack(unsigned char aHeader, char* data, int datalen);
int MQTTPacket_send_pingreq(int socket, char* clientID);
int MQTTPacket_send_pingreq(networkHandles* net, char* clientID);
int MQTTPacket_send_subscribe(List* topics, List* qoss, int msgid, int dup, int socket, char* clientID);
int MQTTPacket_send_subscribe(List* topics, List* qoss, int msgid, int dup, networkHandles* net, char* clientID);
void* MQTTPacket_suback(unsigned char aHeader, char* data, int datalen);
int MQTTPacket_send_unsubscribe(List* topics, int msgid, int dup, int socket, char* clientID);
int MQTTPacket_send_unsubscribe(List* topics, int msgid, int dup, networkHandles* net, char* clientID);
#endif

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -8,6 +8,7 @@
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs - async client updates
*******************************************************************************/
/**
@ -165,17 +166,23 @@ int MQTTPersistence_clear(Clients *c)
int MQTTPersistence_restore(Clients *c)
{
int rc = 0;
char **msgkeys, *buffer;
char **msgkeys = NULL,
*buffer = NULL;
int nkeys, buflen;
int i = 0;
int msgs_sent = 0;
int msgs_rcvd = 0;
FUNC_ENTRY;
if ( c->persistence != NULL &&
(rc = c->persistence->pkeys(c->phandle, &msgkeys, &nkeys)) == 0 )
if (c->persistence && (rc = c->persistence->pkeys(c->phandle, &msgkeys, &nkeys)) == 0)
{
while( rc == 0 && i < nkeys)
while (rc == 0 && i < nkeys)
{
if ( (rc = c->persistence->pget(c->phandle, msgkeys[i], &buffer, &buflen)) == 0 )
if (strncmp(msgkeys[i], PERSISTENCE_COMMAND_KEY, strlen(PERSISTENCE_COMMAND_KEY)) == 0)
;
else if (strncmp(msgkeys[i], PERSISTENCE_QUEUE_KEY, strlen(PERSISTENCE_QUEUE_KEY)) == 0)
;
else if ((rc = c->persistence->pget(c->phandle, msgkeys[i], &buffer, &buflen)) == 0)
{
MQTTPacket* pack = MQTTPersistence_restorePacket(buffer, buflen);
if ( pack != NULL )
@ -190,6 +197,7 @@ int MQTTPersistence_restore(Clients *c)
ListAppend(c->inboundMsgs, msg, msg->len);
publish->topic = NULL;
MQTTPacket_freePublish(publish);
msgs_rcvd++;
}
else if ( strstr(msgkeys[i],PERSISTENCE_PUBLISH_SENT) != NULL )
{
@ -208,6 +216,7 @@ int MQTTPersistence_restore(Clients *c)
publish->topic = NULL;
MQTTPacket_freePublish(publish);
free(key);
msgs_sent++;
}
else if ( strstr(msgkeys[i],PERSISTENCE_PUBREL) != NULL )
{
@ -224,16 +233,20 @@ int MQTTPersistence_restore(Clients *c)
else /* pack == NULL -> bad persisted record */
rc = c->persistence->premove(c->phandle, msgkeys[i]);
}
if ( buffer != NULL )
if (buffer)
{
free(buffer);
if ( msgkeys[i] != NULL )
buffer = NULL;
}
if (msgkeys[i])
free(msgkeys[i]);
i++;
}
if ( msgkeys != NULL )
if (msgkeys)
free(msgkeys);
}
Log(TRACE_MINIMUM, -1, "%d sent messages and %d received messages restored for client %s\n",
msgs_sent, msgs_rcvd, c->clientID);
MQTTPersistence_wrapMsgID(c);
FUNC_EXIT_RC(rc);

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -8,6 +8,7 @@
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs - async client updates
*******************************************************************************/
#include "Clients.h"
@ -18,7 +19,11 @@
#define PERSISTENCE_PUBREL "sc-"
/** Stem of the key for a received PUBLISH QoS2 */
#define PERSISTENCE_PUBLISH_RECEIVED "r-"
/** Stem of the key for an async client command */
#define PERSISTENCE_COMMAND_KEY "c-"
/** Stem of the key for an async client message queue */
#define PERSISTENCE_QUEUE_KEY "q-"
#define PERSISTENCE_MAX_KEY_LENGTH 8
int MQTTPersistence_create(MQTTClient_persistence** per, int type, void* pcontext);
int MQTTPersistence_initialize(Clients* c, char* serverURI);

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -8,6 +8,7 @@
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs - async client updates
*******************************************************************************/
/**
@ -395,7 +396,10 @@ int containskeyUnix(char *dirname, char *key)
{
while((dir_entry = readdir(dp)) != NULL && notFound)
{
lstat(dir_entry->d_name, &stat_info);
char* filename = malloc(strlen(dirname) + strlen(dir_entry->d_name) + 2);
sprintf(filename, "%s/%s", dirname, dir_entry->d_name);
lstat(filename, &stat_info);
free(filename);
if(S_ISREG(stat_info.st_mode))
{
filekey = malloc(strlen(dir_entry->d_name) + 1);

View File

@ -9,6 +9,7 @@
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
*******************************************************************************/
#if !defined(MQTTPROTOCOL_H)
#define MQTTPROTOCOL_H

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -8,6 +8,7 @@
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs, Allan Stockdill-Mander - SSL updates
*******************************************************************************/
/**
@ -80,7 +81,7 @@ void MQTTProtocol_storeQoS0(Clients* pubclient, Publish* publish)
pw = malloc(sizeof(pending_write));
Log(TRACE_MIN, 12, NULL);
pw->p = MQTTProtocol_storePublication(publish, &len);
pw->socket = pubclient->socket;
pw->socket = pubclient->net.socket;
ListAppend(&(state.pending_writes), pw, sizeof(pending_write)+len);
/* we don't copy QoS 0 messages unless we have to, so now we have to tell the socket buffer where
the saved copy is */
@ -103,7 +104,7 @@ int MQTTProtocol_startPublishCommon(Clients* pubclient, Publish* publish, int qo
int rc = TCPSOCKET_COMPLETE;
FUNC_ENTRY;
rc = MQTTPacket_send_publish(publish, 0, qos, retained, pubclient->socket, pubclient->clientID);
rc = MQTTPacket_send_publish(publish, 0, qos, retained, &pubclient->net, pubclient->clientID);
if (qos == 0 && rc == TCPSOCKET_INTERRUPTED)
MQTTProtocol_storeQoS0(pubclient, publish);
FUNC_EXIT_RC(rc);
@ -253,7 +254,7 @@ int MQTTProtocol_handlePublishes(void* pack, int sock)
else if (publish->header.bits.qos == 1)
{
/* send puback before processing the publications because a lot of return publications could fill up the socket buffer */
rc = MQTTPacket_send_puback(publish->msgId, sock, client->clientID);
rc = MQTTPacket_send_puback(publish->msgId, &client->net, client->clientID);
/* if we get a socket error from sending the puback, should we ignore the publication? */
Protocol_processPublication(publish, client);
}
@ -277,7 +278,7 @@ int MQTTProtocol_handlePublishes(void* pack, int sock)
ListRemove(client->inboundMsgs, msg);
} else
ListAppend(client->inboundMsgs, m, sizeof(Messages) + len);
rc = MQTTPacket_send_pubrec(publish->msgId, sock, client->clientID);
rc = MQTTPacket_send_pubrec(publish->msgId, &client->net, client->clientID);
publish->topic = NULL;
}
MQTTPacket_freePublish(publish);
@ -363,7 +364,7 @@ int MQTTProtocol_handlePubrecs(void* pack, int sock)
}
else
{
rc = MQTTPacket_send_pubrel(pubrec->msgId, 0, sock, client->clientID);
rc = MQTTPacket_send_pubrel(pubrec->msgId, 0, &client->net, client->clientID);
m->nextMessageType = PUBCOMP;
time(&(m->lastTouch));
}
@ -397,7 +398,7 @@ int MQTTProtocol_handlePubrels(void* pack, int sock)
Log(TRACE_MIN, 3, NULL, "PUBREL", client->clientID, pubrel->msgId);
else
/* Apparently this is "normal" behaviour, so we don't need to issue a warning */
rc = MQTTPacket_send_pubcomp(pubrel->msgId, sock, client->clientID);
rc = MQTTPacket_send_pubcomp(pubrel->msgId, &client->net, client->clientID);
}
else
{
@ -411,7 +412,7 @@ int MQTTProtocol_handlePubrels(void* pack, int sock)
Publish publish;
/* send pubcomp before processing the publications because a lot of return publications could fill up the socket buffer */
rc = MQTTPacket_send_pubcomp(pubrel->msgId, sock, client->clientID);
rc = MQTTPacket_send_pubcomp(pubrel->msgId, &client->net, client->clientID);
publish.header.bits.qos = m->qos;
publish.header.bits.retain = m->retain;
publish.msgId = m->msgid;
@ -497,10 +498,10 @@ void MQTTProtocol_keepalive(time_t now)
{
Clients* client = (Clients*)(current->content);
ListNextElement(bstate->clients, &current);
if (client->connected && client->keepAliveInterval > 0
if (client->connected && client->keepAliveInterval > 0 && Socket_noPendingWrites(client->net.socket)
&& (difftime(now, client->lastContact) >= client->keepAliveInterval))
{
MQTTPacket_send_pingreq(client->socket, client->clientID);
MQTTPacket_send_pingreq(&client->net, client->clientID);
client->lastContact = now;
client->ping_outstanding = 1;
}
@ -533,17 +534,17 @@ void MQTTProtocol_retries(time_t now, Clients* client)
Publish publish;
int rc;
Log(TRACE_MIN, 7, NULL, "PUBLISH", client->clientID, client->socket, m->msgid);
Log(TRACE_MIN, 7, NULL, "PUBLISH", client->clientID, client->net.socket, m->msgid);
publish.msgId = m->msgid;
publish.topic = m->publish->topic;
publish.payload = m->publish->payload;
publish.payloadlen = m->publish->payloadlen;
rc = MQTTPacket_send_publish(&publish, 1, m->qos, m->retain, client->socket, client->clientID);
rc = MQTTPacket_send_publish(&publish, 1, m->qos, m->retain, &client->net, client->clientID);
if (rc == SOCKET_ERROR)
{
client->good = 0;
Log(TRACE_MIN, 8, NULL, client->clientID, client->socket,
Socket_getpeer(client->socket));
Log(TRACE_MIN, 8, NULL, client->clientID, client->net.socket,
Socket_getpeer(client->net.socket));
MQTTProtocol_closeSession(client, 1);
client = NULL;
}
@ -556,12 +557,12 @@ void MQTTProtocol_retries(time_t now, Clients* client)
}
else if (m->qos && m->nextMessageType == PUBCOMP)
{
Log(TRACE_MIN, 7, NULL, "PUBREL", client->clientID, client->socket, m->msgid);
if (MQTTPacket_send_pubrel(m->msgid, 1, client->socket, client->clientID) != TCPSOCKET_COMPLETE)
Log(TRACE_MIN, 7, NULL, "PUBREL", client->clientID, client->net.socket, m->msgid);
if (MQTTPacket_send_pubrel(m->msgid, 1, &client->net, client->clientID) != TCPSOCKET_COMPLETE)
{
client->good = 0;
Log(TRACE_MIN, 8, NULL, client->clientID, client->socket,
Socket_getpeer(client->socket));
Log(TRACE_MIN, 8, NULL, client->clientID, client->net.socket,
Socket_getpeer(client->net.socket));
MQTTProtocol_closeSession(client, 1);
client = NULL;
}
@ -599,7 +600,7 @@ void MQTTProtocol_retry(time_t now, int doRetry)
MQTTProtocol_closeSession(client, 1);
continue;
}
if (Socket_noPendingWrites(client->socket) == 0)
if (Socket_noPendingWrites(client->net.socket) == 0)
continue;
if (doRetry)
MQTTProtocol_retries(now, client);
@ -620,13 +621,16 @@ void MQTTProtocol_freeClient(Clients* client)
MQTTProtocol_freeMessageList(client->inboundMsgs);
ListFree(client->messageQueue);
free(client->clientID);
/*if (client->will != NULL)
if (client->will)
{
willMessages* w = client->will;
free(w->msg);
free(w->topic);
free(client->will->msg);
free(client->will->topic);
free(client->will);
}*/
}
#if defined(OPENSSL)
if (client->sslopts)
free(client->sslopts);
#endif
/* don't free the client structure itself... this is done elsewhere */
FUNC_EXIT;
}

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -8,6 +8,7 @@
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs, Allan Stockdill-Mander - SSL updates
*******************************************************************************/
#if !defined(MQTTPROTOCOLCLIENT_H)
@ -28,6 +29,7 @@ Messages* MQTTProtocol_createMessage(Publish* publish, Messages** mm, int qos, i
Publications* MQTTProtocol_storePublication(Publish* publish, int* len);
int messageIDCompare(void* a, void* b);
int MQTTProtocol_assignMsgId(Clients* client);
void MQTTProtocol_removePublication(Publications* p);
int MQTTProtocol_handlePublishes(void* pack, int sock);
int MQTTProtocol_handlePubacks(void* pack, int sock);

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
* Copyright (c) 2009, 201 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -8,6 +8,7 @@
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs, Allan Stockdill-Mander - SSL updates
*******************************************************************************/
/**
@ -80,7 +81,11 @@ char* MQTTProtocol_addressPort(char* ip_address, int* port)
* @param password MQTT 3.1 password, or NULL
* @return the new client structure
*/
#if defined(OPENSSL)
int MQTTProtocol_connect(char* ip_address, Clients* aClient, int ssl)
#else
int MQTTProtocol_connect(char* ip_address, Clients* aClient)
#endif
{
int rc, port;
char* addr;
@ -90,15 +95,33 @@ int MQTTProtocol_connect(char* ip_address, Clients* aClient)
time(&(aClient->lastContact));
addr = MQTTProtocol_addressPort(ip_address, &port);
rc = Socket_new(addr, port, &(aClient->socket));
rc = Socket_new(addr, port, &(aClient->net.socket));
if (rc == EINPROGRESS || rc == EWOULDBLOCK)
aClient->connect_state = 1; /* TCP connect called */
aClient->connect_state = 1; /* TCP connect called - wait for connect completion */
else if (rc == 0)
{
if ((rc = MQTTPacket_send_connect(aClient)) == 0)
aClient->connect_state = 2; /* TCP connect completed, in which case send the MQTT connect packet */
else
aClient->connect_state = 0;
{ /* TCP connect completed. If SSL, send SSL connect */
#if defined(OPENSSL)
if (ssl)
{
if((aClient->net.ssl = SSLSocket_setSocketForSSL(aClient->net.socket, aClient->sslopts)) != NULL)
{
rc = SSLSocket_connect(aClient->net.ssl, aClient->net.socket);
if (rc == -1)
aClient->connect_state = 2; /* SSL connect called - wait for completion */
}
else
rc = SOCKET_ERROR;
}
#endif
if (rc == 0)
{
/* Now send the MQTT connect packet */
if ((rc = MQTTPacket_send_connect(aClient)) == 0)
aClient->connect_state = 3; /* MQTT Connect sent - wait for CONNACK */
else
aClient->connect_state = 0;
}
}
FUNC_EXIT_RC(rc);
@ -139,7 +162,7 @@ int MQTTProtocol_subscribe(Clients* client, List* topics, List* qoss)
FUNC_ENTRY;
/* we should stack this up for retry processing too */
rc = MQTTPacket_send_subscribe(topics, qoss, MQTTProtocol_assignMsgId(client), 0, client->socket, client->clientID);
rc = MQTTPacket_send_subscribe(topics, qoss, MQTTProtocol_assignMsgId(client), 0, &client->net, client->clientID);
FUNC_EXIT_RC(rc);
return rc;
}
@ -178,7 +201,7 @@ int MQTTProtocol_unsubscribe(Clients* client, List* topics)
FUNC_ENTRY;
/* we should stack this up for retry processing too? */
rc = MQTTPacket_send_unsubscribe(topics, MQTTProtocol_assignMsgId(client), 0, client->socket, client->clientID);
rc = MQTTPacket_send_unsubscribe(topics, MQTTProtocol_assignMsgId(client), 0, &client->net, client->clientID);
FUNC_EXIT_RC(rc);
return rc;
}

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -8,6 +8,7 @@
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs, Allan Stockdill-Mander - SSL updates
*******************************************************************************/
#if !defined(MQTTPROTOCOLOUT_H)
@ -24,7 +25,11 @@
#define DEFAULT_PORT 1883
void MQTTProtocol_reconnect(char* ip_address, Clients* client);
#if defined(OPENSSL)
int MQTTProtocol_connect(char* ip_address, Clients* acClients, int ssl);
#else
int MQTTProtocol_connect(char* ip_address, Clients* acClients);
#endif
int MQTTProtocol_handlePingresps(void* pack, int sock);
int MQTTProtocol_subscribe(Clients* client, List* topics, List* qoss);
int MQTTProtocol_handleSubacks(void* pack, int sock);

203
src/MQTTVersion.c Normal file
View File

@ -0,0 +1,203 @@
/*******************************************************************************
* Copyright (c) 2012 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
*******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <memory.h>
#include <ctype.h>
#include "MQTTAsync.h"
#if defined(WIN32)
#include <windows.h>
#include <tchar.h>
#include <io.h>
#include <sys/stat.h>
#else
#include <dlfcn.h>
#include <sys/mman.h>
#include <unistd.h>
#endif
/**
*
* @file
* \brief MQTTVersion - display the version and build information strings for a library.
*
* With no arguments, we try to load and call the version string for the libraries we
* know about: mqttv3c, mqttv3cs, mqttv3a, mqttv3as.
* With an argument:
* 1) we try to load the named library, call getVersionInfo and display those values.
* 2) If that doesn't work, we look through the binary for eyecatchers, and display those.
* This will work if the library is not executable in the current environment.
*
* */
char* libraries[] = {"mqttv3c", "mqttv3cs", "mqttv3a", "mqttv3as"};
char* eyecatchers[] = {"MQTTAsyncV3_Version", "MQTTAsyncV3_Timestamp",
"MQTTClientV3_Version", "MQTTClientV3_Timestamp"};
/**
* Finds an eyecatcher in a binary file and returns the following value.
* @param filename the name of the file
* @param eyecatcher_input the eyecatcher string to look for
* @return the value found - "" if not found
*/
char* FindString(char* filename, char* eyecatcher_input)
{
FILE* infile = NULL;
static char value[100];
char* eyecatcher = eyecatcher_input;
memset(value, 0, 100);
if ((infile = fopen(filename, "rb")) != NULL)
{
int buflen = strlen(eyecatcher);
char* buffer = (char*) malloc(buflen);
int count = 0;
int c = fgetc(infile);
while (feof(infile) == 0)
{
buffer[count++] = c;
if (memcmp(eyecatcher, buffer, buflen) == 0)
{
char* ptr = value;
c = fgetc(infile); /* skip space */
c = fgetc(infile);
while (isprint(c))
{
*ptr++ = c;
c = fgetc(infile);
}
break;
}
if (count == buflen)
{
memmove(buffer, &buffer[1], buflen - 1);
count--;
}
c = fgetc(infile);
}
free(buffer);
}
return value;
}
int printVersionInfo(MQTTAsync_nameValue* info)
{
int rc = 0;
while (info->name)
{
printf("%s: %s\n", info->name, info->value);
info++;
rc = 1; /* at least one value printed */
}
return rc;
}
typedef MQTTAsync_nameValue* (*func_type)(void);
int loadandcall(char* libname)
{
int rc = 0;
MQTTAsync_nameValue* (*func_address)(void) = NULL;
#if defined(WIN32)
HMODULE APILibrary = LoadLibrary(libname);
if (APILibrary == NULL)
printf("Error loading library %s, error code %d\n", libname, GetLastError());
else
{
func_address = (func_type)GetProcAddress(APILibrary, "MQTTAsync_getVersionInfo");
if (func_address == NULL)
func_address = (func_type)GetProcAddress(APILibrary, "MQTTClient_getVersionInfo");
if (func_address)
rc = printVersionInfo((*func_address)());
FreeLibrary(APILibrary);
}
#else
void* APILibrary = dlopen(libname, RTLD_LAZY); /* Open the Library in question */
char* ErrorOutput = dlerror(); /* Check it opened properly */
if (ErrorOutput != NULL)
printf("Error loading library %s, error %s\n", libname, ErrorOutput);
else
{
*(void **) (&func_address) = dlsym(APILibrary, "MQTTAsync_getVersionInfo");
if (func_address == NULL)
func_address = dlsym(APILibrary, "MQTTClient_getVersionInfo");
if (func_address)
rc = printVersionInfo((*func_address)());
dlclose(APILibrary);
}
#endif
return rc;
}
#if !defined(ARRAY_SIZE)
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#endif
void printEyecatchers(char* filename)
{
int i = 0;
for (i = 0; i < ARRAY_SIZE(eyecatchers); ++i)
{
char* value = FindString(filename, eyecatchers[i]);
if (value[0])
printf("%s: %s\n", eyecatchers[i], value);
}
}
int main(int argc, char** argv)
{
printf("MQTTVersion: print the version strings of an MQTT client library\n");
printf("Copyright (c) 2012 IBM Corp.\n");
if (argc == 1)
{
int i = 0;
char namebuf[60];
printf("Specify a particular library name if it is not in the current directory, or not executable on this platform\n");
for (i = 0; i < ARRAY_SIZE(libraries); ++i)
{
#if defined(WIN32)
sprintf(namebuf, "%s.dll", libraries[i]);
#else
sprintf(namebuf, "lib%s.so", libraries[i]);
#endif
printf("--- Trying library %s ---\n", libraries[i]);
if (!loadandcall(namebuf))
printEyecatchers(namebuf);
}
}
else
{
if (!loadandcall(argv[1]))
printEyecatchers(argv[1]);
}
return 0;
}

682
src/SSLSocket.c Normal file
View File

@ -0,0 +1,682 @@
/*******************************************************************************
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Ian Craggs, Allan Stockdill-Mander - initial implementation
*******************************************************************************/
/**
* @file
* \brief SSL related functions
*
*/
#if defined(OPENSSL)
#include "SocketBuffer.h"
#include "MQTTClient.h"
#include "SSLSocket.h"
#include "Log.h"
#include "StackTrace.h"
#include "Socket.h"
#include "Heap.h"
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/crypto.h>
extern Sockets s;
void SSLSocket_addPendingRead(int sock);
static ssl_mutex_type* sslLocks = NULL;
static ssl_mutex_type sslCoreMutex;
#if defined(WIN32)
#define iov_len len
#define iov_base buf
#endif
/**
* Gets the specific error corresponding to SOCKET_ERROR
* @param aString the function that was being used when the error occurred
* @param sock the socket on which the error occurred
* @return the specific TCP error code
*/
int SSLSocket_error(char* aString, SSL* ssl, int sock, int rc)
{
int error;
FUNC_ENTRY;
if (ssl)
error = SSL_get_error(ssl, rc);
else
error = ERR_get_error();
if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE)
{
Log(TRACE_MIN, -1, "SSLSocket error WANT_READ/WANT_WRITE");
}
else
{
static char buf[120];
if (strcmp(aString, "shutdown") != 0)
Log(TRACE_MIN, -1, "SSLSocket error %s(%d) in %s for socket %d rc %d errno %d %s\n", buf, error, aString, sock, rc, errno, strerror(errno));
//ERR_print_errors_fp(stderr);
if (error == SSL_ERROR_SSL || error == SSL_ERROR_SYSCALL)
error = SSL_FATAL;
}
FUNC_EXIT_RC(error);
return error;
}
static struct
{
int code;
char* string;
}
X509_message_table[] =
{
{ X509_V_OK, "X509_V_OK" },
{ X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT, "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT" },
{ X509_V_ERR_UNABLE_TO_GET_CRL, "X509_V_ERR_UNABLE_TO_GET_CRL" },
{ X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE, "X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE" },
{ X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE, "X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE" },
{ X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY, "X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY" },
{ X509_V_ERR_CERT_SIGNATURE_FAILURE, "X509_V_ERR_CERT_SIGNATURE_FAILURE" },
{ X509_V_ERR_CRL_SIGNATURE_FAILURE, "X509_V_ERR_CRL_SIGNATURE_FAILURE" },
{ X509_V_ERR_CERT_NOT_YET_VALID, "X509_V_ERR_CERT_NOT_YET_VALID" },
{ X509_V_ERR_CERT_HAS_EXPIRED, "X509_V_ERR_CERT_HAS_EXPIRED" },
{ X509_V_ERR_CRL_NOT_YET_VALID, "X509_V_ERR_CRL_NOT_YET_VALID" },
{ X509_V_ERR_CRL_HAS_EXPIRED, "X509_V_ERR_CRL_HAS_EXPIRED" },
{ X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD, "X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD" },
{ X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD, "X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD" },
{ X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD, "X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD" },
{ X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD, "X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD" },
{ X509_V_ERR_OUT_OF_MEM, "X509_V_ERR_OUT_OF_MEM" },
{ X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT, "X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT" },
{ X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN, "X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN" },
{ X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY" },
{ X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE, "X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE" },
{ X509_V_ERR_CERT_CHAIN_TOO_LONG, "X509_V_ERR_CERT_CHAIN_TOO_LONG" },
{ X509_V_ERR_CERT_REVOKED, "X509_V_ERR_CERT_REVOKED" },
{ X509_V_ERR_INVALID_CA, "X509_V_ERR_INVALID_CA" },
{ X509_V_ERR_PATH_LENGTH_EXCEEDED, "X509_V_ERR_PATH_LENGTH_EXCEEDED" },
{ X509_V_ERR_INVALID_PURPOSE, "X509_V_ERR_INVALID_PURPOSE" },
{ X509_V_ERR_CERT_UNTRUSTED, "X509_V_ERR_CERT_UNTRUSTED" },
{ X509_V_ERR_CERT_REJECTED, "X509_V_ERR_CERT_REJECTED" },
{ X509_V_ERR_SUBJECT_ISSUER_MISMATCH, "X509_V_ERR_SUBJECT_ISSUER_MISMATCH" },
{ X509_V_ERR_AKID_SKID_MISMATCH, "X509_V_ERR_AKID_SKID_MISMATCH" },
{ X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH, "X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH" },
{ X509_V_ERR_KEYUSAGE_NO_CERTSIGN, "X509_V_ERR_KEYUSAGE_NO_CERTSIGN" },
{ X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER, "X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER" },
{ X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION, "X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION" },
{ X509_V_ERR_KEYUSAGE_NO_CRL_SIGN, "X509_V_ERR_KEYUSAGE_NO_CRL_SIGN" },
{ X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION, "X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION" },
{ X509_V_ERR_INVALID_NON_CA, "X509_V_ERR_INVALID_NON_CA" },
{ X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED, "X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED" },
{ X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE, "X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE" },
{ X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED, "X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED" },
{ X509_V_ERR_INVALID_EXTENSION, "X509_V_ERR_INVALID_EXTENSION" },
{ X509_V_ERR_INVALID_POLICY_EXTENSION, "X509_V_ERR_INVALID_POLICY_EXTENSION" },
{ X509_V_ERR_NO_EXPLICIT_POLICY, "X509_V_ERR_NO_EXPLICIT_POLICY" },
{ X509_V_ERR_DIFFERENT_CRL_SCOPE, "X509_V_ERR_DIFFERENT_CRL_SCOPE" },
{ X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE, "X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE" },
{ X509_V_ERR_UNNESTED_RESOURCE, "X509_V_ERR_UNNESTED_RESOURCE" },
{ X509_V_ERR_PERMITTED_VIOLATION, "X509_V_ERR_PERMITTED_VIOLATION" },
{ X509_V_ERR_EXCLUDED_VIOLATION, "X509_V_ERR_EXCLUDED_VIOLATION" },
{ X509_V_ERR_SUBTREE_MINMAX, "X509_V_ERR_SUBTREE_MINMAX" },
{ X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE, "X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE" },
{ X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX, "X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX" },
{ X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, "X509_V_ERR_UNSUPPORTED_NAME_SYNTAX" },
};
#if !defined(ARRAY_SIZE)
/**
* Macro to calculate the number of entries in an array
*/
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#endif
char* SSL_get_verify_result_string(int rc)
{
int i;
char* retstring = "undef";
for (i = 0; i < ARRAY_SIZE(X509_message_table); ++i)
{
if (X509_message_table[i].code == rc)
{
retstring = X509_message_table[i].string;
break;
}
}
return retstring;
}
void SSL_CTX_info_callback(const SSL* ssl, int where, int ret)
{
FUNC_ENTRY;
if (where & SSL_CB_LOOP)
{
Log(TRACE_PROTOCOL, 1, "SSL state %s:%s:%s",
(where & SSL_ST_CONNECT) ? "connect" : (where & SSL_ST_ACCEPT) ? "accept" : "undef",
SSL_state_string_long(ssl), SSL_get_cipher_name(ssl));
}
else if (where & SSL_CB_EXIT)
{
Log(TRACE_PROTOCOL, 1, "SSL %s:%s",
(where & SSL_ST_CONNECT) ? "connect" : (where & SSL_ST_ACCEPT) ? "accept" : "undef",
SSL_state_string_long(ssl));
}
else if (where & SSL_CB_ALERT)
{
Log(TRACE_PROTOCOL, 1, "SSL alert %s:%s:%s",
(where & SSL_CB_READ) ? "read" : "write",
SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret));
}
else if (where & SSL_CB_HANDSHAKE_START)
{
Log(TRACE_PROTOCOL, 1, "SSL handshake started %s:%s:%s",
(where & SSL_CB_READ) ? "read" : "write",
SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret));
}
else if (where & SSL_CB_HANDSHAKE_DONE)
{
Log(TRACE_PROTOCOL, 1, "SSL handshake done %s:%s:%s",
(where & SSL_CB_READ) ? "read" : "write",
SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret));
Log(TRACE_PROTOCOL, 1, "SSL certificate verification: %s",
SSL_get_verify_result_string(SSL_get_verify_result(ssl)));
}
else
{
Log(TRACE_PROTOCOL, 1, "SSL state %s:%s:%s", SSL_state_string_long(ssl),
SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret));
}
FUNC_EXIT;
}
int pem_passwd_cb(char* buf, int size, int rwflag, void* userdata)
{
FUNC_ENTRY;
if (!rwflag)
{
strncpy(buf, (char*)(userdata), size);
buf[size-1] = '\0';
}
FUNC_EXIT;
if (!rwflag) return strlen(buf);
else return 0;
}
int SSL_create_mutex(ssl_mutex_type* mutex)
{
int rc = 0;
FUNC_ENTRY;
#if defined(WIN32)
mutex = CreateMutex(NULL, 0, NULL);
#else
rc = pthread_mutex_init(mutex, NULL);
#endif
FUNC_EXIT_RC(rc);
return rc;
}
int SSL_lock_mutex(ssl_mutex_type* mutex)
{
int rc = -1;
/* don't add entry/exit trace points as the stack log uses mutexes - recursion beckons */
#if defined(WIN32)
if (WaitForSingleObject(mutex, INFINITE) != WAIT_FAILED)
#else
if ((rc = pthread_mutex_lock(mutex)) == 0)
#endif
rc = 0;
return rc;
}
int SSL_unlock_mutex(ssl_mutex_type* mutex)
{
int rc = -1;
/* don't add entry/exit trace points as the stack log uses mutexes - recursion beckons */
#if defined(WIN32)
if (ReleaseMutex(mutex) != 0)
#else
if ((rc = pthread_mutex_unlock(mutex)) == 0)
#endif
rc = 0;
return rc;
}
void SSL_destroy_mutex(ssl_mutex_type* mutex)
{
int rc = 0;
FUNC_ENTRY;
#if defined(WIN32)
rc = CloseHandle(mutex);
#else
rc = pthread_mutex_destroy(mutex);
free(mutex);
#endif
FUNC_EXIT_RC(rc);
}
extern void SSLThread_id(CRYPTO_THREADID *id)
{
#if defined(WIN32)
CRYPTO_THREADID_set_numeric(id, (unsigned long)GetCurrentThreadId());
#else
CRYPTO_THREADID_set_numeric(id, (unsigned long)pthread_self());
#endif
}
extern void SSLLocks_callback(int mode, int n, const char *file, int line)
{
if (mode & CRYPTO_LOCK)
SSL_lock_mutex(&sslLocks[n]);
else
SSL_unlock_mutex(&sslLocks[n]);
}
int SSLSocket_initialize()
{
int rc = 0;
/*int prc;*/
int i;
FUNC_ENTRY;
if ((rc = SSL_library_init()) != 1)
rc = -1;
ERR_load_crypto_strings();
SSL_load_error_strings();
/* OpenSSL 0.9.8o and 1.0.0a and later added SHA2 algorithms to SSL_library_init().
Applications which need to use SHA2 in earlier versions of OpenSSL should call
OpenSSL_add_all_algorithms() as well. */
OpenSSL_add_all_algorithms();
sslLocks = calloc(CRYPTO_num_locks(), sizeof(ssl_mutex_type));
if (!sslLocks)
{
rc = -1;
goto exit;
}
for (i = 0; i < CRYPTO_num_locks(); i++)
{
/* prc = */SSL_create_mutex(&sslLocks[i]);
}
CRYPTO_THREADID_set_callback(SSLThread_id);
CRYPTO_set_locking_callback(SSLLocks_callback);
SSL_create_mutex(&sslCoreMutex);
exit:
FUNC_EXIT_RC(rc);
return rc;
}
SSL_CTX* SSLSocket_createContext(int socket, MQTTClient_SSLOptions* opts)
{
int rc = 1;
SSL_CTX* ctx = NULL;
char* ciphers = NULL;
FUNC_ENTRY;
if ((ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) /* SSLv23 for compatibility with SSLv2, SSLv3 and TLSv1 */
{
SSLSocket_error("SSL_CTX_new", NULL, socket, rc);
goto exit;
}
if (opts->keyStore)
{
if ((rc = SSL_CTX_use_certificate_chain_file(ctx, opts->keyStore)) != 1)
{
SSLSocket_error("SSL_CTX_use_certificate_chain_file", NULL, socket, rc);
goto free_ctx; /*If we can't load the certificate (chain) file then loading the privatekey won't work either as it needs a matching cert already loaded */
}
if (opts->privateKey == NULL)
opts->privateKey = opts->keyStore; /* the privateKey can be included in the keyStore */
if (opts->privateKeyPassword != NULL)
{
SSL_CTX_set_default_passwd_cb(ctx, pem_passwd_cb);
SSL_CTX_set_default_passwd_cb_userdata(ctx, (void*)opts->privateKeyPassword);
}
/* support for ASN.1 == DER format? DER can contain only one certificate? */
if ((rc = SSL_CTX_use_PrivateKey_file(ctx, opts->privateKey, SSL_FILETYPE_PEM)) != 1)
{
SSLSocket_error("SSL_CTX_use_PrivateKey_file", NULL, socket, rc);
goto free_ctx;
}
}
if (opts->trustStore)
{
if ((rc = SSL_CTX_load_verify_locations(ctx, opts->trustStore, NULL)) != 1)
{
SSLSocket_error("SSL_CTX_load_verify_locations", NULL, socket, rc);
goto free_ctx;
}
}
else if ((rc = SSL_CTX_set_default_verify_paths(ctx)) != 1)
{
SSLSocket_error("SSL_CTX_set_default_verify_paths", NULL, socket, rc);
goto free_ctx;
}
if (opts->enabledCipherSuites == NULL)
ciphers = "DEFAULT";
else
ciphers = opts->enabledCipherSuites;
if ((rc = SSL_CTX_set_cipher_list(ctx, ciphers)) != 1)
{
SSLSocket_error("SSL_CTX_set_cipher_list", NULL, socket, rc);
goto free_ctx;
}
SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
goto exit;
free_ctx:
SSL_CTX_free(ctx);
ctx = NULL;
exit:
FUNC_EXIT;
return ctx;
}
SSL* SSLSocket_setSocketForSSL(int socket, MQTTClient_SSLOptions* opts)
{
int rc = 1;
SSL_CTX* ctx = NULL;
SSL* ssl = NULL;
FUNC_ENTRY;
if ((ctx = SSLSocket_createContext(socket, opts)) != NULL)
{
int i;
SSL_CTX_set_info_callback(ctx, SSL_CTX_info_callback);
if (opts->enableServerCertAuth)
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
ssl = SSL_new(ctx);
/* Log all ciphers available to the SSL sessions (loaded in ctx) */
for (i = 0; ;i++)
{
const char* cipher = SSL_get_cipher_list(ssl, i);
if (cipher == NULL) break;
Log(TRACE_MIN, 1, "SSL cipher available: %d:%s", i, cipher);
}
if ((rc = SSL_set_fd(ssl, socket)) != 1)
SSLSocket_error("SSL_set_fd", ssl, socket, rc);
}
FUNC_EXIT_RC(rc);
return ssl;
}
int SSLSocket_connect(SSL* ssl, int sock)
{
int rc = 0;
FUNC_ENTRY;
rc = SSL_connect(ssl);
if (rc != 1)
{
int error;
error = SSLSocket_error("SSL_connect", ssl, sock, rc);
if (error == SSL_FATAL)
rc = error;
}
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Reads one byte from a socket
* @param socket the socket to read from
* @param c the character read, returned
* @return completion code
*/
int SSLSocket_getch(SSL* ssl, int socket, char* c)
{
int rc = SOCKET_ERROR;
FUNC_ENTRY;
if ((rc = SocketBuffer_getQueuedChar(socket, c)) != SOCKETBUFFER_INTERRUPTED)
goto exit;
if ((rc = SSL_read(ssl, c, (size_t)1)) < 0)
{
int err = SSLSocket_error("SSL_read - getch", ssl, socket, rc);
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)
{
rc = TCPSOCKET_INTERRUPTED;
SocketBuffer_interrupted(socket, 0);
}
}
else if (rc == 0)
rc = SOCKET_ERROR; /* The return value from recv is 0 when the peer has performed an orderly shutdown. */
else if (rc == 1)
{
SocketBuffer_queueChar(socket, *c);
rc = TCPSOCKET_COMPLETE;
}
exit:
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Attempts to read a number of bytes from a socket, non-blocking. If a previous read did not
* finish, then retrieve that data.
* @param socket the socket to read from
* @param bytes the number of bytes to read
* @param actual_len the actual number of bytes read
* @return completion code
*/
char *SSLSocket_getdata(SSL* ssl, int socket, int bytes, int* actual_len)
{
int rc;
char* buf;
FUNC_ENTRY;
if (bytes == 0)
{
buf = SocketBuffer_complete(socket);
goto exit;
}
buf = SocketBuffer_getQueuedData(socket, bytes, actual_len);
if ((rc = SSL_read(ssl, buf + (*actual_len), (size_t)(bytes - (*actual_len)))) < 0)
{
rc = SSLSocket_error("SSL_read - getdata", ssl, socket, rc);
if (rc != SSL_ERROR_WANT_READ && rc != SSL_ERROR_WANT_WRITE)
{
buf = NULL;
goto exit;
}
}
else if (rc == 0) /* rc 0 means the other end closed the socket */
{
buf = NULL;
goto exit;
}
else
*actual_len += rc;
if (*actual_len == bytes)
{
SocketBuffer_complete(socket);
/* if we read the whole packet, there might still be data waiting in the SSL buffer, which
isn't picked up by select. So here we should check for any data remaining in the SSL buffer, and
if so, add this socket to a new "pending SSL reads" list.
*/
if (SSL_pending(ssl) > 0) /* return no of bytes pending */
SSLSocket_addPendingRead(socket);
}
else /* we didn't read the whole packet */
{
SocketBuffer_interrupted(socket, *actual_len);
Log(TRACE_MAX, -1, "SSL_read: %d bytes expected but %d bytes now received", bytes, *actual_len);
}
exit:
FUNC_EXIT;
return buf;
}
int SSLSocket_close(SSL* ssl)
{
return SSL_shutdown(ssl);
}
/* No SSL_writev() provided by OpenSSL. Boo. */
int SSLSocket_putdatas(SSL* ssl, int socket, char* buf0, int buf0len, int count, char** buffers, int* buflens)
{
int rc = 0;
int i;
char *ptr;
iobuf iovec;
int sslerror;
FUNC_ENTRY;
iovec.iov_len = buf0len;
for (i = 0; i < count; i++)
iovec.iov_len += buflens[i];
ptr = iovec.iov_base = (char *)malloc(iovec.iov_len);
memcpy(ptr, buf0, buf0len);
ptr += buf0len;
for (i = 0; i < count; i++)
{
memcpy(ptr, buffers[i], buflens[i]);
ptr += buflens[i];
}
SSL_lock_mutex(&sslCoreMutex);
if ((rc = SSL_write(ssl, iovec.iov_base, iovec.iov_len)) == iovec.iov_len)
rc = TCPSOCKET_COMPLETE;
else
{
sslerror = SSLSocket_error("SSL_write", ssl, socket, rc);
if (sslerror == SSL_ERROR_WANT_WRITE)
{
int* sockmem = (int*)malloc(sizeof(int));
Log(TRACE_MIN, -1, "Partial write: incomplete write of %d bytes on SSL socket %d",
iovec.iov_len, socket);
SocketBuffer_pendingWrite(socket, ssl, 1, &iovec, iovec.iov_len, 0);
*sockmem = socket;
ListAppend(s.write_pending, sockmem, sizeof(int));
FD_SET(socket, &(s.pending_wset));
rc = TCPSOCKET_INTERRUPTED;
iovec.iov_base = NULL; /* don't free it because it hasn't been completely written yet */
}
else
rc = SOCKET_ERROR;
}
SSL_unlock_mutex(&sslCoreMutex);
if (iovec.iov_base)
free(iovec.iov_base);
FUNC_EXIT_RC(rc);
return rc;
}
static List pending_reads = {NULL, NULL, NULL, 0, 0};
void SSLSocket_addPendingRead(int sock)
{
FUNC_ENTRY;
if (ListFindItem(&pending_reads, &sock, intcompare) == NULL) /* make sure we don't add the same socket twice */
{
int* psock = (int*)malloc(sizeof(sock));
*psock = sock;
ListAppend(&pending_reads, psock, sizeof(sock));
}
else
Log(TRACE_MIN, -1, "SSLSocket_addPendingRead: socket %d already in the list", sock);
FUNC_EXIT;
}
int SSLSocket_getPendingRead()
{
int sock = -1;
if (pending_reads.count > 0)
{
sock = *(int*)(pending_reads.first->content);
ListRemoveHead(&pending_reads);
}
return sock;
}
int SSLSocket_continueWrite(pending_writes* pw)
{
int rc = 0;
FUNC_ENTRY;
if ((rc = SSL_write(pw->ssl, pw->iovecs[0].iov_base, pw->iovecs[0].iov_len)) == pw->iovecs[0].iov_len)
{
/* topic and payload buffers are freed elsewhere, when all references to them have been removed */
free(pw->iovecs[0].iov_base);
if (pw->count > 1)
{
free(pw->iovecs[1].iov_base);
if (pw->count == 5)
free(pw->iovecs[3].iov_base);
}
Log(TRACE_MIN, -1, "SSL continueWrite: partial write now complete for socket %d", pw->socket);
rc = 1;
}
else
{
int sslerror = SSLSocket_error("SSL_write", pw->ssl, pw->socket, rc);
if (sslerror == SSL_ERROR_WANT_WRITE)
rc = 0; /* indicate we haven't finished writing the payload yet */
}
FUNC_EXIT_RC(rc);
return rc;
}
#endif

43
src/SSLSocket.h Normal file
View File

@ -0,0 +1,43 @@
/*******************************************************************************
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Ian Craggs, Allan Stockdill-Mander - initial implementation
*******************************************************************************/
#include "MQTTClient.h"
#if !defined(SSLSOCKET_H)
#define SSLSOCKET_H
#if defined(WIN32)
#define ssl_mutex_type HANDLE
#else
#include <pthread.h>
#include <semaphore.h>
#define ssl_mutex_type pthread_mutex_t
#endif
#include <openssl/ssl.h>
#include "SocketBuffer.h"
#define URI_SSL "ssl://"
int SSLSocket_initialize();
SSL* SSLSocket_setSocketForSSL(int socket, MQTTClient_SSLOptions* opts);
int SSLSocket_getch(SSL* ssl, int socket, char* c);
char *SSLSocket_getdata(SSL* ssl, int socket, int bytes, int* actual_len);
int SSLSocket_close(SSL* ssl);
int SSLSocket_putdatas(SSL* ssl, int socket, char* buf0, int buf0len, int count, char** buffers, int* buflens);
int SSLSocket_connect(SSL* ssl, int socket);
int SSLSocket_getPendingRead();
int SSLSocket_continueWrite(pending_writes* pw);
#endif

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,8 @@
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs - initial implementation and documentation
* Ian Craggs - async client updates
*******************************************************************************/
/**
@ -23,6 +24,9 @@
#include "SocketBuffer.h"
#include "Messages.h"
#include "StackTrace.h"
#if defined(OPENSSL)
#include "SSLSocket.h"
#endif
#include <stdlib.h>
#include <string.h>
@ -37,14 +41,12 @@ int Socket_continueWrites(fd_set* pwset);
#if defined(WIN32)
#define iov_len len
#define iov_base buf
#else
#include <sys/uio.h>
#endif
/**
* Structure to hold all socket data for the module
*/
static Sockets s;
Sockets s;
static fd_set wset;
/**
@ -92,7 +94,7 @@ int Socket_error(char* aString, int sock)
if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS && errno != EWOULDBLOCK)
{
if (strcmp(aString, "shutdown") != 0 || (errno != ENOTCONN && errno != ECONNRESET))
Log(TRACE_MIN, -1, "Socket error %d in %s for socket %d", errno, aString, sock);
Log(TRACE_MIN, -1, "Socket error %s in %s for socket %d", strerror(errno), aString, sock);
}
FUNC_EXIT_RC(errno);
return errno;
@ -461,7 +463,11 @@ int Socket_putdatas(int socket, char* buf0, int buf0len, int count, char** buffe
int* sockmem = (int*)malloc(sizeof(int));
Log(TRACE_MIN, -1, "Partial write: %ld bytes of %d actually written on socket %d",
bytes, total, socket);
#if defined(OPENSSL)
SocketBuffer_pendingWrite(socket, NULL, count+1, iovecs, total, bytes);
#else
SocketBuffer_pendingWrite(socket, count+1, iovecs, total, bytes);
#endif
*sockmem = socket;
ListAppend(s.write_pending, sockmem, sizeof(int));
FD_SET(socket, &(s.pending_wset));
@ -474,6 +480,29 @@ exit:
}
/**
* Add a socket to the pending write list, so that it is checked for writing in select. This is used
* in connect processing when the TCP connect is incomplete, as we need to check the socket for both
* ready to read and write states.
* @param socket the socket to add
*/
void Socket_addPendingWrite(int socket)
{
FD_SET(socket, &(s.pending_wset));
}
/**
* Clear a socket from the pending write list - if one was added with Socket_addPendingWrite
* @param socket the socket to remove
*/
void Socket_clearPendingWrite(int socket)
{
if (FD_ISSET(socket, &(s.pending_wset)))
FD_CLR(socket, &(s.pending_wset));
}
/**
* Close a socket without removing it from the select list.
* @param socket the socket to close
@ -490,7 +519,9 @@ int Socket_close_only(int socket)
if ((rc = closesocket(socket)) == SOCKET_ERROR)
Socket_error("close", socket);
#else
if (shutdown(socket, SHUT_RDWR) == SOCKET_ERROR)
if (shutdown(socket, SHUT_WR) == SOCKET_ERROR)
Socket_error("shutdown", socket);
if ((rc = recv(socket, NULL, (size_t)0, 0)) == SOCKET_ERROR)
Socket_error("shutdown", socket);
if ((rc = close(socket)) == SOCKET_ERROR)
Socket_error("close", socket);
@ -613,6 +644,13 @@ int Socket_new(char* addr, int port, int* sock)
rc = Socket_error("socket", *sock);
else
{
#if defined(NOSIGPIPE)
int opt = 1;
if (setsockopt(*sock, SOL_SOCKET, SO_NOSIGPIPE, (void*)&opt, sizeof(opt)) != 0)
Log(TRACE_MIN, -1, "Could not set SO_NOSIGPIPE for socket %d", *sock);
#endif
Log(TRACE_MIN, -1, "New socket %d for %s, port %d", *sock, addr, port);
if (Socket_addSocket(*sock) == SOCKET_ERROR)
rc = Socket_error("setnonblocking", *sock);
@ -642,6 +680,13 @@ int Socket_new(char* addr, int port, int* sock)
}
static Socket_writeComplete* writecomplete = NULL;
void Socket_setWriteCompleteCallback(Socket_writeComplete* mywritecomplete)
{
writecomplete = mywritecomplete;
}
/**
* Continue an outstanding write for a particular socket
* @param socket that socket
@ -658,6 +703,14 @@ int Socket_continueWrite(int socket)
FUNC_ENTRY;
pw = SocketBuffer_getWrite(socket);
#if defined(OPENSSL)
if (pw->ssl)
{
rc = SSLSocket_continueWrite(pw);
goto exit;
}
#endif
for (i = 0; i < pw->count; ++i)
{
@ -687,11 +740,14 @@ int Socket_continueWrite(int socket)
free(pw->iovecs[1].iov_base);
if (pw->count == 5)
free(pw->iovecs[3].iov_base);
Log(TRACE_MIN, -1, "ContinueWrite: partial write now complete for socket %d", socket);
Log(TRACE_MIN, -1, "ContinueWrite: partial write now complete for socket %d", socket);
}
else
Log(TRACE_MIN, -1, "ContinueWrite wrote +%lu bytes on socket %d", bytes, socket);
}
#if defined(OPENSSL)
exit:
#endif
FUNC_EXIT_RC(rc);
return rc;
}
@ -722,6 +778,9 @@ int Socket_continueWrites(fd_set* pwset)
ListNextElement(s.write_pending, &curpending);
}
curpending = s.write_pending->current;
if (writecomplete)
(*writecomplete)(socket);
}
else
ListNextElement(s.write_pending, &curpending);

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,8 @@
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs - initial implementation and documentation
* Ian Craggs - async client updates
*******************************************************************************/
#if !defined(SOCKET_H)
@ -50,7 +51,8 @@
#define SOCKET_ERROR -1
#endif
/** must be the same as SOCKETBUFFER_INTERRUPTED */
#define TCPSOCKET_INTERRUPTED -2
#define TCPSOCKET_INTERRUPTED -22
#define SSL_FATAL -3
#if !defined(INET6_ADDRSTRLEN)
#define INET6_ADDRSTRLEN 46 /** only needed for gcc/cygwin on windows */
@ -111,4 +113,10 @@ int Socket_new(char* addr, int port, int* socket);
int Socket_noPendingWrites(int socket);
char* Socket_getpeer(int sock);
void Socket_addPendingWrite(int socket);
void Socket_clearPendingWrite(int socket);
typedef void Socket_writeComplete(int socket);
void Socket_setWriteCompleteCallback(Socket_writeComplete*);
#endif /* SOCKET_H */

View File

@ -8,6 +8,7 @@
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs, Allan Stockdill-Mander - SSL updates
*******************************************************************************/
/**
@ -296,7 +297,11 @@ void SocketBuffer_queueChar(int socket, char c)
* @param total total data length to be written
* @param bytes actual data length that was written
*/
#if defined(OPENSSL)
void SocketBuffer_pendingWrite(int socket, SSL* ssl, int count, iobuf* iovecs, int total, int bytes)
#else
void SocketBuffer_pendingWrite(int socket, int count, iobuf* iovecs, int total, int bytes)
#endif
{
int i = 0;
pending_writes* pw = NULL;
@ -305,6 +310,9 @@ void SocketBuffer_pendingWrite(int socket, int count, iobuf* iovecs, int total,
/* store the buffers until the whole packet is written */
pw = malloc(sizeof(pending_writes));
pw->socket = socket;
#if defined(OPENSSL)
pw->ssl = ssl;
#endif
pw->bytes = bytes;
pw->total = total;
pw->count = count;

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -8,6 +8,7 @@
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs, Allan Stockdill-Mander - SSL updates
*******************************************************************************/
#if !defined(SOCKETBUFFER_H)
@ -19,6 +20,10 @@
#include <sys/socket.h>
#endif
#if defined(OPENSSL)
#include <openssl/ssl.h>
#endif
#if defined(WIN32)
typedef WSABUF iobuf;
#else
@ -38,6 +43,9 @@ typedef struct
typedef struct
{
int socket, total, count;
#if defined(OPENSSL)
SSL* ssl;
#endif
unsigned long bytes;
iobuf iovecs[5];
} pending_writes;
@ -46,7 +54,7 @@ typedef struct
#if !defined(SOCKET_ERROR)
#define SOCKET_ERROR -1
#endif
#define SOCKETBUFFER_INTERRUPTED -2 /* must be the same value as TCPSOCKET_INTERRUPTED */
#define SOCKETBUFFER_INTERRUPTED -22 /* must be the same value as TCPSOCKET_INTERRUPTED */
void SocketBuffer_initialize(void);
void SocketBuffer_terminate(void);
@ -57,7 +65,11 @@ void SocketBuffer_interrupted(int socket, int actual_len);
char* SocketBuffer_complete(int socket);
void SocketBuffer_queueChar(int socket, char c);
#if defined(OPENSSL)
void SocketBuffer_pendingWrite(int socket, SSL* ssl, int count, iobuf* iovecs, int total, int bytes);
#else
void SocketBuffer_pendingWrite(int socket, int count, iobuf* iovecs, int total, int bytes);
#endif
pending_writes* SocketBuffer_getWrite(int socket);
int SocketBuffer_writeComplete(int socket);
pending_writes* SocketBuffer_updateWrite(int socket, char* topic, char* payload);

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,8 @@
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs - initial implementation
* Ian Craggs, Allan Stockdill-Mander - async client updates
*******************************************************************************/
/**
@ -32,6 +33,10 @@
#include <errno.h>
#include <unistd.h>
#include <sys/time.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <limits.h>
#endif
#include <memory.h>
#include <stdlib.h>
@ -158,6 +163,20 @@ thread_id_type Thread_getid()
}
#if defined(USE_NAMED_SEMAPHORES)
#define MAX_NAMED_SEMAPHORES 10
static int named_semaphore_count = 0;
static struct
{
sem_type sem;
char name[NAME_MAX-4];
} named_semaphores[MAX_NAMED_SEMAPHORES];
#endif
/**
* Create a new semaphore
* @return the new condition variable
@ -175,9 +194,30 @@ sem_type Thread_create_sem()
FALSE, // initial state is nonsignaled
NULL // object name
);
#elif defined(USE_NAMED_SEMAPHORES)
if (named_semaphore_count == 0)
memset(named_semaphores, '\0', sizeof(named_semaphores));
char* name = &(strrchr(tempnam("/", "MQTT"), '/'))[1]; /* skip first slash of name */
if ((sem = sem_open(name, O_CREAT, S_IRWXU, 0)) == SEM_FAILED)
rc = -1;
else
{
int i;
named_semaphore_count++;
for (i = 0; i < MAX_NAMED_SEMAPHORES; ++i)
{
if (named_semaphores[i].name[0] == '\0')
{
named_semaphores[i].sem = sem;
strcpy(named_semaphores[i].name, name);
break;
}
}
}
#else
sem = malloc(sizeof(sem_t));
rc = sem_init(sem, 0, 0);
sem = malloc(sizeof(sem_t));
rc = sem_init(sem, 0, 0);
#endif
FUNC_EXIT_RC(rc);
return sem;
@ -185,24 +225,26 @@ sem_type Thread_create_sem()
/**
* Lock a mutex which has already been created, block until ready
* @param mutex the mutex
* Wait for a semaphore to be posted, or timeout.
* @param sem the semaphore
* @param timeout the maximum time to wait, in seconds
* @return completion code
*/
int Thread_wait_sem(sem_type sem)
int Thread_wait_sem_timeout(sem_type sem, int timeout)
{
/* sem_timedwait is the obvious call to use, but seemed not to work on the Viper,
* so I've used trywait in a loop instead. Ian Craggs 23/7/2010
*/
int rc = -1;
int timeout = 10; /* seconds */
#if !defined(WIN32)
#define USE_TRYWAIT
#if defined(USE_TRYWAIT)
int i = 0;
int interval = 10000;
int count = (1000000 / interval) * timeout;
#elif !defined(WIN32)
#else
struct timespec ts;
#endif
#endif
FUNC_ENTRY;
@ -212,14 +254,17 @@ int Thread_wait_sem(sem_type sem)
while (++i < count && (rc = sem_trywait(sem)) != 0)
{
if (rc == -1 && ((rc = errno) != EAGAIN))
{
rc = 0;
break;
}
usleep(interval); /* microseconds - .1 of a second */
}
#else
if (clock_gettime(CLOCK_REALTIME, &ts) != -1)
{
ts.tv_sec += timeout;
rc = sem_timedwait(sem, &ts);
rc = sem_timedwait(sem, &ts);
}
#endif
@ -228,6 +273,22 @@ int Thread_wait_sem(sem_type sem)
}
/**
* Wait for a semaphore to be posted, or timeout after 10 seconds.
* @param sem the semaphore
* @return completion code
*/
int Thread_wait_sem(sem_type sem)
{
return Thread_wait_sem_timeout(sem, 10);
}
/**
* Check to see if a semaphore has been posted, without waiting.
* @param sem the semaphore
* @return 0 (false) or 1 (true)
*/
int Thread_check_sem(sem_type sem)
{
#if defined(WIN32)
@ -241,8 +302,8 @@ int Thread_check_sem(sem_type sem)
/**
* Lock a mutex which has already been created, block until ready
* @param mutex the mutex
* Post a semaphore
* @param sem the semaphore
* @return completion code
*/
int Thread_post_sem(sem_type sem)
@ -274,6 +335,19 @@ int Thread_destroy_sem(sem_type sem)
FUNC_ENTRY;
#if defined(WIN32)
rc = CloseHandle(sem);
#elif defined(USE_NAMED_SEMAPHORES)
int i;
rc = sem_close(sem);
for (i = 0; i < MAX_NAMED_SEMAPHORES; ++i)
{
if (named_semaphores[i].sem == sem)
{
rc = sem_unlink(named_semaphores[i].name);
named_semaphores[i].name[0] = '\0';
break;
}
}
named_semaphore_count--;
#else
rc = sem_destroy(sem);
free(sem);
@ -283,6 +357,80 @@ int Thread_destroy_sem(sem_type sem)
}
#if !defined(WIN32)
/**
* Create a new condition variable
* @return the condition variable struct
*/
cond_type Thread_create_cond()
{
cond_type condvar = NULL;
int rc = 0;
FUNC_ENTRY;
condvar = malloc(sizeof(cond_type_struct));
rc = pthread_cond_init(&condvar->cond, NULL);
rc = pthread_mutex_init(&condvar->mutex, NULL);
FUNC_EXIT_RC(rc);
return condvar;
}
/**
* Signal a condition variable
* @return completion code
*/
int Thread_signal_cond(cond_type condvar)
{
int rc = 0;
pthread_mutex_lock(&condvar->mutex);
rc = pthread_cond_signal(&condvar->cond);
pthread_mutex_unlock(&condvar->mutex);
return rc;
}
/**
* Wait with a timeout (seconds) for condition variable
* @return completion code
*/
int Thread_wait_cond_timeout(cond_type condvar, int timeout)
{
FUNC_ENTRY;
int rc = 0;
struct timespec cond_timeout;
struct timeval cur_time;
gettimeofday(&cur_time, NULL);
cond_timeout.tv_sec = cur_time.tv_sec + timeout;
cond_timeout.tv_nsec = cur_time.tv_usec * 1000;
pthread_mutex_lock(&condvar->mutex);
rc = pthread_cond_timedwait(&condvar->cond, &condvar->mutex, &cond_timeout);
pthread_mutex_unlock(&condvar->mutex);
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Destroy a condition variable
* @return completion code
*/
int Thread_destroy_cond(cond_type condvar)
{
int rc = 0;
rc = pthread_mutex_destroy(&condvar->mutex);
rc = pthread_cond_destroy(&condvar->cond);
free(condvar);
return rc;
}
#endif
#if defined(THREAD_UNIT_TESTS)

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
* Copyright (c) 2009, 2013 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,8 @@
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Ian Craggs - initial API and implementation and/or initial documentation
* Ian Craggs - initial implementation
* Ian Craggs, Allan Stockdill-Mander - async client updates
*******************************************************************************/
#if !defined(THREAD_H)
@ -33,6 +34,11 @@
typedef struct { pthread_cond_t cond; pthread_mutex_t mutex; } cond_type_struct;
typedef cond_type_struct *cond_type;
typedef sem_t *sem_type;
cond_type Thread_create_cond();
int Thread_signal_cond(cond_type);
int Thread_wait_cond_timeout(cond_type condvar, int timeout);
int Thread_destroy_cond(cond_type);
#endif
thread_type Thread_start(thread_fn, void*);
@ -46,6 +52,7 @@ thread_id_type Thread_getid();
sem_type Thread_create_sem();
int Thread_wait_sem(sem_type sem);
int Thread_wait_sem_timeout(sem_type sem, int timeout);
int Thread_check_sem(sem_type sem);
int Thread_post_sem(sem_type sem);
int Thread_destroy_sem(sem_type sem);

703
src/Tree.c Normal file
View File

@ -0,0 +1,703 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Ian Craggs - initial implementation and documentation
*******************************************************************************/
/** @file
* \brief functions which apply to tree structures.
*
* These trees can hold data of any sort, pointed to by the content pointer of the
* Node structure.
* */
#define NO_HEAP_TRACKING 1
#include "Tree.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <memory.h>
#include "Heap.h"
void TreeInitializeNoMalloc(Tree* aTree, int(*compare)(void*, void*, int))
{
memset(aTree, '\0', sizeof(Tree));
aTree->heap_tracking = 1;
aTree->index[0].compare = compare;
aTree->indexes = 1;
}
/**
* Allocates and initializes a new tree structure.
* @return a pointer to the new tree structure
*/
Tree* TreeInitialize(int(*compare)(void*, void*, int))
{
#if defined(UNIT_TESTS)
Tree* newt = malloc(sizeof(Tree));
#else
Tree* newt = mymalloc(__FILE__, __LINE__, sizeof(Tree));
#endif
TreeInitializeNoMalloc(newt, compare);
return newt;
}
void TreeAddIndex(Tree* aTree, int(*compare)(void*, void*, int))
{
aTree->index[aTree->indexes].compare = compare;
++(aTree->indexes);
}
void TreeFree(Tree* aTree)
{
#if defined(UNIT_TESTS)
free(aTree);
#else
(aTree->heap_tracking) ? myfree(__FILE__, __LINE__, aTree) : free(aTree);
#endif
}
#define LEFT 0
#define RIGHT 1
#if !defined(max)
#define max(a, b) (a > b) ? a : b;
#endif
int isRed(Node* aNode)
{
return (aNode != NULL) && (aNode->red);
}
int isBlack(Node* aNode)
{
return (aNode == NULL) || (aNode->red == 0);
}
int TreeWalk(Node* curnode, int depth)
{
if (curnode)
{
int left = TreeWalk(curnode->child[LEFT], depth+1);
int right = TreeWalk(curnode->child[RIGHT], depth+1);
depth = max(left, right);
if (curnode->red)
{
/*if (isRed(curnode->child[LEFT]) || isRed(curnode->child[RIGHT]))
{
printf("red/black tree violation %p\n", curnode->content);
exit(-99);
}*/;
}
}
return depth;
}
int TreeMaxDepth(Tree *aTree)
{
int rc = TreeWalk(aTree->index[0].root, 0);
/*if (aTree->root->red)
{
printf("root node should not be red %p\n", aTree->root->content);
exit(-99);
}*/
return rc;
}
void TreeRotate(Tree* aTree, Node* curnode, int direction, int index)
{
Node* other = curnode->child[!direction];
curnode->child[!direction] = other->child[direction];
if (other->child[direction] != NULL)
other->child[direction]->parent = curnode;
other->parent = curnode->parent;
if (curnode->parent == NULL)
aTree->index[index].root = other;
else if (curnode == curnode->parent->child[direction])
curnode->parent->child[direction] = other;
else
curnode->parent->child[!direction] = other;
other->child[direction] = curnode;
curnode->parent = other;
}
Node* TreeBAASub(Tree* aTree, Node* curnode, int which, int index)
{
Node* uncle = curnode->parent->parent->child[which];
if (isRed(uncle))
{
curnode->parent->red = uncle->red = 0;
curnode = curnode->parent->parent;
curnode->red = 1;
}
else
{
if (curnode == curnode->parent->child[which])
{
curnode = curnode->parent;
TreeRotate(aTree, curnode, !which, index);
}
curnode->parent->red = 0;
curnode->parent->parent->red = 1;
TreeRotate(aTree, curnode->parent->parent, which, index);
}
return curnode;
}
void TreeBalanceAfterAdd(Tree* aTree, Node* curnode, int index)
{
while (curnode && isRed(curnode->parent) && curnode->parent->parent)
{
if (curnode->parent == curnode->parent->parent->child[LEFT])
curnode = TreeBAASub(aTree, curnode, RIGHT, index);
else
curnode = TreeBAASub(aTree, curnode, LEFT, index);
}
aTree->index[index].root->red = 0;
}
/**
* Add an item to a tree
* @param aTree the list to which the item is to be added
* @param content the list item content itself
* @param size the size of the element
*/
void* TreeAddByIndex(Tree* aTree, void* content, int size, int index)
{
Node* curparent = NULL;
Node* curnode = aTree->index[index].root;
Node* newel = NULL;
int left = 0;
int result = 1;
void* rc = NULL;
while (curnode)
{
result = aTree->index[index].compare(curnode->content, content, 1);
left = (result > 0);
if (result == 0)
break;
else
{
curparent = curnode;
curnode = curnode->child[left];
}
}
if (result == 0)
{
if (aTree->allow_duplicates)
exit(-99);
{
newel = curnode;
rc = newel->content;
if (index == 0)
aTree->size += (size - curnode->size);
}
}
else
{
#if defined(UNIT_TESTS)
newel = malloc(sizeof(Node));
#else
newel = (aTree->heap_tracking) ? mymalloc(__FILE__, __LINE__, sizeof(Node)) : malloc(sizeof(Node));
#endif
memset(newel, '\0', sizeof(Node));
if (curparent)
curparent->child[left] = newel;
else
aTree->index[index].root = newel;
newel->parent = curparent;
newel->red = 1;
if (index == 0)
{
++(aTree->count);
aTree->size += size;
}
}
newel->content = content;
newel->size = size;
TreeBalanceAfterAdd(aTree, newel, index);
return rc;
}
void* TreeAdd(Tree* aTree, void* content, int size)
{
void* rc = NULL;
int i;
for (i = 0; i < aTree->indexes; ++i)
rc = TreeAddByIndex(aTree, content, size, i);
return rc;
}
Node* TreeFindIndex1(Tree* aTree, void* key, int index, int value)
{
int result = 0;
Node* curnode = aTree->index[index].root;
while (curnode)
{
result = aTree->index[index].compare(curnode->content, key, value);
if (result == 0)
break;
else
curnode = curnode->child[result > 0];
}
return curnode;
}
Node* TreeFindIndex(Tree* aTree, void* key, int index)
{
return TreeFindIndex1(aTree, key, index, 0);
}
Node* TreeFindContentIndex(Tree* aTree, void* key, int index)
{
return TreeFindIndex1(aTree, key, index, 1);
}
Node* TreeFind(Tree* aTree, void* key)
{
return TreeFindIndex(aTree, key, 0);
}
Node* TreeMinimum(Node* curnode)
{
if (curnode)
while (curnode->child[LEFT])
curnode = curnode->child[LEFT];
return curnode;
}
Node* TreeSuccessor(Node* curnode)
{
if (curnode->child[RIGHT])
curnode = TreeMinimum(curnode->child[RIGHT]);
else
{
Node* curparent = curnode->parent;
while (curparent && curnode == curparent->child[RIGHT])
{
curnode = curparent;
curparent = curparent->parent;
}
curnode = curparent;
}
return curnode;
}
Node* TreeNextElementIndex(Tree* aTree, Node* curnode, int index)
{
if (curnode == NULL)
curnode = TreeMinimum(aTree->index[index].root);
else
curnode = TreeSuccessor(curnode);
return curnode;
}
Node* TreeNextElement(Tree* aTree, Node* curnode)
{
return TreeNextElementIndex(aTree, curnode, 0);
}
Node* TreeBARSub(Tree* aTree, Node* curnode, int which, int index)
{
Node* sibling = curnode->parent->child[which];
if (isRed(sibling))
{
sibling->red = 0;
curnode->parent->red = 1;
TreeRotate(aTree, curnode->parent, !which, index);
sibling = curnode->parent->child[which];
}
if (!sibling)
curnode = curnode->parent;
else if (isBlack(sibling->child[!which]) && isBlack(sibling->child[which]))
{
sibling->red = 1;
curnode = curnode->parent;
}
else
{
if (isBlack(sibling->child[which]))
{
sibling->child[!which]->red = 0;
sibling->red = 1;
TreeRotate(aTree, sibling, which, index);
sibling = curnode->parent->child[which];
}
sibling->red = curnode->parent->red;
curnode->parent->red = 0;
sibling->child[which]->red = 0;
TreeRotate(aTree, curnode->parent, !which, index);
curnode = aTree->index[index].root;
}
return curnode;
}
void TreeBalanceAfterRemove(Tree* aTree, Node* curnode, int index)
{
while (curnode != aTree->index[index].root && isBlack(curnode))
{
/* curnode->content == NULL must equal curnode == NULL */
if (((curnode->content) ? curnode : NULL) == curnode->parent->child[LEFT])
curnode = TreeBARSub(aTree, curnode, RIGHT, index);
else
curnode = TreeBARSub(aTree, curnode, LEFT, index);
}
curnode->red = 0;
}
/**
* Remove an item from a tree
* @param aTree the list to which the item is to be added
* @param curnode the list item content itself
*/
void* TreeRemoveNodeIndex(Tree* aTree, Node* curnode, int index)
{
Node* redundant = curnode;
Node* curchild = NULL;
int size = curnode->size;
void* content = curnode->content;
/* if the node to remove has 0 or 1 children, it can be removed without involving another node */
if (curnode->child[LEFT] && curnode->child[RIGHT]) /* 2 children */
redundant = TreeSuccessor(curnode); /* now redundant must have at most one child */
curchild = redundant->child[(redundant->child[LEFT] != NULL) ? LEFT : RIGHT];
if (curchild) /* we could have no children at all */
curchild->parent = redundant->parent;
if (redundant->parent == NULL)
aTree->index[index].root = curchild;
else
{
if (redundant == redundant->parent->child[LEFT])
redundant->parent->child[LEFT] = curchild;
else
redundant->parent->child[RIGHT] = curchild;
}
if (redundant != curnode)
{
curnode->content = redundant->content;
curnode->size = redundant->size;
}
if (isBlack(redundant))
{
if (curchild == NULL)
{
if (redundant->parent)
{
Node temp;
memset(&temp, '\0', sizeof(Node));
temp.parent = (redundant) ? redundant->parent : NULL;
temp.red = 0;
TreeBalanceAfterRemove(aTree, &temp, index);
}
}
else
TreeBalanceAfterRemove(aTree, curchild, index);
}
#if defined(UNIT_TESTS)
free(redundant);
#else
(aTree->heap_tracking) ? myfree(__FILE__, __LINE__, redundant) : free(redundant);
#endif
if (index == 0)
{
aTree->size -= size;
--(aTree->count);
}
return content;
}
/**
* Remove an item from a tree
* @param aTree the list to which the item is to be added
* @param curnode the list item content itself
*/
void* TreeRemoveIndex(Tree* aTree, void* content, int index)
{
Node* curnode = TreeFindContentIndex(aTree, content, index);
if (curnode == NULL)
return NULL;
return TreeRemoveNodeIndex(aTree, curnode, index);
}
void* TreeRemove(Tree* aTree, void* content)
{
int i;
void* rc = NULL;
for (i = 0; i < aTree->indexes; ++i)
rc = TreeRemoveIndex(aTree, content, i);
return rc;
}
void* TreeRemoveKeyIndex(Tree* aTree, void* key, int index)
{
Node* curnode = TreeFindIndex(aTree, key, index);
void* content = NULL;
int i;
if (curnode == NULL)
return NULL;
content = TreeRemoveNodeIndex(aTree, curnode, index);
for (i = 0; i < aTree->indexes; ++i)
{
if (i != index)
content = TreeRemoveIndex(aTree, content, i);
}
return content;
}
void* TreeRemoveKey(Tree* aTree, void* key)
{
return TreeRemoveKeyIndex(aTree, key, 0);
}
int TreeIntCompare(void* a, void* b, int content)
{
int i = *((int*)a);
int j = *((int*)b);
//printf("comparing %d %d\n", *((int*)a), *((int*)b));
return (i > j) ? -1 : (i == j) ? 0 : 1;
}
int TreePtrCompare(void* a, void* b, int content)
{
return (a > b) ? -1 : (a == b) ? 0 : 1;
}
int TreeStringCompare(void* a, void* b, int content)
{
return strcmp((char*)a, (char*)b);
}
#if defined(UNIT_TESTS)
int check(Tree *t)
{
Node* curnode = NULL;
int rc = 0;
curnode = TreeNextElement(t, curnode);
while (curnode)
{
Node* prevnode = curnode;
curnode = TreeNextElement(t, curnode);
if (prevnode && curnode && (*(int*)(curnode->content) < *(int*)(prevnode->content)))
{
printf("out of order %d < %d\n", *(int*)(curnode->content), *(int*)(prevnode->content));
rc = 99;
}
}
return rc;
}
int traverse(Tree *t, int lookfor)
{
Node* curnode = NULL;
int rc = 0;
printf("Traversing\n");
curnode = TreeNextElement(t, curnode);
//printf("content int %d\n", *(int*)(curnode->content));
while (curnode)
{
Node* prevnode = curnode;
curnode = TreeNextElement(t, curnode);
//if (curnode)
// printf("content int %d\n", *(int*)(curnode->content));
if (prevnode && curnode && (*(int*)(curnode->content) < *(int*)(prevnode->content)))
{
printf("out of order %d < %d\n", *(int*)(curnode->content), *(int*)(prevnode->content));
}
if (curnode && (lookfor == *(int*)(curnode->content)))
printf("missing item %d actually found\n", lookfor);
}
printf("End traverse %d\n", rc);
return rc;
}
int test(int limit)
{
int i, *ip, *todelete;
Node* current = NULL;
Tree* t = TreeInitialize(TreeIntCompare);
int rc = 0;
printf("Tree initialized\n");
srand(time(NULL));
ip = malloc(sizeof(int));
*ip = 2;
TreeAdd(t, (void*)ip, sizeof(int));
check(t);
i = 2;
void* result = TreeRemove(t, (void*)&i);
if (result)
free(result);
int actual[limit];
for (i = 0; i < limit; i++)
{
void* replaced = NULL;
ip = malloc(sizeof(int));
*ip = rand();
replaced = TreeAdd(t, (void*)ip, sizeof(int));
if (replaced) /* duplicate */
{
free(replaced);
actual[i] = -1;
}
else
actual[i] = *ip;
if (i==5)
todelete = ip;
printf("Tree element added %d\n", *ip);
if (1 % 1000 == 0)
{
rc = check(t);
printf("%d elements, check result %d\n", i+1, rc);
if (rc != 0)
return 88;
}
}
check(t);
for (i = 0; i < limit; i++)
{
int parm = actual[i];
if (parm == -1)
continue;
Node* found = TreeFind(t, (void*)&parm);
if (found)
printf("Tree find %d %d\n", parm, *(int*)(found->content));
else
{
printf("%d not found\n", parm);
traverse(t, parm);
return -2;
}
}
check(t);
for (i = limit -1; i >= 0; i--)
{
int parm = actual[i];
void *found;
if (parm == -1) /* skip duplicate */
continue;
found = TreeRemove(t, (void*)&parm);
if (found)
{
printf("%d Tree remove %d %d\n", i, parm, *(int*)(found));
free(found);
}
else
{
int count = 0;
printf("%d %d not found\n", i, parm);
traverse(t, parm);
for (i = 0; i < limit; i++)
if (actual[i] == parm)
++count;
printf("%d occurs %d times\n", parm, count);
return -2;
}
if (i % 1000 == 0)
{
rc = check(t);
printf("%d elements, check result %d\n", i+1, rc);
if (rc != 0)
return 88;
}
}
printf("finished\n");
return 0;
}
int main(int argc, char *argv[])
{
int rc = 0;
while (rc == 0)
rc = test(999999);
}
#endif

109
src/Tree.h Normal file
View File

@ -0,0 +1,109 @@
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Ian Craggs - initial implementation and documentation
*******************************************************************************/
#if !defined(TREE_H)
#define TREE_H
/*BE
defm defTree(T) // macro to define a tree
def T concat Node
{
n32 ptr T concat Node "parent"
n32 ptr T concat Node "left"
n32 ptr T concat Node "right"
n32 ptr T id2str(T)
n32 suppress "size"
}
def T concat Tree
{
struct
{
n32 ptr T concat Node suppress "root"
n32 ptr DATA suppress "compare"
}
struct
{
n32 ptr T concat Node suppress "root"
n32 ptr DATA suppress "compare"
}
n32 dec "count"
n32 dec suppress "size"
}
endm
defTree(INT)
defTree(STRING)
defTree(TMP)
BE*/
/**
* Structure to hold all data for one list element
*/
typedef struct NodeStruct
{
struct NodeStruct *parent, /**< pointer to parent tree node, in case we need it */
*child[2]; /**< pointers to child tree nodes 0 = left, 1 = right */
void* content; /**< pointer to element content */
int size; /**< size of content */
unsigned int red : 1;
} Node;
/**
* Structure to hold all data for one tree
*/
typedef struct
{
struct
{
Node *root; /**< root node pointer */
int (*compare)(void*, void*, int); /**< comparison function */
} index[2];
int indexes, /**< no of indexes into tree */
count, /**< no of items */
size; /**< heap storage used */
unsigned int heap_tracking : 1; /**< switch on heap tracking for this tree? */
unsigned int allow_duplicates : 1; /**< switch to allow duplicate entries */
} Tree;
Tree* TreeInitialize(int(*compare)(void*, void*, int));
void TreeInitializeNoMalloc(Tree* aTree, int(*compare)(void*, void*, int));
void TreeAddIndex(Tree* aTree, int(*compare)(void*, void*, int));
void* TreeAdd(Tree* aTree, void* content, int size);
void* TreeRemove(Tree* aTree, void* content);
void* TreeRemoveKey(Tree* aTree, void* key);
void* TreeRemoveKeyIndex(Tree* aTree, void* key, int index);
void* TreeRemoveNodeIndex(Tree* aTree, Node* aNode, int index);
void TreeFree(Tree* aTree);
Node* TreeFind(Tree* aTree, void* key);
Node* TreeFindIndex(Tree* aTree, void* key, int index);
Node* TreeNextElement(Tree* aTree, Node* curnode);
int TreeIntCompare(void* a, void* b, int);
int TreePtrCompare(void* a, void* b, int);
int TreeStringCompare(void* a, void* b, int);
#endif