diff options
Diffstat (limited to 'lib/ssl/c_src')
-rw-r--r-- | lib/ssl/c_src/Makefile | 26 | ||||
-rw-r--r-- | lib/ssl/c_src/Makefile.dist | 33 | ||||
-rw-r--r-- | lib/ssl/c_src/Makefile.in | 205 | ||||
-rw-r--r-- | lib/ssl/c_src/Makefile.win32 | 147 | ||||
-rw-r--r-- | lib/ssl/c_src/Makefile.win32.dist | 45 | ||||
-rw-r--r-- | lib/ssl/c_src/debuglog.c | 251 | ||||
-rw-r--r-- | lib/ssl/c_src/debuglog.h | 50 | ||||
-rw-r--r-- | lib/ssl/c_src/esock.c | 1904 | ||||
-rw-r--r-- | lib/ssl/c_src/esock.h | 273 | ||||
-rw-r--r-- | lib/ssl/c_src/esock_openssl.c | 1213 | ||||
-rw-r--r-- | lib/ssl/c_src/esock_osio.c | 328 | ||||
-rw-r--r-- | lib/ssl/c_src/esock_osio.h | 34 | ||||
-rw-r--r-- | lib/ssl/c_src/esock_poll.c | 222 | ||||
-rw-r--r-- | lib/ssl/c_src/esock_poll.h | 60 | ||||
-rw-r--r-- | lib/ssl/c_src/esock_posix_str.c | 642 | ||||
-rw-r--r-- | lib/ssl/c_src/esock_posix_str.h | 28 | ||||
-rw-r--r-- | lib/ssl/c_src/esock_ssl.h | 110 | ||||
-rw-r--r-- | lib/ssl/c_src/esock_utils.c | 150 | ||||
-rw-r--r-- | lib/ssl/c_src/esock_utils.h | 32 | ||||
-rw-r--r-- | lib/ssl/c_src/esock_winsock.h | 36 |
20 files changed, 5789 insertions, 0 deletions
diff --git a/lib/ssl/c_src/Makefile b/lib/ssl/c_src/Makefile new file mode 100644 index 0000000000..52d9140153 --- /dev/null +++ b/lib/ssl/c_src/Makefile @@ -0,0 +1,26 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 1999-2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# + +# + +# +# Invoke with GNU make or clearmake -C gnu. +# + +include $(ERL_TOP)/make/run_make.mk diff --git a/lib/ssl/c_src/Makefile.dist b/lib/ssl/c_src/Makefile.dist new file mode 100644 index 0000000000..2468468921 --- /dev/null +++ b/lib/ssl/c_src/Makefile.dist @@ -0,0 +1,33 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 1999-2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# + +# Makefile for SSL on Unix +# +# Placed in obj directory. +# +CC = gcc + +BINDIR = %BINDIR% +LIBS = %LIBS% +SSL_LIBDIR = %SSL_LIBDIR% +OBJS = %OBJS% + +$(BINDIR)/ssl_esock: $(OBJS) + $(CC) -L$(SSL_LIBDIR) -Wl,-R$(SSL_LIBDIR) -o $@ $^ \ + $(LIBS) -lssl -lcrypto diff --git a/lib/ssl/c_src/Makefile.in b/lib/ssl/c_src/Makefile.in new file mode 100644 index 0000000000..bd1b2f9375 --- /dev/null +++ b/lib/ssl/c_src/Makefile.in @@ -0,0 +1,205 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 1999-2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# + +# +# Makefile only for Unix and Win32/Cygwin. +# + +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk +# ---------------------------------------------------- +# SSL locations and include options from configure +# ---------------------------------------------------- +SSL_LIBDIR = @SSL_LIBDIR@ +SSL_INCLUDE = @SSL_INCLUDE@ + +# ---------------------------------------------------- +# Application version +# ---------------------------------------------------- +include ../vsn.mk +VSN=$(SSL_VSN) + +# ---------------------------------------------------- +# Commands +# ---------------------------------------------------- +CC = @CC@ +LD = @LD@ +SHELL = /bin/sh +LIBS = @LIBS@ +PLAIN_CFLAGS = @CFLAGS@ + +# ---------------------------------------------------- +# Includes and libs +# ---------------------------------------------------- + +ALL_CFLAGS = @WFLAGS@ @CFLAGS@ @DEFS@ $(TYPE_FLAGS) +TARGET = @host@ + +ifeq ($(TYPE),debug) +TYPEMARKER = .debug +TYPE_FLAGS = -g -DDEBUG @DEBUG_FLAGS@ +else +TYPEMARKER = +TYPE_FLAGS = -O2 +endif + +PRIVDIR = ../priv +BINDIR = $(PRIVDIR)/bin/$(TARGET) +OBJDIR = $(PRIVDIR)/obj/$(TARGET) + +# ---------------------------------------------------- +# File suffixes +# ---------------------------------------------------- +exe = @EXEEXT@ +obj = .@OBJEXT@ + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/lib/ssl-$(VSN) + +# ---------------------------------------------------- +# Common Macros +# ---------------------------------------------------- +OBJS = $(OBJDIR)/esock$(obj) \ + $(OBJDIR)/debuglog$(obj) \ + $(OBJDIR)/esock_poll$(obj) \ + $(OBJDIR)/esock_osio$(obj) \ + $(OBJDIR)/esock_utils$(obj) \ + $(OBJDIR)/esock_posix_str$(obj) \ + $(OBJDIR)/esock_openssl$(obj) + +PORT_PROGRAM = $(BINDIR)/ssl_esock$(exe) + +SKIP_BUILDING_BINARIES := false + +# Try to be BC for R10 +ifeq ($(findstring @SSL_,@SSL_DYNAMIC_ONLY@),@SSL_) +DYNAMIC_CRYPTO_LIB=yes +else +DYNAMIC_CRYPTO_LIB=@SSL_DYNAMIC_ONLY@ +endif + + +ifeq ($(DYNAMIC_CRYPTO_LIB),yes) + +ifneq ($(findstring win32,$(TARGET)),win32) +SSL_MAKEFILE = $(OBJDIR)/Makefile +else +SSL_MAKEFILE = +endif + +CC_R_FLAG=@CFLAG_RUNTIME_LIBRARY_PATH@ +ifeq ($(findstring @,$(CC_R_FLAG)),@) +# Old erts configure used which hasn't replaced @CFLAG_RUNTIME_LIBRARY_PATH@; +# we try our best here instead... + +ifeq ($(findstring darwin,$(TARGET)),darwin) # darwin: no flag +CC_R_FLAG = +else +ifeq ($(findstring osf,$(TARGET)),osf) # osf1: -Wl,-rpath, +CC_R_FLAG = -Wl,-rpath, +else # Default: -Wl,-R +CC_R_FLAG = -Wl,-R +endif +endif +endif + +ifeq ($(strip $(CC_R_FLAG)),) +CC_R_OPT = +else +CC_R_OPT = $(CC_R_FLAG)$(SSL_LIBDIR) +endif + +SSL_LINK_LIB=-L$(SSL_LIBDIR) -lssl -lcrypto +else +# not dynamic crypto lib (default from R11B-5) +NEED_KERBEROS=@SSL_LINK_WITH_KERBEROS@ +NEED_ZLIB=@SSL_LINK_WITH_ZLIB@ +SSL_MAKEFILE = +CC_R_OPT = +SSL_LINK_LIB = $(SSL_LIBDIR)/libssl.a $(SSL_LIBDIR)/libcrypto.a +ifeq ($(NEED_KERBEROS),yes) +SSL_LINK_LIB += @STATIC_KERBEROS_LIBS@ +endif +ifeq ($(NEED_ZLIB),yes) +SSL_LINK_LIB += @STATIC_ZLIB_LIBS@ +endif +endif + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +debug opt: $(OBJDIR) $(BINDIR) $(OBJS) $(PORT_PROGRAM) $(SSL_MAKEFILE) + +$(OBJDIR): + -@mkdir -p $(OBJDIR) + +$(BINDIR): + -@mkdir -p $(BINDIR) + +$(OBJDIR)/esock_openssl$(obj): esock_openssl.c + $(CC) -c -o $@ $(ALL_CFLAGS) $(SSL_INCLUDE) $< + +$(OBJDIR)/%$(obj): %.c + $(CC) -c -o $@ $(ALL_CFLAGS) $< + +# Unix +$(BINDIR)/ssl_esock: $(OBJS) + $(CC) $(CC_R_OPT) $(PLAIN_CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) $(SSL_LINK_LIB) + +# Win32/Cygwin +$(BINDIR)/ssl_esock.exe: $(OBJS) + $(LD) -L$(SSL_LIBDIR) -o $@ $^ -lwsock32 -llibeay32 -lssleay32 + +# Unix only, and only when linking statically +$(SSL_MAKEFILE): + sed -e "s;%BINDIR%;../../bin/$(TARGET);" \ + -e "s;%SSL_LIBDIR%;$(SSL_LIBDIR);" \ + -e "s;%OBJS;$(OBJS);" \ + -e "s;%LIBS%;$(LIBS);" ./Makefile.dist \ + > $(OBJDIR)/Makefile + + +clean: + rm -f $(PORT_PROGRAM) $(OBJS) core *~ $(SSL_MAKEFILE) + +docs: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + $(INSTALL_DIR) $(RELSYSDIR)/priv/bin + $(INSTALL_PROGRAM) $(PORT_PROGRAM) $(RELSYSDIR)/priv/bin +ifneq ($(SSL_MAKEFILE),) + $(INSTALL_DIR) $(RELSYSDIR)/priv/obj + $(INSTALL_DATA) $(OBJS) $(RELSYSDIR)/priv/obj + sed -e "s;%BINDIR%;../bin;" \ + -e "s;%SSL_LIBDIR%;$(SSL_LIBDIR);" \ + -e "s;%OBJS;$(OBJS);" \ + -e "s;%LIBS%;$(LIBS);" ./Makefile.dist \ + > $(RELSYSDIR)/priv/obj/Makefile +endif + +release_docs_spec: + diff --git a/lib/ssl/c_src/Makefile.win32 b/lib/ssl/c_src/Makefile.win32 new file mode 100644 index 0000000000..668cd2a28d --- /dev/null +++ b/lib/ssl/c_src/Makefile.win32 @@ -0,0 +1,147 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 1999-2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# + +# +# SSL - Makefile for Windows NT +# +# It is assumed that the following environment variables have been set: +# +# INCLUDE X:\MSDEV\INCLUDE +# LIB X:\MSDEV\LIB +# +# so that standard include files, and the socket library can be found. +# +# When ssl_esock.exe is run, the PATH environment variable must contain +# the name of a directory that contains ssleay32.dll and libeay32.dll, +# and windows socket dll. +# + +# Roots +!ifndef OPENSSL_ROOT +! error "Makefile.win32: ssl: OPENSSL_ROOT not set" +!endif + +TARGET = win32 + +BINDIR = ..\priv\bin\$(TARGET) +OBJDIR = ..\priv\obj\$(TARGET) + +!if !exist($(BINDIR)) +! if [mkdir $(BINDIR)] +! error "SSL: cannot create BINDIR" +! endif +!endif + +!if !exist($(OBJDIR)) +! if [mkdir $(OBJDIR)] +! error "SSL: cannot create OBJDIR" +! endif +!endif + +# Includes +# +OPENSSL_INCLUDE = $(OPENSSL_ROOT)\inc32 + +INCLUDES = /I. /I$(OPENSSL_INCLUDE) + +# Libraries +# +OPENSSL_LIBDIR = $(OPENSSL_ROOT)\out32dll +OPENSSL_LIBS = \ + $(OPENSSL_LIBDIR)\ssleay32.lib \ + $(OPENSSL_LIBDIR)\libeay32.lib + +!ifdef ESOCK_WINSOCK2 +WINSOCK_LIB = ws2_32.lib +DEFS = -DESOCK_WINSOCK2 +!else +WINSOCK_LIB = wsock32.lib +!endif + +# Compiler options +# +# NOTE: Size of fd_set is set in esock_winsock.h but can be overridden +# with a -D option here. +# +OPTS = /MDd /G5 /Ox /O2 /Ob2 /Z7 +DEFS = -D__WIN32__ -DWIN32 $(DEFS) +CFLAGS = $(INCLUDES) /nologo $(OPTS) $(DEFS) + +# Object files +# +SSL_BASE_OBJS = \ + $(OBJDIR)\esock.obj \ + $(OBJDIR)\debuglog.obj \ + $(OBJDIR)\esock_poll$(obj) \ + $(OBJDIR)\esock_osio.obj \ + $(OBJDIR)\esock_utils.obj \ + $(OBJDIR)\esock_posix_str.obj + +OPENSSL_OBJS = \ + $(OBJDIR)\esock_openssl.obj + +# +# Targets +# + +all: $(SSL_BASE_OBJS) $(OPENSSL_OBJS) $(BINDIR)\ssl_esock.exe + +clean: + del $(BINDIR)\*.exe + del $(OBJDIR)\*.obj + +# Inference rule .c.obj: +# +{.}.c{$(OBJDIR)}.obj: + $(CC) $(CFLAGS) /c /Fo$@ $(*B).c + +# Binary +# +$(BINDIR)\ssl_esock.exe: $(SSL_BASE_OBJS) $(OPENSSL_OBJS) + $(CC) /nologo $(SSL_BASE_OBJS) $(OPENSSL_OBJS) $(OPENSSL_LIBS) \ + $(WINSOCK_LIB) /Fe$(BINDIR)\ssl_esock.exe + + + +# Dependencies +# +$(OBJDIR)\esock.o: esock.h debuglog.h esock_ssl.h esock_osio.h \ + esock_utils.h esock_winsock.h +$(OBJDIR)\debuglog.o: debuglog.h esock_ssl.h esock_utils.h +$(OBJDIR)\esock_osio.o: esock_osio.h esock.h debuglog.h esock_utils.h \ + esock_winsock.h +$(OBJDIR)\esock_utils.o: esock_utils.h +$(OBJDIR)\esock_posix_str.o: esock_posix_str.h esock_winsock.h + +$(OBJDIR)\esock_openssl.o: esock.h esock_ssl.h debuglog.h esock_utils.h \ + $(OPENSSL_INCLUDE)\crypto.h \ + $(OPENSSL_INCLUDE)\ssl.h \ + $(OPENSSL_INCLUDE)\err.h + + + + + + + + + + + + diff --git a/lib/ssl/c_src/Makefile.win32.dist b/lib/ssl/c_src/Makefile.win32.dist new file mode 100644 index 0000000000..8510c44e08 --- /dev/null +++ b/lib/ssl/c_src/Makefile.win32.dist @@ -0,0 +1,45 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 1999-2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# + +# Makefile.win32.dist for SSL +# +# To be placed in obj directory. +# + +CC = cl + +BINDIR = %BINDIR% + +OPENSSL_LIBS = \ + $(BINDIR)\ssleay32.lib \ + $(BINDIR)\libeay32.lib + +WINSOCK_LIB = ws2_32.lib + +SSL_BASE_OBJS = esock.obj debuglog.obj esock_osio.obj esock_utils.obj \ + esock_posix_str.obj + +OPENSSL_OBJS = esock_openssl.obj + +$(BINDIR)\ssl_esock.exe: $(SSL_BASE_OBJS) $(OPENSSL_OBJS) + $(CC) /nologo $(SSL_BASE_OBJS) $(OPENSSL_OBJS) $(OPENSSL_LIBS) \ + $(WINSOCK_LIB) /Fe$(BINDIR)\ssl_esock.exe + + + diff --git a/lib/ssl/c_src/debuglog.c b/lib/ssl/c_src/debuglog.c new file mode 100644 index 0000000000..e2e55df4b2 --- /dev/null +++ b/lib/ssl/c_src/debuglog.c @@ -0,0 +1,251 @@ +/*<copyright> + * <year>1999-2008</year> + * <holder>Ericsson AB, All Rights Reserved</holder> + *</copyright> + *<legalnotice> + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson AB. + *</legalnotice> + */ +/* + * Purpose: Various routines for debug printouts and logs. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <ctype.h> +#include <time.h> +#include "debuglog.h" +#include "esock_utils.h" + +#ifndef __WIN32__ +static char tr_format_buf[256]; +static char *tr_format(const char *format); +static int vfprintclistf(FILE *fp, const char *format, va_list args); +#endif + +int debug = 0; +int debugmsg = 0; +FILE *ssllogfp = NULL; +FILE *__locallogfp = NULL; + +void open_ssllog(char *path) +{ + ssllogfp = openlog(path); +} + +void close_ssllog(void) +{ + if (ssllogfp) + closelog(ssllogfp); +} + +FILE *openlog(char *s) +{ + FILE *fp; + time_t t = time(NULL); + + if ((fp = fopen(s, "a"))) { + setbuf(fp, NULL); + fprintf(fp, "===== Opened [%s] %s", s, ctime(&t)); + } + return fp; +} + +void closelog(FILE *fp) +{ + time_t t = time(NULL); + + if (fp) { + fprintf(fp, "Closed %s", ctime(&t)); + fclose(fp); + } +} + +int __debugprintf(const char *format, ...) +{ + va_list args; + int ret; +#ifndef __WIN32__ + char *newformat; + + va_start(args, format); + newformat = tr_format(format); + ret = vfprintf(stderr, newformat, args); + if (newformat != format && newformat != tr_format_buf) + esock_free(newformat); +#else + va_start(args, format); + ret = vfprintf(stderr, format, args); +#endif + va_end(args); + if (ssllogfp) { + va_start(args, format); + vfprintf(ssllogfp, format, args); + va_end(args); + } + return ret; +} + +int __debugprintclistf(const char *format, ...) +{ + va_list args; + int ret; +#ifndef __WIN32__ + char *newformat; + + va_start(args, format); + newformat = tr_format(format); + ret = vfprintclistf(stderr, newformat, args); + if (newformat != format && newformat != tr_format_buf) + esock_free(newformat); +#else + va_start(args, format); + ret = vfprintclistf(stderr, format, args); +#endif + if (ssllogfp) + vfprintclistf(ssllogfp, format, args); + va_end(args); + return ret; +} + +int __debuglogf(const char *format, ...) +{ + va_list args; + int ret; + + va_start(args, format); + ret = vfprintf(__locallogfp, format, args); + va_end(args); + return ret; +} + +#ifndef __WIN32__ + +/* Insert `\r' before each `\n' i format */ +static char *tr_format(const char *format) +{ + char *newformat, *s, *t; + int len; + + len = strlen(format); + if ((newformat = (len > 127) ? esock_malloc(len) : tr_format_buf)) { + for (s = (char *)format, t = newformat; *s; *t++ = *s++) + if (*s == '\n') + *t++ = '\r'; + *t = '\0'; + } else + newformat = (char *)format; + return newformat; +} + +#endif + +/* This function is for printing arrays of characters with formats + * %FPa or %FPb, where F and P are the ordinary specifiers for + * field width and precision, respectively. + * + * The conversion specifier `a' implies hex-string output, while + * the `b' specifier provides character output (for non-printable + * characters a `.' is written. + * + * The F specifier contains the width for each character. The + * P specifier tells how many characters to print. + * + * Example: Suppose we have a function myprintf(char *format, ...) + * that calls our vfprintclistf(), and that + * + * char buf[] = "h\r\n"; + * len = 3; + * + * Then + * + * myprintf("%.2b", buf) prints "h." + * myprintf("%2.3b", buf) prints "h . . " + * myprintf("%3.*a", len, buf) prints "68 0d 0a" + * + */ + +static int vfprintclistf(FILE *fp, const char *format, va_list args) +{ + + int i, len, width, prec, written = 0; + char *s, *prevs, *fstart; + unsigned char *buf; + + if (!format || !*format) + return 0; + + /* %{[0-9]*|\*}{.{[0-9]*|\*}{a|b} */ + + prevs = (char *)format; /* format is const */ + s = strchr(format, '%'); + while (s && *s) { + if (s - prevs > 0) + written += fprintf(fp, "%.*s", s - prevs, prevs); + width = prec = 0; + fstart = s; + s++; + if (*s != '%') { /* otherwise it is not a format */ + if (*s == '*') { /* width in arg */ + s++; + width = va_arg(args, int); + } else if ((len = strspn(s, "0123456789"))) { /* const width */ + width = atoi(s); + s += len; + } else + width = 0; + if (*s == '.') { /* precision specified */ + s++; + if (*s == '*') { /* precision in arg */ + s++; + prec = va_arg(args, int); + } else if ((len = strspn(s, "0123456789"))) { /* const prec */ + prec = atoi(s); + s += len; + } else /* no precision value, defaults to zero */ + prec = 0; + } else + prec = 0; /* no precision defaults to zero */ + if (*s == 'a' || *s == 'b') { /* only valid specifiers */ + buf = va_arg(args, unsigned char *); + if (*s == 'a') { + for (i = 0; i < prec; i++) + written += fprintf(fp, "%*.2x", width, buf[i]); + }else if (*s == 'b') { + for (i = 0; i < prec; i++) { + if (isprint(buf[i])) + written += fprintf(fp, "%*c", width, buf[i]); + else + written += fprintf(fp, "%*c", width, '.'); + } + } + } else { + fprintf(stderr, "fprintclistf: format \"%s\" invalid.\n", + format); + va_end(args); + return written; + } + } + s++; + /* Now s points to the next character after the format */ + prevs = s; + s = strchr(s, '%'); + } + if (format + strlen(format) + 1 - prevs > 0) + written += fprintf(fp, "%s", prevs); + return written; +} + diff --git a/lib/ssl/c_src/debuglog.h b/lib/ssl/c_src/debuglog.h new file mode 100644 index 0000000000..5699e6b495 --- /dev/null +++ b/lib/ssl/c_src/debuglog.h @@ -0,0 +1,50 @@ +/*<copyright> + * <year>1998-2008</year> + * <holder>Ericsson AB, All Rights Reserved</holder> + *</copyright> + *<legalnotice> + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson AB. + *</legalnotice> + */ +/* + * Purpose: Debug functions and macros. + * + */ + +#ifndef __DEBUGLOG_H_ +#define __DEBUGLOG_H_ + +#include <stdio.h> +#include "esock_ssl.h" + +#define DEBUGF(x) if (debug) __debugprintf x; +#define DEBUGMSGF(x) if (debugmsg) __debugprintclistf x; +#define LOGF(fp, x) if (fp) { __locallogfp = fp; __debuglogf x; } +#define SSLDEBUGF() if (debug) { esock_ssl_print_errors_fp(stderr); \ + if (ssllogfp) esock_ssl_print_errors_fp(ssllogfp); } + +int debug; +int debugmsg; +FILE *ssllogfp; +FILE *__locallogfp; + +void open_ssllog(char *path); +void close_ssllog(void); +FILE *openlog(char *); +void closelog(FILE *); +int __debugprintf(const char *, ...); +int __debugprintclistf(const char *, ...); +int __debuglogf(const char *, ...); + +#endif diff --git a/lib/ssl/c_src/esock.c b/lib/ssl/c_src/esock.c new file mode 100644 index 0000000000..78d08f7c29 --- /dev/null +++ b/lib/ssl/c_src/esock.c @@ -0,0 +1,1904 @@ +/*<copyright> + * <year>1999-2008</year> + * <holder>Ericsson AB, All Rights Reserved</holder> + *</copyright> + *<legalnotice> + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson AB. + *</legalnotice> + */ + +/* + * Purpose: Implementation of Secure Socket Layer (SSL). + * + * This is an "SSL proxy" for Erlang in the form of a port + * program. + * + * The implementation has borrowed somewhat from the original + * implementation of `socket' by Claes Wikstr�m, and the former + * implementation of `ssl_socket' by Helen Ariyan. + * + * All I/O is now non-blocking. + * + * When a connection (cp) is in the state JOINED we have the following + * picture: + * + * proxy->fd fd + * | | + * proxy->eof | --------> wq -----------> | bp + * | | + * Erlang | | SSL + * | | + * proxy->bp | <------ proxy->wq --------- | eof + * | | + * + * We read from Erlang (proxy->fd) and write to SSL (fd); and read from + * SSL (fd) and write to Erlang (proxy->fd). + * + * The variables bp (broken pipe) and eof (end of file) take the + * values 0 and 1. + * + * What has been read and cannot be immediately written is put in a + * write queue (wq). A wq is emptied before reads are continued, which + * means that at most one chunk that is read can be in a wq. + * + * The proxy-to-ssl part of a cp is valid iff + * + * !bp && (wq.len > 0 || !proxy->eof). + * + * The ssl-to-proxy part of a cp is valid iff + * + * !proxy->bp && (proxy->wq.len > 0 || !eof). + * + * The connection is valid if any of the above parts are valid, i.e. + * invalid if both parts are invalid. + * + * Every SELECT_TIMEOUT second we try to write to those file + * descriptors that have non-empty wq's (the only way to detect that a + * far end has gone away is to write to it). + * + * STATE TRANSITIONS + * + * Below (*) means that the corresponding file descriptor is published + * (i.e. kwown outside this port program) when the state is entered, + * and thus cannot be closed without synchronization with the + * ssl_server. + * + * Listen: + * + * STATE_NONE ---> (*) PASSIVE_LISTENING <---> ACTIVE_LISTENING + * + * Accept: + * + * STATE_NONE ---> SSL_ACCEPT ---> (*) CONNECTED ---> JOINED ---> + * ---> SSL_SHUTDOWN ---> DEFUNCT + * + * Connect: + * + * STATE_NONE ---> (*) WAIT_CONNECT ---> SSL_CONNECT ---> CONNECTED ---> + * ---> JOINED ---> SSL_SHUTDOWN ---> DEFUNCT + * + * In states where file descriptors has been published, and where + * something goes wrong, the state of the connection is set to + * DEFUNCT. A connection in such a state can only be closed by a CLOSE + * message from Erlang (a reception of such a message is registered in + * cp->closed). The possible states are: WAIT_CONNECT, SSL_CONNECT, + * CONNECTED, JOINED, and SSL_SHUTDOWN. + * + * A connection in state SSL_ACCEPT can be closed and removed without + * synchronization. + * + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef __WIN32__ +#include "esock_winsock.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <time.h> +#include <ctype.h> +#include <sys/types.h> +#include <errno.h> + +#ifdef __WIN32__ +#include <process.h> +#else +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <sys/time.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <fcntl.h> +#endif + +#ifndef INADDR_NONE +#define INADDR_NONE 0xffffffff /* Should be in <netinet/in.h>. */ +#endif + +#include "esock.h" +#include "debuglog.h" +#include "esock_utils.h" +#include "esock_ssl.h" +#include "esock_osio.h" +#include "esock_posix_str.h" +#include "esock_poll.h" + +#define MAJOR_VERSION 2 +#define MINOR_VERSION 0 +#define MAXREPLYBUF 256 +#define RWBUFLEN (32*1024) +#define IS_CLIENT 0 +#define IS_SERVER 1 +#define SELECT_TIMEOUT 2 /* seconds */ + +#define psx_errstr() esock_posix_str(sock_errno()) +#define ssl_errstr() esock_ssl_errstr + +#define PROXY_TO_SSL_VALID(cp) (!(cp)->bp && \ + ((cp)->wq.len > 0 || !(cp)->proxy->eof)) + +#define SSL_TO_PROXY_VALID(cp) (!(cp)->proxy->bp && \ + ((cp)->proxy->wq.len > 0 || !(cp)->eof)) + +#define JOINED_STATE_INVALID(cp) (!(PROXY_TO_SSL_VALID(cp)) && \ + !(SSL_TO_PROXY_VALID(cp))) +static int loop(void); +static int set_poll_conns(Connection *cp, EsockPoll *ep, int verbose); +static Connection *next_polled_conn(Connection *cp, Connection **cpnext, + EsockPoll *ep, int set_wq_fds); + +static void leave_joined_state(Connection *cp); +static void do_shutdown(Connection *cp); +static void close_and_remove_connection(Connection *cp); +static int reply(int cmd, char *fmt, ...); +static int input(char *fmt, ...); +static int put_pars(unsigned char *buf, char *fmt, va_list args); +static int get_pars(unsigned char *buf, char *fmt, va_list args); +static FD do_connect(char *lipstring, int lport, char *fipstring, int fport); +static FD do_listen(char *ipstring, int lport, int backlog, int *aport); +static FD do_accept(FD listensock, struct sockaddr *saddr, int *len); +static void print_connections(void); +static void dump_connections(void); +static int check_num_sock_fds(FD fd); +static void safe_close(FD fd); +static Connection *new_connection(int state, FD fd); +static Connection *get_connection(FD fd); +static void remove_connection(Connection *conn); +static Proxy *get_proxy_by_peerport(int port); +static Proxy *new_proxy(FD fd); +static void remove_proxy(Proxy *proxy); +static void ensure_write_queue(WriteQueue *wq, int size); +static void clean_up(void); + +static Connection *connections = NULL; +static int num_sock_fds; /* On UNIX all file descriptors */ +static Proxy *proxies = NULL; +static int proxy_listensock = INVALID_FD; +static int proxy_listenport = 0; +static int proxy_backlog = 128; +static int proxysock_last_err = 0; +static int proxysock_err_cnt = 0; +static char rwbuf[RWBUFLEN]; +static unsigned char *ebuf = NULL; /* Set by read_ctrl() */ + +static char *connstr[] = { + "STATE_NONE", + "ACTIVE_LISTENING", + "PASSIVE_LISTENING", + "CONNECTED", + "WAIT_CONNECT", + "SSL_CONNECT", + "SSL_ACCEPT", + "TRANSPORT_ACCEPT", + "JOINED", + "SSL_SHUTDOWN", + "DEFUNCT" +}; + +static char *originstr[] = { + "listen", + "accept", + "connect" +}; + +int main(int argc, char **argv) +{ + char *logfile = NULL; + int i; + esock_version *vsn; + char *ciphers; +#ifdef __WIN32__ + int pid; + WORD version; + WSADATA wsa_data; + + set_binary_mode(); + setvbuf(stderr, NULL, _IONBF, 0); + /* Two sockets for the stdin socket pipe (local thread). */ + num_sock_fds = 2; +#else + pid_t pid; + num_sock_fds = 3; /* 0, 1, 2 */ +#endif + + pid = getpid(); + i = 1; + while (i < argc) { + if (strcmp(argv[i], "-d") == 0) { + debug = 1; + i++; + } else if (strcmp(argv[i], "-dm") == 0) { + debugmsg = 1; + i++; + } else if (strcmp(argv[i], "-pp") == 0) { + i++; + proxy_listenport = atoi(argv[i]); + i++; + } else if (strcmp(argv[i], "-pb") == 0) { + i++; + proxy_backlog = atoi(argv[i]); + i++; + } else if (strcmp(argv[i], "-pv") == 0) { + i++; + protocol_version = atoi(argv[i]); + i++; + } else if (strcmp(argv[i], "-dd") == 0) { + i++; + logfile = esock_malloc(strlen(argv[i]) + 64); + sprintf(logfile, "%s/ssl_esock.%d.log", argv[i], (int)pid); + i++; + } else if (strcmp(argv[i], "-ersa") == 0) { + ephemeral_rsa = 1; + i++; + } else if (strcmp(argv[i], "-edh") == 0) { + ephemeral_dh = 1; + i++; + } + } + if (debug || debugmsg) { + DEBUGF(("Starting ssl_esock\n")); + if (logfile) { + open_ssllog(logfile); +#ifndef __WIN32__ + num_sock_fds++; +#endif + } + atexit(close_ssllog); + DEBUGF(("pid = %d\n", getpid())); + } + if (esock_ssl_init() < 0) { + fprintf(stderr, "esock: Could not do esock_ssl_init\n"); + exit(EXIT_FAILURE); + } + + atexit(esock_ssl_finish); + +#ifdef __WIN32__ + /* Start Windows' sockets */ + version = MAKEWORD(MAJOR_VERSION, MINOR_VERSION); + if (WSAStartup(version, &wsa_data) != 0) { + fprintf(stderr, "esock: Could not start up Windows' sockets\n"); + exit(EXIT_FAILURE); + } + atexit((void (*)(void))WSACleanup); + if (LOBYTE(wsa_data.wVersion) < MAJOR_VERSION || + (LOBYTE(wsa_data.wVersion) == MAJOR_VERSION && + HIBYTE(wsa_data.wVersion) < MINOR_VERSION)) { + fprintf(stderr, "esock: Windows socket version error. " + "Requested version:" + "%d.%d, version found: %d.%d\n", MAJOR_VERSION, + MINOR_VERSION, LOBYTE(wsa_data.wVersion), + HIBYTE(wsa_data.wVersion)); + exit(EXIT_FAILURE); + } + DEBUGF(("Using Windows socket version: %d.%d\n", + LOBYTE(wsa_data.wVersion), HIBYTE(wsa_data.wVersion))); + DEBUGF(("Maximum number of sockets available: %d\n", + wsa_data.iMaxSockets)); + + if (esock_osio_init() < 0) { + fprintf(stderr, "esock: Could not init osio\n"); + exit(EXIT_FAILURE); + } + atexit(esock_osio_finish); +#endif + + /* Create the local proxy listen socket and set it to non-blocking */ + proxy_listensock = do_listen("127.0.0.1", proxy_listenport, + proxy_backlog, &proxy_listenport); + if (proxy_listensock == INVALID_FD) { + fprintf(stderr, "esock: Cannot create local listen socket\n"); + exit(EXIT_FAILURE); + } + SET_NONBLOCKING(proxy_listensock); + DEBUGF(("Local proxy listen socket: fd = %d, port = %d\n", + proxy_listensock, proxy_listenport)); + + vsn = esock_ssl_version(); + ciphers = esock_ssl_ciphers(); + + /* Report: port number of the local proxy listen socket, the native + * os pid, the compile and lib versions of the ssl library, and + * the list of available ciphers. */ + reply(ESOCK_PROXY_PORT_REP, "24sss", proxy_listenport, (int)pid, + vsn->compile_version, vsn->lib_version, ciphers); + + atexit(clean_up); + + loop(); + + if (logfile) + esock_free(logfile); + exit(EXIT_SUCCESS); +} + + +/* + * Local functions + * + */ + +static int loop(void) +{ + EsockPoll pollfd; + FD fd, msgsock, listensock, connectsock, proxysock; + int cc, wc, fport, lport, pport, length, backlog, intref, op; + int value; + char *lipstring, *fipstring; + char *flags; + char *protocol_vsn, *cipher; + unsigned char *cert, *bin; + int certlen, binlen; + struct sockaddr_in iserv_addr; + int sret = 1; + Connection *cp, *cpnext, *newcp; + Proxy *pp; + time_t last_time = 0, now = 0; + int set_wq_fds; + + esock_poll_init(&pollfd); + + while(1) { + esock_poll_zero(&pollfd); + esock_poll_fd_set_read(&pollfd, proxy_listensock); + esock_poll_fd_set_read(&pollfd, local_read_fd); + + set_wq_fds = 0; + + if (sret) /* sret == 1 the first time. */ + DEBUGF(("==========LOOP=============\n")); + + cc = set_poll_conns(connections, &pollfd, sret) + 1; + + if (sret) { + print_connections(); + DEBUGF(("Before poll/select: %d descriptor%s (total %d)\n", + cc, (cc == 1) ? "" : "s", num_sock_fds)); + } + + sret = esock_poll(&pollfd, SELECT_TIMEOUT); + if (sret < 0) { + DEBUGF(("select/poll error: %s\n", psx_errstr())); + continue; + } + + time(&now); + if (now >= last_time + SELECT_TIMEOUT) { + set_wq_fds = 1; + last_time = now; + } + /* + * First accept as many connections as possible on the + * proxy listen socket. We record the peer port, which + * is later used as a reference for joining a proxy + * connection with a network connection. + */ + + if (esock_poll_fd_isset_read(&pollfd, proxy_listensock)) { + while (1) { + length = sizeof(iserv_addr); + proxysock = do_accept(proxy_listensock, + (struct sockaddr *)&iserv_addr, + (int*)&length); + if(proxysock == INVALID_FD) { + if (sock_errno() != ERRNO_BLOCK) { + /* We can here for example get the error + * EMFILE, i.e. no more file descriptors + * available, but we do not have any specific + * connection to report the error to. We + * increment the error counter and saves the + * last err. + */ + proxysock_err_cnt++; + proxysock_last_err = sock_errno(); + DEBUGF(("accept error (proxy_listensock): %s\n", + psx_errstr())); + } + break; + } else { + /* Get peer port number */ +/* length = sizeof(iserv_addr); */ +/* if (getpeername(proxysock, (struct sockaddr *)&iserv_addr, */ +/* &length) < 0) { */ +/* DEBUGF(("Can't get peername of proxy socket")); */ +/* safe_close(proxysock); */ +/* } else { */ + /* Add to pending proxy connections */ + SET_NONBLOCKING(proxysock); + pp = new_proxy(proxysock); + pp->peer_port = ntohs(iserv_addr.sin_port); + DEBUGF(("-----------------------------------\n")); + DEBUGF(("[PROXY_LISTEN_SOCK] conn accepted: " + "proxyfd = %d, " + "peer port = %d\n", proxysock, pp->peer_port)); +/* } */ + } + } + } + + /* + * Read control messages from Erlang + */ + if (esock_poll_fd_isset_read(&pollfd, local_read_fd)) { + cc = read_ctrl(&ebuf); + if ( cc < 0 ) { + DEBUGF(("Read loop -1 or 0\n")); + return -1; + } else if (cc == 0) { /* not eof */ + DEBUGF(("GOT empty string \n")); + + } else { + + switch((int)*ebuf) { + + case ESOCK_SET_SEED_CMD: + /* + * ebuf = {cmd(1), binary(N) } + */ + input("b", &binlen, &bin); + DEBUGF(("[SET_SEED_CMD]\n")); + esock_ssl_seed(bin, binlen); + /* no reply */ + break; + + case ESOCK_GETPEERNAME_CMD: + /* + * ebuf = {cmd(1), fd(4)} + */ + input("4", &fd); + DEBUGF(("[GETPEERNAME_CMD] fd = %d\n", fd)); + cp = get_connection(fd); + length = sizeof(iserv_addr); + if (!cp) { + sock_set_errno(ERRNO_NOTSOCK); + reply(ESOCK_GETPEERNAME_ERR, "4s", fd, psx_errstr()); + } else if (getpeername(fd, + (struct sockaddr *) &iserv_addr, + &length) < 0) { + reply(ESOCK_GETPEERNAME_ERR, "4s", fd, psx_errstr()); + } else { + /* + * reply = {cmd(1), fd(4), port(2), + * ipstring(N), 0(1)} + */ + reply(ESOCK_GETPEERNAME_REP, "42s", fd, + ntohs(iserv_addr.sin_port), + inet_ntoa(iserv_addr.sin_addr)); + } + break; + + case ESOCK_GETSOCKNAME_CMD: + /* + * ebuf = {cmd(1), fd(4)} + */ + input("4", &fd); + DEBUGF(("[GETSOCKNAME_CMD] fd = %d\n", fd)); + cp = get_connection(fd); + length = sizeof(iserv_addr); + if (!cp) { + sock_set_errno(ERRNO_NOTSOCK); + reply(ESOCK_GETSOCKNAME_ERR, "4s", fd, psx_errstr()); + } else if (getsockname(fd, + (struct sockaddr *)&iserv_addr, + &length) < 0) { + reply(ESOCK_GETSOCKNAME_ERR, "4s", fd, psx_errstr()); + } else { + /* + * reply = {cmd(1), fd(4), port(2), + * ipstring(N), 0(1)} + */ + reply(ESOCK_GETSOCKNAME_REP, "42s", fd, + ntohs(iserv_addr.sin_port), + inet_ntoa(iserv_addr.sin_addr)); + } + break; + + case ESOCK_GETCONNINFO_CMD: + /* + * ebuf = {cmd(1), fd(4)} + */ + input("4", &fd); + DEBUGF(("[GETCONNINFO_CMD] fd = %d\n", fd)); + cp = get_connection(fd); + if (!cp) { + sock_set_errno(ERRNO_NOTSOCK); + reply(ESOCK_GETCONNINFO_ERR, "4s", fd, psx_errstr()); + } else { + if (esock_ssl_getprotocol_version(cp, + &protocol_vsn) < 0) + reply(ESOCK_GETCONNINFO_ERR, "4s", fd, psx_errstr()); + else if (esock_ssl_getcipher(cp, &cipher) < 0) + reply(ESOCK_GETCONNINFO_ERR, "4s", fd, psx_errstr()); + else + /* + * reply = {cmd(1), fd(4), protocol(N), 0(1), + * cipher(N), 0(1)} + */ + reply(ESOCK_GETCONNINFO_REP, "4ss", fd, + protocol_vsn, cipher); + } + break; + + case ESOCK_GETPEERCERT_CMD: + /* + * ebuf = {cmd(1), fd(4)} + */ + input("4", &fd); + DEBUGF(("[GETPEERCERT_CMD] fd = %d\n", fd)); + cp = get_connection(fd); + if (!cp) { + sock_set_errno(ERRNO_NOTSOCK); + reply(ESOCK_GETPEERCERT_ERR, "4s", fd, psx_errstr()); + } else { + if ((certlen = esock_ssl_getpeercert(cp, &cert)) < 0) + reply(ESOCK_GETPEERCERT_ERR, "4s", fd, psx_errstr()); + else { + /* + * reply = {cmd(1), fd(4), certlen(4), cert(N)} + */ + reply(ESOCK_GETPEERCERT_REP, "4b", fd, + certlen, cert); + esock_free(cert); + } + } + break; + + case ESOCK_CONNECT_CMD: + /* + * ebuf = {cmd(1), intref(4), + * lport(2), lipstring(N), 0(1), -- local + * fport(2), fipstring(N), 0(1), -- foreign + * flags(N), 0(1)} + */ + input("42s2ss", &intref, &lport, &lipstring, + &fport, &fipstring, &flags); + DEBUGF(("[CONNECT_CMD] intref = %d, " + "lipstring = %s lport = %d, " + "fipstring = %s fport = %d, " + "flags = %s\n", intref, lipstring, lport, + fipstring, fport, flags)); + connectsock = do_connect(lipstring, lport, + fipstring, fport); + if(connectsock == INVALID_FD) { + reply(ESOCK_CONNECT_SYNC_ERR, "4s", intref, psx_errstr()); + break; + } + DEBUGF((" fd = %d\n", connectsock)); + cp = new_connection(ESOCK_WAIT_CONNECT, connectsock); + cp->origin = ORIG_CONNECT; + length = strlen(flags); + cp->flags = esock_malloc(length + 1); + strcpy(cp->flags, flags); + DEBUGF(("-> WAIT_CONNECT fd = %d\n", connectsock)); + /* Publish connectsock */ + reply(ESOCK_CONNECT_WAIT_REP, "44", intref, connectsock); + break; + + case ESOCK_TERMINATE_CMD: + /* + * ebuf = {cmd(1)} + */ + exit(EXIT_SUCCESS); + break; + + case ESOCK_CLOSE_CMD: + /* + * ebuf = {cmd(1), fd(4)} + */ + input("4", &fd); + if ((cp = get_connection(fd))) { + DEBUGF(("%s[CLOSE_CMD]: fd = %d\n", + connstr[cp->state], fd)); + if (cp->proxy) + cp->proxy->bp = 1; + switch (cp->state) { + case ESOCK_JOINED: + cp->close = 1; + if (JOINED_STATE_INVALID(cp)) + leave_joined_state(cp); + break; + case ESOCK_SSL_SHUTDOWN: + cp->close = 1; + DEBUGF((" close flag set\n")); + break; + default: + DEBUGF(("-> (removal)\n")); + close_and_remove_connection(cp); + } + } else + DEBUGF(("[CLOSE_CMD]: ERROR: fd = %d not found\n", fd)); + break; + + case ESOCK_SET_SOCKOPT_CMD: + /* + * ebuf = {cmd(1), fd(4), op(1), on(1)} + */ + input("411", &fd, &op, &value); + switch(op) { + case ESOCK_SET_TCP_NODELAY: + if(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, + (void *)&value, sizeof(value)) < 0) { + DEBUGF(("Error: setsockopt TCP_NODELAY\n")); + reply(ESOCK_IOCTL_ERR, "4s", fd, psx_errstr()); + } else { + reply(ESOCK_IOCTL_OK, "4", fd); + } + break; + default: + DEBUGF(("Error: set_sock_opt - Not implemented\n")); + sock_set_errno(ERRNO_OPNOTSUPP); + reply(ESOCK_IOCTL_ERR, "4", fd, psx_errstr()); + break; + } + break; + + case ESOCK_LISTEN_CMD: + /* + * ebuf = {cmd(1), intref(4), lport(2), ipstring(N), 0(1), + * backlog(2), flags(N), 0(1)} + */ + input("42s2s", &intref, &lport, &lipstring, &backlog, + &flags); + DEBUGF(("[LISTEN_CMD] intref = %d, port = %d, " + "ipstring = %s, backlog = %d, flags = %s\n", + intref, lport, lipstring, backlog, flags)); + + listensock = do_listen(lipstring, lport, backlog, &lport); + if(listensock == INVALID_FD) { + reply(ESOCK_LISTEN_SYNC_ERR, "4s", intref, psx_errstr()); + break; + } + cp = new_connection(ESOCK_PASSIVE_LISTENING, listensock); + /* Flags may be an empty string */ + length = strlen(flags); + cp->flags = esock_malloc(length + 1); + strcpy(cp->flags, flags); + + cp->origin = ORIG_LISTEN; + if (esock_ssl_listen_init(cp) < 0) { + DEBUGF(("esock_ssl_listen_init() failed.\n")); + reply(ESOCK_LISTEN_SYNC_ERR, "4s", intref, + ssl_errstr()); + close_and_remove_connection(cp); + break; + } + DEBUGF(("-> PASSIVE_LISTENING (fd = %d)\n", listensock)); + /* Publish listensock */ + reply(ESOCK_LISTEN_REP, "442", intref, listensock, + ntohs(iserv_addr.sin_port)); + break; + + case ESOCK_TRANSPORT_ACCEPT_CMD: + /* + * ebuf = { op(1), fd(4), flags(N), 0(1)} + */ + input("4s", &fd, &flags); + DEBUGF(("[TRANSPORT_ACCEPT_CMD] listenfd = %d, flags = %s\n", fd, + flags)); + cp = get_connection(fd); + if (cp) { + /* We store the flags in the listen socket's + * connection, and overwrite previous flags. + */ + if ((length = strlen(flags)) > 0) { + if (cp->flags) + cp->flags = esock_realloc(cp->flags, + length + 1); + else + cp->flags = esock_malloc(length + 1); + strcpy(cp->flags, flags); + } + if (cp->flags && cp->flags[0] != '\0') { + cp->acceptors++; + cp->state = ESOCK_ACTIVE_LISTENING; + DEBUGF(("-> ACTIVE_LISTENING\n")); + break; + } + DEBUGF(("ERROR: flags empty\n")); + } + reply(ESOCK_TRANSPORT_ACCEPT_ERR, "4s", fd, "ebadf"); + break; + + case ESOCK_SSL_ACCEPT_CMD: + input("4s", &fd, &flags); + DEBUGF(("[SSL_ACCEPT_CMD] fd = %d, flags = %s\n", fd, flags)); + cp = get_connection(fd); + if (cp) + cp->state = ESOCK_SSL_ACCEPT; + //reply(ESOCK_SSL_ACCEPT_REP, "4", fd); + break; + + case ESOCK_NOACCEPT_CMD: + /* + * ebuf = {cmd(1), fd(4)} + */ + input("4", &fd); + DEBUGF(("[NOACCEPT_CMD] listenfd = %d\n", fd)); + cp = get_connection(fd); + if (cp && (--cp->acceptors <= 0)) { + cp->acceptors = 0; + cp->state = ESOCK_PASSIVE_LISTENING; + esock_poll_clear_event(&pollfd, fd); + DEBUGF(("-> PASSIVE_LISTENING\n")); + } + break; + + case ESOCK_PROXY_JOIN_CMD: + /* + * ebuf = {cmd(1), fd(4), portnum(2)} + * + * fd - file descriptor of a connection in state + * CONNECTED + * portnum - port number of the Erlang proxy peer + */ + input("42", &fd, &pport); + cp = get_connection(fd); + pp = get_proxy_by_peerport(pport); + if (cp && cp->state == ESOCK_CONNECTED && pp) { + DEBUGF(("CONNECTED[PROXY_JOIN_CMD] fd = %d " + "portnum = %d\n", fd, pport)); + cp->proxy = pp; + pp->conn = cp; + reply(ESOCK_PROXY_JOIN_REP, "4", fd); + cp->state = ESOCK_JOINED; + DEBUGF(("-> JOINED\n")); + break; + } + if (!cp) { + DEBUGF(("[PROXY_JOIN_CMD] ERROR: No connection " + "having fd = %d\n", fd)); + reply(ESOCK_PROXY_JOIN_ERR, "4s", fd, "ebadsocket"); + } else if (cp->state != ESOCK_CONNECTED) { + DEBUGF(("%s[PROXY_JOIN_CMD] ERROR: Bad state: " + "fd = %d\n", connstr[cp->state], cp->fd)); + reply(ESOCK_PROXY_JOIN_ERR, "4s", fd, "ebadstate"); + } else { + DEBUGF(("ERROR: No proxy: fd = %d, pport = %d\n", + fd, pport)); + if (proxysock_err_cnt > 0) { + proxysock_err_cnt--; + reply(ESOCK_PROXY_JOIN_ERR, "4s", fd, + esock_posix_str(proxysock_last_err)); + } else { + reply(ESOCK_PROXY_JOIN_ERR, "4s", fd, + "enoproxysocket"); + } + cp->state = ESOCK_DEFUNCT; + } + break; + + case ESOCK_DUMP_STATE_CMD: + dump_connections(); + break; + + case ESOCK_SET_DEBUG_CMD: + /* + * ebuf = {cmd(1), debug(1)} + */ + input("1", &debug); + break; + + case ESOCK_SET_DEBUGMSG_CMD: + /* + * ebuf = {cmd(1), debugmsg(1)} + */ + input("1", &debugmsg); + break; + + default: + fprintf(stderr, "esock: default value in loop %c\n", + *ebuf); + exit(EXIT_FAILURE); + break; + } + } + } + + /* Go through all connections that have their file descriptors + set. */ + + /* Note: We may remove the current connection (cp). Thus we + * must be careful not to read cp->next after cp has been + * removed. */ + for (cp = next_polled_conn(connections, &cpnext, &pollfd, set_wq_fds); + cp != NULL; + cp = next_polled_conn(cpnext, &cpnext, &pollfd, set_wq_fds) + ) { + + switch(cp->state) { + + case ESOCK_PASSIVE_LISTENING: + DEBUGF(("-----------------------------------\n")); + fprintf(stderr, "esock: Got connect request while PASSIVE\n"); + exit(EXIT_FAILURE); + break; + + case ESOCK_ACTIVE_LISTENING: + /* new connect from network */ + DEBUGF(("-----------------------------------\n")); + DEBUGF(("ACTIVE_LISTENING - trying to accept on %d\n", + cp->fd)); + length = sizeof(iserv_addr); + msgsock = do_accept(cp->fd, (struct sockaddr*)&iserv_addr, + (int*)&length); + if(msgsock == INVALID_FD) { + DEBUGF(("accept error: %s\n", psx_errstr())); + reply(ESOCK_TRANSPORT_ACCEPT_ERR, "4s", cp->fd, psx_errstr()); + break; + } + SET_NONBLOCKING(msgsock); + if (--cp->acceptors <= 0) { + cp->acceptors = 0; + cp->state = ESOCK_PASSIVE_LISTENING; + DEBUGF(("-> PASSIVE_LISTENING\n")); + } + DEBUGF(("server accepted connection on fd %d\n", msgsock)); + newcp = new_connection(ESOCK_TRANSPORT_ACCEPT, msgsock); + newcp->origin = ORIG_ACCEPT; + reply(ESOCK_TRANSPORT_ACCEPT_REP, "44", cp->fd, msgsock); + newcp->listen_fd = cp->fd; /* Needed for ESOCK_ACCEPT_ERR */ + length = strlen(cp->flags); + /* XXX new flags are not needed */ + newcp->flags = esock_malloc(length + 1); + strcpy(newcp->flags, cp->flags); /* XXX Why? */ + if (esock_ssl_accept_init(newcp, cp->opaque) < 0) { + cp->errstr = ssl_errstr(); + break; + } + newcp->ssl_want = ESOCK_SSL_WANT_READ; + break; + + case ESOCK_SSL_ACCEPT: + /* SSL accept handshake. msgsock is *not* published yet. */ + msgsock = cp->fd; + DEBUGF(("-----------------------------------\n")); + DEBUGF(("SSL_ACCEPT fd = %d\n", msgsock)); + if (cp->errstr != NULL) { /* this means we got an error in ssl_accept_init */ + /* N.B.: The *listen fd* is reported. */ + reply(ESOCK_SSL_ACCEPT_ERR, "4s", msgsock, cp->errstr); + close_and_remove_connection(cp); + break; + } + if (esock_ssl_accept(cp) < 0) { + if (sock_errno() != ERRNO_BLOCK) { + /* Handshake failed. */ + reply(ESOCK_SSL_ACCEPT_ERR, "4s", msgsock, + ssl_errstr()); + DEBUGF(("ERROR: handshake: %s\n", ssl_errstr())); + close_and_remove_connection(cp); + } + } else { + /* SSL handshake successful: publish */ + reply(ESOCK_SSL_ACCEPT_REP, "4", msgsock); + DEBUGF(("-> CONNECTED\n")); + DEBUGF((" Session was %sreused.\n", + (esock_ssl_session_reused(cp)) ? "" : "NOT ")); + cp->state = ESOCK_CONNECTED; + } + break; + + case ESOCK_CONNECTED: + /* Should not happen. We do not read or write until + the connection is in state JOINED. */ + DEBUGF(("-----------------------------------\n")); + DEBUGF(("CONNECTED: Error: should not happen. fd = %d\n", + cp->fd)); + break; + + case ESOCK_JOINED: + /* + * Reading from Proxy, writing to SSL + */ + if (esock_poll_fd_isset_write(&pollfd, cp->fd)) { + /* If there is a write queue, write to ssl only */ + if (cp->wq.len > 0) { + /* The write retry semantics of SSL_write in + * the OpenSSL package is strange. Partial + * writes never occur, only complete writes or + * failures. A failure, however, still + * consumes all data written, although not all + * encrypted data could be written to the + * underlying socket. To retry a write we have + * to provide the same buf and length as in + * the original call, in our case rwbuf and + * the original buffer length. Hence the + * strange memcpy(). Note that wq.offset will + * always be zero when we use OpenSSL. + */ + DEBUGF(("-----------------------------------\n")); + DEBUGF(("JOINED: writing to ssl " + "fd = %d, from write queue only, wc = %d\n", + cp->fd, cp->wq.len - cp->wq.offset)); + memcpy(rwbuf, cp->wq.buf, cp->wq.len - cp->wq.offset); + + /* esock_ssl_write sets cp->eof, cp->bp when return + * value is zero */ + wc = esock_ssl_write(cp, rwbuf, + cp->wq.len - cp->wq.offset); + if (wc < 0) { + if (sock_errno() != ERRNO_BLOCK) { + /* Assume broken SSL pipe */ + DEBUGF(("broken SSL pipe\n")); + cp->bp = 1; + shutdown(cp->proxy->fd, SHUTDOWN_READ); + cp->proxy->eof = 1; + if (JOINED_STATE_INVALID(cp)) { + leave_joined_state(cp); + break; + } + } + } else if (wc == 0) { + /* SSL broken pipe */ + DEBUGF(("broken SSL pipe\n")); + cp->bp = 1; + shutdown(cp->proxy->fd, SHUTDOWN_READ); + cp->proxy->eof = 1; + if (JOINED_STATE_INVALID(cp)) { + leave_joined_state(cp); + break; + } + } else { + cp->wq.offset += wc; + if (cp->wq.offset == cp->wq.len) + cp->wq.len = 0; + } + } + } else if (esock_poll_fd_isset_read(&pollfd, cp->proxy->fd)) { + /* Read from proxy and write to SSL */ + DEBUGF(("-----------------------------------\n")); + DEBUGF(("JOINED: reading from proxy, " + "proxyfd = %d\n", cp->proxy->fd)); + cc = sock_read(cp->proxy->fd, rwbuf, RWBUFLEN); + DEBUGF(("read from proxyfd = %d, cc = %d\n", + cp->proxy->fd, cc)); + if (cc > 0) { + /* esock_ssl_write sets cp->eof, cp->bp when return + * value is zero */ + wc = esock_ssl_write(cp, rwbuf, cc); + if (wc < 0) { + if (sock_errno() != ERRNO_BLOCK) { + /* Assume broken pipe */ + DEBUGF(("broken SSL pipe\n")); + cp->bp = 1; + shutdown(cp->proxy->fd, SHUTDOWN_READ); + cp->proxy->eof = 1; + if (JOINED_STATE_INVALID(cp)) { + leave_joined_state(cp); + break; + } + } else { + /* add to write queue */ + DEBUGF(("adding all to write queue " + "%d bytes\n", cc)); + ensure_write_queue(&cp->wq, cc); + memcpy(cp->wq.buf, rwbuf, cc); + cp->wq.len = cc; + cp->wq.offset = 0; + } + } else if (wc == 0) { + /* Broken SSL pipe */ + DEBUGF(("broken SSL pipe\n")); + cp->bp = 1; + shutdown(cp->proxy->fd, SHUTDOWN_READ); + cp->proxy->eof = 1; + if (JOINED_STATE_INVALID(cp)) { + leave_joined_state(cp); + break; + } + } else if (wc < cc) { + /* add remainder to write queue */ + DEBUGF(("adding remainder to write queue " + "%d bytes\n", cc - wc)); + ensure_write_queue(&cp->wq, cc - wc); + memcpy(cp->wq.buf, rwbuf + wc, cc - wc); + cp->wq.len = cc - wc; + cp->wq.offset = 0; + } + } else { + /* EOF proxy or error */ + DEBUGF(("proxy eof or error %d\n", errno)); + cp->proxy->eof = 1; + if (cp->wq.len == 0) { + esock_ssl_shutdown(cp); + cp->bp = 1; + } + if (JOINED_STATE_INVALID(cp)) { + leave_joined_state(cp); + break; + } + } + } + /* + * Reading from SSL, writing to proxy + */ + if (esock_poll_fd_isset_write(&pollfd, cp->proxy->fd)) { + /* If there is a write queue, write to proxy only */ + if (cp->proxy->wq.len > 0) { + DEBUGF(("-----------------------------------\n")); + DEBUGF(("JOINED: writing to proxyfd = %d, " + "from write queue only, wc = %d\n", + cp->proxy->fd, cp->proxy->wq.len - + cp->proxy->wq.offset)); + wc = sock_write(cp->proxy->fd, cp->proxy->wq.buf + + cp->proxy->wq.offset, + cp->proxy->wq.len - + cp->proxy->wq.offset); + if (wc < 0) { + if (sock_errno() != ERRNO_BLOCK) { + /* Assume broken pipe */ + DEBUGF(("broken proxy pipe\n")); + cp->proxy->bp = 1; + /* There is no SSL shutdown for read */ + cp->eof = 1; + if (JOINED_STATE_INVALID(cp)) { + leave_joined_state(cp); + break; + } + } + } else { + cp->proxy->wq.offset += wc; + if (cp->proxy->wq.offset == cp->proxy->wq.len) + cp->proxy->wq.len = 0; + } + } + } else if (esock_poll_fd_isset_read(&pollfd, cp->fd)) { + /* Read from SSL and write to proxy */ + DEBUGF(("-----------------------------------\n")); + DEBUGF(("JOINED: read from ssl fd = %d\n", + cp->fd)); + cc = esock_ssl_read(cp, rwbuf, RWBUFLEN); + DEBUGF(("read from fd = %d, cc = %d\n", cp->fd, cc)); + if (cc > 0) { + wc = sock_write(cp->proxy->fd, rwbuf, cc); + if (wc < 0) { + if (sock_errno() != ERRNO_BLOCK) { + DEBUGF(("broken proxy pipe\n")); + /* Assume broken pipe */ + cp->proxy->bp = 1; + /* There is no SSL shutdown for read */ + cp->eof = 1; + if (JOINED_STATE_INVALID(cp)) { + leave_joined_state(cp); + break; + } + } else { + /* add all to write queue */ + DEBUGF(("adding to write queue %d bytes\n", + cc)); + ensure_write_queue(&cp->proxy->wq, cc); + memcpy(cp->proxy->wq.buf, rwbuf, cc); + cp->proxy->wq.len = cc; + cp->proxy->wq.offset = 0; + } + } else if (wc < cc) { + /* add to write queue */ + DEBUGF(("adding to write queue %d bytes\n", + cc - wc)); + ensure_write_queue(&cp->proxy->wq, cc - wc); + memcpy(cp->proxy->wq.buf, rwbuf + wc, cc - wc); + cp->proxy->wq.len = cc - wc; + cp->proxy->wq.offset = 0; + } + } else if (cc == 0) { + /* SSL eof */ + DEBUGF(("SSL eof\n")); + cp->eof = 1; + if (cp->proxy->wq.len == 0) { + shutdown(cp->proxy->fd, SHUTDOWN_WRITE); + cp->proxy->bp = 1; + } + if (JOINED_STATE_INVALID(cp)) { + leave_joined_state(cp); + break; + } + } else { + /* This may very well happen when reading from SSL. */ + DEBUGF(("NOTE: readmask set, cc < 0, fd = %d, " + "is ok\n", cp->fd)); + } + } + break; + + case ESOCK_SSL_SHUTDOWN: + DEBUGF(("-----------------------------------\n")); + DEBUGF(("SSL_SHUTDOWN: fd = %d\n", cp->fd)); + do_shutdown(cp); + break; + + case ESOCK_DEFUNCT: + DEBUGF(("-----------------------------------\n")); + DEBUGF(("DEFUNCT: ERROR: should not happen. fd = %d\n", + cp->fd)); + break; + + case ESOCK_WAIT_CONNECT: + /* New connection shows up */ + connectsock = cp->fd;/* Is published */ + DEBUGF(("-----------------------------------\n")); + DEBUGF(("WAIT_CONNECT fd = %d\n", connectsock)); + + /* If the connection did succeed it's possible to + * fetch the peer name (UNIX); or failure shows in + * exceptmask (WIN32). Sorry for the mess below, but + * we have to have balanced paren's in #ifdefs in + * order not to confuse Emacs' indentation. */ + length = sizeof(iserv_addr); + if ( +#ifdef __WIN32__ + esock_poll_fd_isset_exception(&pollfd, connectsock) +#else + getpeername(connectsock, (struct sockaddr *)&iserv_addr, + &length) < 0 +#endif + ) { + sock_set_errno(ERRNO_CONNREFUSED); + DEBUGF(("connect error: %s\n", psx_errstr())); + reply(ESOCK_CONNECT_ERR, "4s", connectsock, psx_errstr()); + cp->state = ESOCK_DEFUNCT; + break; + } + if (esock_ssl_connect_init(cp) < 0) { + DEBUGF(("esock_ssl_connect_init() failed\n")); + reply(ESOCK_CONNECT_ERR, "4s", connectsock, ssl_errstr()); + cp->state = ESOCK_DEFUNCT; + break; + } + DEBUGF(("-> SSL_CONNECT\n")); + cp->state = ESOCK_SSL_CONNECT; + cp->ssl_want = ESOCK_SSL_WANT_WRITE; + break; + + case ESOCK_SSL_CONNECT: + /* SSL connect handshake. connectsock is published. */ + connectsock = cp->fd; + DEBUGF(("-----------------------------------\n")); + DEBUGF(("SSL_CONNECT fd = %d\n", connectsock)); + if (esock_ssl_connect(cp) < 0) { + if (sock_errno() != ERRNO_BLOCK) { + /* Handshake failed */ + DEBUGF(("ERROR: handshake: %s\n", ssl_errstr())); + reply(ESOCK_CONNECT_ERR, "4s", connectsock, + ssl_errstr()); + cp->state = ESOCK_DEFUNCT; + } + } else { + /* SSL connect handshake successful */ + DEBUGF(("-> CONNECTED\n")); + reply(ESOCK_CONNECT_REP, "4", connectsock); + cp->state = ESOCK_CONNECTED; + } + break; + + default: + DEBUGF(("ERROR: Connection in unknown state.\n")); + } + } + } +} + +static int set_poll_conns(Connection *cp, EsockPoll *ep, int verbose) +{ + int i = 0; + + if (verbose) + DEBUGF(("MASKS SET FOR FD: ")); + while (cp) { + switch (cp->state) { + case ESOCK_ACTIVE_LISTENING: + if (verbose) + DEBUGF(("%d (read) ", cp->fd)); + esock_poll_fd_set_read(ep, cp->fd); + break; + case ESOCK_WAIT_CONNECT: + if (verbose) + DEBUGF(("%d (write) ", cp->fd)); + esock_poll_fd_set_write(ep, cp->fd); +#ifdef __WIN32__ + esock_poll_fd_set_exception(ep, cp->fd); /* Failure shows in exceptions */ +#endif + break; + case ESOCK_SSL_CONNECT: + case ESOCK_SSL_ACCEPT: + if (cp->ssl_want == ESOCK_SSL_WANT_READ) { + if (verbose) + DEBUGF(("%d (read) ", cp->fd)); + esock_poll_fd_set_read(ep, cp->fd); + } else if (cp->ssl_want == ESOCK_SSL_WANT_WRITE) { + if (verbose) + DEBUGF(("%d (write) ", cp->fd)); + esock_poll_fd_set_write(ep, cp->fd); + } + break; + case ESOCK_JOINED: + if (!cp->bp) { + if (cp->wq.len) { + if (verbose) + DEBUGF(("%d (write) ", cp->fd)); + esock_poll_fd_set_write(ep, cp->fd); + } else if (!cp->proxy->eof) { + if (verbose) + DEBUGF(("%d (read) ", cp->proxy->fd)); + esock_poll_fd_set_read(ep, cp->proxy->fd); + } + } + if (!cp->proxy->bp) { + if (cp->proxy->wq.len) { + if (verbose) + DEBUGF(("%d (write) ", cp->proxy->fd)); + esock_poll_fd_set_write(ep, cp->proxy->fd); + } else if (!cp->eof) { + if (verbose) + DEBUGF(("%d (read) ", cp->fd)); + esock_poll_fd_set_read(ep, cp->fd); + } + } + break; + case ESOCK_SSL_SHUTDOWN: + if (cp->ssl_want == ESOCK_SSL_WANT_READ) { + if (verbose) + DEBUGF(("%d (read) ", cp->fd)); + esock_poll_fd_set_read(ep, cp->fd); + } else if (cp->ssl_want == ESOCK_SSL_WANT_WRITE) { + if (verbose) + DEBUGF(("%d (write) ", cp->fd)); + esock_poll_fd_set_write(ep, cp->fd); + } + break; + default: + break; + } + i++; + cp = cp->next; + } + if (verbose) + DEBUGF(("\n")); + return i; +} + + +static Connection *next_polled_conn(Connection *cp, Connection **cpnext, + EsockPoll *ep, int set_wq_fds) +{ + while(cp) { + if (esock_poll_fd_isset_read(ep, cp->fd) || + (cp->proxy && esock_poll_fd_isset_read(ep, cp->proxy->fd)) || + (esock_poll_fd_isset_write(ep, cp->fd)) || + (cp->proxy && esock_poll_fd_isset_write(ep, cp->proxy->fd)) +#ifdef __WIN32__ + || esock_poll_fd_isset_exception(ep, cp->fd) /* Connect failure in WIN32 */ +#endif + || (set_wq_fds && (cp->wq.len || + (cp->proxy && cp->proxy->wq.len))) + || cp->errstr != NULL) { + *cpnext = cp->next; + return cp; + } + cp = cp->next; + } + *cpnext = NULL; + return NULL; +} + +static void leave_joined_state(Connection *cp) +{ + shutdown(cp->proxy->fd, SHUTDOWN_ALL); + if (((cp->bp || cp->eof) && cp->clean) || + (!cp->bp && !cp->eof)) { + DEBUGF(("-> SSL_SHUTDOWN\n")); + cp->state = ESOCK_SSL_SHUTDOWN; + cp->ssl_want = ESOCK_SSL_WANT_WRITE; + do_shutdown(cp); + } else if (cp->close) { + DEBUGF(("-> (removal)\n")); + close_and_remove_connection(cp); + } else { + DEBUGF(("-> DEFUNCT\n")); + cp->state = ESOCK_DEFUNCT; + } +} + +/* We are always in state SHUTDOWN here */ +static void do_shutdown(Connection *cp) +{ + int ret; + + ret = esock_ssl_shutdown(cp); + if (ret < 0) { + if (sock_errno() == ERRNO_BLOCK) { + return; + } else { + /* Something is wrong -- close and remove or move to DEFUNCT */ + DEBUGF(("Error in SSL shutdown\n")); + if (cp->close) { + DEBUGF(("-> (removal)\n")); + close_and_remove_connection(cp); + } else { + DEBUGF(("-> DEFUNCT\n")); + cp->state = ESOCK_DEFUNCT; + } + } + } else if (ret == 0) { + /* `close_notify' has been sent. Wait for reception of + same. */ + return; + } else if (ret == 1) { + /* `close_notify' has been sent, and received. */ + if (cp->close) { + DEBUGF(("-> (removal)\n")); + close_and_remove_connection(cp); + } else { + DEBUGF(("-> DEFUNCT\n")); + cp->state = ESOCK_DEFUNCT; + } + } +} + +static void close_and_remove_connection(Connection *cp) +{ + safe_close(cp->fd); + remove_connection(cp); +} + +static int reply(int cmd, char *fmt, ...) +{ + static unsigned char replybuf[MAXREPLYBUF]; + unsigned char *buf = replybuf; + va_list args; + int len; + + va_start(args, fmt); + len = put_pars(NULL, fmt, args); + va_end(args); + len++; + if (len > sizeof(replybuf)) + buf = esock_malloc(len); + + PUT_INT8(cmd, buf); + va_start(args, fmt); + (void) put_pars(buf + 1, fmt, args); + va_end(args); + write_ctrl(buf, len); + if (buf != replybuf) + esock_free(buf); + return len; +} + +static int input(char *fmt, ...) +{ + va_list args; + int len; + + va_start(args, fmt); + len = get_pars(ebuf + 1, fmt, args); + va_end(args); + return len + 1; +} + +static int put_pars(unsigned char *buf, char *fmt, va_list args) +{ + char *s, *str, *bin; + int val, len, pos = 0; + + s = fmt; + while (*s) { + switch (*s) { + case '1': + val = va_arg(args, int); + if (buf) + PUT_INT8(val, buf + pos); + pos++; + break; + case '2': + val = va_arg(args, int); + if (buf) + PUT_INT16(val, buf + pos); + pos += 2; + break; + case '4': + val = va_arg(args, int); + if (buf) + PUT_INT32(val, buf + pos); + pos += 4; + break; + case 's': /* string */ + str = va_arg(args, char *); + if (buf) + strcpy((char *)(buf + pos), str); + pos += strlen(str) + 1; + break; + case 'b': /* binary */ + len = va_arg(args, int); + if (buf) + PUT_INT32(len, buf + pos); + pos += 4; + bin = va_arg(args, char *); + if (buf) + memcpy(buf + pos, bin, len); + pos += len; + break; + default: + fprintf(stderr, "esock: Invalid format character: %c\n", *s); + exit(EXIT_FAILURE); + break; + } + s++; + } + return pos; +} + + +static int get_pars(unsigned char *buf, char *fmt, va_list args) +{ + int *ip; + char *s, **strp, **bin; + int pos = 0; + + s = fmt; + while (*s) { + switch (*s) { + case '1': + ip = va_arg(args, int *); + *ip = GET_INT8(buf + pos); + pos++; + break; + case '2': + ip = va_arg(args, int *); + *ip = GET_INT16(buf + pos); + pos += 2; + break; + case '4': + ip = va_arg(args, int *); + *ip = GET_INT32(buf + pos); + pos += 4; + break; + case 's': + strp = va_arg(args, char **); + *strp = (char *)(buf + pos); + pos += strlen(*strp) + 1; + break; + case 'b': + ip = va_arg(args, int *); + *ip = GET_INT32(buf + pos); + pos += 4; + bin = va_arg(args, char **); + *bin = (char *)(buf + pos); + pos += *ip; + break; + default: + fprintf(stderr, "esock: Invalid format character: %c\n", *s); + exit(EXIT_FAILURE); + break; + } + s++; + } + return pos; +} + +static FD do_connect(char *lipstring, int lport, char *fipstring, int fport) +{ + struct sockaddr_in sock_addr; + long inaddr; + FD fd; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_FD) { + DEBUGF(("Error calling socket()\n")); + return fd; + } + if (check_num_sock_fds(fd) < 0) + return INVALID_FD; + DEBUGF((" fd = %d\n", fd)); + + /* local */ + if ((inaddr = inet_addr(lipstring)) == INADDR_NONE) { + DEBUGF(("Error in inet_addr(): lipstring = %s\n", lipstring)); + safe_close(fd); + sock_set_errno(ERRNO_ADDRNOTAVAIL); + return INVALID_FD; + } + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = inaddr; + sock_addr.sin_port = htons(lport); + if(bind(fd, (struct sockaddr*) &sock_addr, sizeof(sock_addr)) < 0) { + DEBUGF(("Error in bind()\n")); + safe_close(fd); + /* XXX Set error code for bind error */ + return INVALID_FD; + } + + /* foreign */ + if ((inaddr = inet_addr(fipstring)) == INADDR_NONE) { + DEBUGF(("Error in inet_addr(): fipstring = %s\n", fipstring)); + safe_close(fd); + sock_set_errno(ERRNO_ADDRNOTAVAIL); + return INVALID_FD; + } + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = inaddr; + sock_addr.sin_port = htons(fport); + + SET_NONBLOCKING(fd); + + if(connect(fd, (struct sockaddr*)&sock_addr, sizeof(sock_addr)) < 0) { + if (sock_errno() != ERRNO_PROGRESS && /* UNIX */ + sock_errno() != ERRNO_BLOCK) { /* WIN32 */ + DEBUGF(("Error in connect()\n")); + safe_close(fd); + return INVALID_FD; + } + } + return fd; +} + +static FD do_listen(char *ipstring, int lport, int backlog, int *aport) +{ + static int one = 1; /* Type must be int, not long */ + struct sockaddr_in sock_addr; + long inaddr; + int length; + FD fd; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_FD) { + DEBUGF(("Error calling socket()\n")); + return fd; + } + if (check_num_sock_fds(fd) < 0) + return INVALID_FD; + DEBUGF((" fd = %d\n", fd)); + if ((inaddr = inet_addr(ipstring)) == INADDR_NONE) { + DEBUGF(("Error in inet_addr(): ipstring = %s\n", ipstring)); + safe_close(fd); + sock_set_errno(ERRNO_ADDRNOTAVAIL); + return INVALID_FD; + } + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = inaddr; + sock_addr.sin_port = htons(lport); + + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one)); + + if(bind(fd, (struct sockaddr*) &sock_addr, sizeof(sock_addr)) < 0) { + DEBUGF(("Error in bind()\n")); + safe_close(fd); + return INVALID_FD; + } + if (listen(fd, backlog) < 0) { + DEBUGF(("Error in listen()\n")); + safe_close(fd); + return INVALID_FD; + } + /* find out assigned local port number */ + length = sizeof(sock_addr); + if (getsockname(fd, (struct sockaddr *)&sock_addr, &length) < 0) { + DEBUGF(("Error in getsockname()\n")); + safe_close(fd); + return INVALID_FD; + } + if (aport) + *aport = ntohs(sock_addr.sin_port); + return fd; +} + +static FD do_accept(FD listensock, struct sockaddr *saddr, int *len) +{ + FD fd; + + if ((fd = accept(listensock, saddr, len)) == INVALID_FD) { + DEBUGF(("Error calling accept()\n")); + return fd; + } + if (check_num_sock_fds(fd) < 0) + return INVALID_FD; + return fd; +} + +static Connection *new_connection(int state, FD fd) +{ + Connection *cp; + + if (!(cp = esock_malloc(sizeof(Connection)))) + return NULL; + cp->state = state; + cp->acceptors = 0; + cp->fd = fd; + cp->listen_fd = INVALID_FD; + cp->proxy = NULL; + cp->opaque = NULL; + cp->ssl_want = 0; + cp->eof = 0; + cp->bp = 0; + cp->clean = 0; /* XXX Used? */ + cp->close = 0; + cp->origin = -1; + cp->flags = NULL; + cp->logfp = NULL; + cp->wq.size = 0; + cp->wq.buf = NULL; + cp->wq.len = 0; + cp->wq.offset = 0; + cp->next = connections; + cp->errstr = NULL; + connections = cp; + return cp; +} + + +static void print_connections(void) +{ + if (debug) { + Connection *cp = connections; + DEBUGF(("CONNECTIONS:\n")); + while (cp) { + if (cp->state == ESOCK_JOINED) { + DEBUGF((" - %s [%8p] (origin = %s)\n" + " (fd = %d, eof = %d, wq = %d, bp = %d)\n" + " (proxyfd = %d, eof = %d, wq = %d, bp = %d)\n", + connstr[cp->state], cp, originstr[cp->origin], + cp->fd, cp->eof, cp->wq.len, cp->bp, + cp->proxy->fd, cp->proxy->eof, cp->proxy->wq.len, + cp->proxy->bp)); + } else if (cp->state == ESOCK_ACTIVE_LISTENING) { + DEBUGF((" - %s [%8p] (fd = %d, acceptors = %d)\n", + connstr[cp->state], cp, cp->fd, cp->acceptors)); + } else { + DEBUGF((" - %s [%8p] (fd = %d)\n", connstr[cp->state], cp, + cp->fd)); + } + cp= cp->next; + } + } +} + +static void dump_connections(void) +{ + Connection *cp = connections; + Proxy *pp = proxies; + time_t t = time(NULL); + int length = 0; + struct sockaddr_in iserv_addr; + + __debugprintf("CONNECTIONS %s", ctime(&t)); + while (cp) { + if (cp->state == ESOCK_JOINED) { + __debugprintf(" - %s [%8p] (origin = %s)\n" + " (fd = %d, eof = %d, wq = %d, bp = %d), close = %d\n" + " (proxyfd = %d, eof = %d, wq = %d, bp = %d)\n", + connstr[cp->state], cp, originstr[cp->origin], + cp->fd, cp->eof, cp->wq.len, cp->bp, cp->close, + cp->proxy->fd, cp->proxy->eof, cp->proxy->wq.len, + cp->proxy->bp); + } else if (cp->state == ESOCK_ACTIVE_LISTENING) { + __debugprintf(" - %s [%8p] (fd = %d, acceptors = %d)\n", + connstr[cp->state], cp, cp->fd, cp->acceptors); + } else { + __debugprintf(" - %s [%8p] (fd = %d)\n", connstr[cp->state], cp, + cp->fd); + } + length = sizeof(iserv_addr); + if ((cp->state == ESOCK_ACTIVE_LISTENING) || + (cp->state == ESOCK_PASSIVE_LISTENING)) { + getsockname(cp->fd, (struct sockaddr *) &iserv_addr, &length); + __debugprintf(" (ip = %s, port = %d)\n", + inet_ntoa(iserv_addr.sin_addr), + ntohs(iserv_addr.sin_port)); + } + else { + getsockname(cp->fd, (struct sockaddr *) &iserv_addr, &length); + __debugprintf(" (local_ip = %s, local_port = %d)\n", + inet_ntoa(iserv_addr.sin_addr), + ntohs(iserv_addr.sin_port)); + length = sizeof(iserv_addr); + getpeername(cp->fd, (struct sockaddr *) &iserv_addr, &length); + __debugprintf(" (remote_ip = %s, remote_port = %d)\n", + inet_ntoa(iserv_addr.sin_addr), + ntohs(iserv_addr.sin_port)); + } + cp=cp->next; + } + + __debugprintf("PROXIES\n"); + while (pp) { + __debugprintf(" - fd = %d [%8p] (external_fd = %d, peer_port = %d," + " eof = %d)\n", pp->fd, pp, pp->conn->fd, pp->peer_port, + pp->eof); + + pp= pp->next; + } +} + +static Connection *get_connection(FD fd) +{ + Connection *cp = connections; + + while(cp) { + if(cp->fd == fd) + return cp; + cp = cp->next; + } + return NULL; +} + +/* + * Remove a connection from the list of connection, close the proxy + * socket and free all resources. The main socket (fd) is *not* + * closed here, because the closing of that socket has to be synchronized + * with the Erlang process controlling this port program. + */ +static void remove_connection(Connection *conn) +{ + Connection **prev = &connections; + Connection *cp = connections; + + while (cp) { + if(cp == conn) { + DEBUGF(("remove_connection: fd = %d\n", cp->fd)); + esock_ssl_free(cp); /* frees cp->opaque only */ + esock_free(cp->flags); + closelog(cp->logfp); /* XXX num_sock_fds */ + esock_free(cp->wq.buf); + if (cp->proxy) { + safe_close(cp->proxy->fd); + remove_proxy(cp->proxy); + } + *prev = cp->next; + esock_free(cp); + return; + } + prev = &cp->next; + cp = cp->next; + } +} + +static Proxy *get_proxy_by_peerport(int port) +{ + Proxy *p = proxies; + + while(p) { + if (p->peer_port == port) + return p; + p = p->next; + } + return NULL; +} + +static Proxy *new_proxy(FD fd) +{ + Proxy *p; + + if (!(p = esock_malloc(sizeof(Proxy)))) + return NULL; + + p->fd = fd; + p->peer_port = -1; + p->eof = 0; + p->bp = 0; + p->conn = NULL; + p->wq.size = 0; + p->wq.buf = NULL; + p->wq.len = 0; + p->wq.offset = 0; + p->next = proxies; + proxies = p; + return p; +} + +static void remove_proxy(Proxy *proxy) +{ + Proxy *p = proxies, **pp = &proxies; + + while(p) { + if (p == proxy) { + DEBUGF(("remove_proxyfd = %d\n", p->fd)); + esock_free(p->wq.buf); + *pp = p->next; + esock_free(p); + return; + } + pp = &p->next; + p = p->next; + } +} + +static int check_num_sock_fds(FD fd) +{ + num_sock_fds++; /* fd is valid */ +#ifdef USE_SELECT + if (num_sock_fds > FD_SETSIZE) { + num_sock_fds--; + sock_set_errno(ERRNO_MFILE); + safe_close(fd); + return -1; + } +#endif + return 0; +} + +static void safe_close(FD fd) +{ + int err; + + err = sock_errno(); + DEBUGF(("safe_close fd = %d\n", fd)); + if (sock_close(fd) < 0) { + DEBUGF(("safe_close failed\n")); + } else { + num_sock_fds--; + } + sock_set_errno(err); +} + +static void clean_up(void) +{ + Connection *cp, *cpnext; + Proxy *pp, *ppnext; + + cp = connections; + while (cp) { + safe_close(cp->fd); + cpnext = cp->next; + remove_connection(cp); + cp = cpnext; + } + + pp = proxies; + while (pp) { + safe_close(pp->fd); + ppnext = pp->next; + remove_proxy(pp); + pp = ppnext; + } +} + +static void ensure_write_queue(WriteQueue *wq, int size) +{ + if (wq->size < size) { + wq->buf = esock_realloc(wq->buf, size); + wq->size = size; + } +} + + + + + + + diff --git a/lib/ssl/c_src/esock.h b/lib/ssl/c_src/esock.h new file mode 100644 index 0000000000..16c9faa530 --- /dev/null +++ b/lib/ssl/c_src/esock.h @@ -0,0 +1,273 @@ +/*<copyright> + * <year>1999-2008</year> + * <holder>Ericsson AB, All Rights Reserved</holder> + *</copyright> + *<legalnotice> + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson AB. + *</legalnotice> + */ +/* + * Purpose: Implementation of Secure Socket Layer (SSL). + * + */ + +#ifndef ESOCK_H +#define ESOCK_H + +#ifdef __WIN32__ +#include "esock_winsock.h" +#endif +#include <stdio.h> + +#ifdef __WIN32__ +#define INVALID_FD INVALID_SOCKET + +#define sock_read(fd, buf, len) recv((fd), (buf), (len), 0) +#define sock_write(fd, buf, len) send((fd), (buf), (len), 0) +#define sock_close(fd) closesocket(fd) +#define sock_errno() WSAGetLastError() +#define sock_set_errno(err) WSASetLastError(err) + +#define ERRNO_NONE 0 +#define ERRNO_BLOCK WSAEWOULDBLOCK +#define ERRNO_CONNREFUSED WSAECONNREFUSED +#define ERRNO_PROGRESS WSAEINPROGRESS +#define ERRNO_PROTONOSUPPORT WSAEPROTONOSUPPORT +#define ERRNO_INVAL WSAEINVAL +#define ERRNO_ADDRNOTAVAIL WSAEADDRNOTAVAIL +#define ERRNO_NOTSOCK WSAENOTSOCK +#define ERRNO_OPNOTSUPP WSAEOPNOTSUPP +#define ERRNO_MFILE WSAEMFILE +#define SET_BLOCKING(fd) do { \ + unsigned long zeroval = 0; \ + ioctlsocket((fd), FIONBIO, &zeroval); \ + } while (0) +#define SET_NONBLOCKING(fd) do { \ + unsigned long oneval = 1; \ + ioctlsocket((fd), FIONBIO, &oneval); \ + } while (0) +#else +#define INVALID_FD (-1) + +#define sock_read(fd, buf, len) read((fd), (buf), (len)) +#define sock_write(fd, buf, len) write((fd), (buf), (len)) +#define sock_close(fd) close(fd) +#define sock_errno() errno +#define sock_set_errno(err) do {errno = (err);} while(0) + +#define ERRNO_NONE 0 +#define ERRNO_BLOCK EAGAIN +#define ERRNO_CONNREFUSED ECONNREFUSED +#define ERRNO_PROGRESS EINPROGRESS +#define ERRNO_PROTONOSUPPORT EPROTONOSUPPORT +#define ERRNO_INVAL EINVAL +#define ERRNO_ADDRNOTAVAIL EADDRNOTAVAIL +#define ERRNO_NOTSOCK ENOTSOCK +#define ERRNO_OPNOTSUPP EOPNOTSUPP +#define ERRNO_MFILE EMFILE +#define SET_BLOCKING(fd) fcntl((fd), F_SETFL, \ + fcntl((fd), F_GETFL, 0) & ~O_NONBLOCK) +#define SET_NONBLOCKING(fd) fcntl((fd), F_SETFL, \ + fcntl((fd), F_GETFL, 0) | O_NONBLOCK) +#endif + +#define GET_INT8(s) ((s)[0]) +#define GET_INT16(s) (((s)[0] << 8) | (s)[1]) +#define GET_INT32(s) (((s)[0] << 24) | ((s)[1] << 16) | \ + ((s)[2] << 8) | (s)[3]) + +#define PUT_INT8(x, s) do { (s)[0] = x; } while(0) +#define PUT_INT16(x, s) do { (s)[0] = ((x) >> 8) & 0xff; \ + (s)[1] = ((x) & 0xff); } while(0) +#define PUT_INT32(x, s) do { (s)[0] = ((x) >> 24) & 0xff; \ + (s)[1] = ((x) >> 16) & 0xff; \ + (s)[2] = ((x) >> 8) & 0xff; \ + (s)[3] = (x) & 0xff; } while(0) + +/* type for Connections */ +#define ESOCK_STATE_NONE 0 +#define ESOCK_ACTIVE_LISTENING 1 +#define ESOCK_PASSIVE_LISTENING 2 +#define ESOCK_CONNECTED 3 +#define ESOCK_WAIT_CONNECT 4 +#define ESOCK_SSL_CONNECT 5 +#define ESOCK_SSL_ACCEPT 6 +#define ESOCK_TRANSPORT_ACCEPT 7 +#define ESOCK_JOINED 8 +#define ESOCK_SSL_SHUTDOWN 9 +#define ESOCK_DEFUNCT 10 + +#ifdef __WIN32__ + typedef SOCKET FD; +#else + typedef int FD; +#endif + +/* For the shutdown(fd, how) call */ +#ifdef __WIN32__ +#define SHUTDOWN_READ SD_RECEIVE +#define SHUTDOWN_WRITE SD_SEND +#define SHUTDOWN_ALL SD_BOTH +#else +#define SHUTDOWN_READ 0 +#define SHUTDOWN_WRITE 1 +#define SHUTDOWN_ALL 2 +#endif + +#define ORIG_LISTEN 0 +#define ORIG_ACCEPT 1 +#define ORIG_CONNECT 2 + +typedef struct { + int size; /* Total size of buf */ + unsigned char *buf; + int len; /* Current number of bytes in buf */ + int offset; /* Bytes already written */ +} WriteQueue; + +typedef struct _proxy Proxy; + +typedef struct Connection { + FD fd; + FD listen_fd; /* Needed for async listen error */ + unsigned char state; + int acceptors; /* Count acceptors for listen socket */ + Proxy *proxy; + void *opaque; /* Any suitable ssl structure */ + int ssl_want; /* read/write flags */ + int eof; /* end of file (read) */ + int bp; /* broken pipe (write) */ + int clean; /* Clean SSL shutdown initiated */ + int close; /* Close if set */ + int origin; /* listen, accept or connect */ + int encrypted; /* 1 = SSL encrypted, 0 = normal, unencrypted tcp */ + char *flags; /* ssl parameters */ + FILE *logfp; /* connection log file (not used) */ + WriteQueue wq; + struct Connection* next; + const char* errstr; /* only used to report errors from ssl_accept_init in SSL_ACCEPT */ +} Connection; + +struct _proxy { + FD fd; + int peer_port; + int eof; /* end of file (read) */ + int bp; /* broken pipe (write) */ + Connection *conn; + WriteQueue wq; + Proxy *next; +}; + +/* Commands, replies, and error responses */ + +#define ESOCK_CONNECT_CMD 1 +#define ESOCK_CONNECT_WAIT_REP 2 +#define ESOCK_CONNECT_REP 3 +#define ESOCK_CONNECT_ERR 4 + +#define ESOCK_TERMINATE_CMD 5 +#define ESOCK_CLOSE_CMD 6 + +#define ESOCK_LISTEN_CMD 7 +#define ESOCK_LISTEN_REP 8 +#define ESOCK_LISTEN_ERR 9 + +#define ESOCK_TRANSPORT_ACCEPT_CMD 10 +#define ESOCK_NOACCEPT_CMD 11 +#define ESOCK_TRANSPORT_ACCEPT_REP 12 +#define ESOCK_TRANSPORT_ACCEPT_ERR 13 + +#define ESOCK_FROMNET_CLOSE_REP 14 + +#define ESOCK_CONNECT_SYNC_ERR 15 +#define ESOCK_LISTEN_SYNC_ERR 16 + +#define ESOCK_PROXY_PORT_REP 23 +#define ESOCK_PROXY_JOIN_CMD 24 +#define ESOCK_PROXY_JOIN_REP 25 +#define ESOCK_PROXY_JOIN_ERR 26 + +#define ESOCK_SET_SOCKOPT_CMD 27 +#define ESOCK_IOCTL_OK 28 +#define ESOCK_IOCTL_ERR 29 + +#define ESOCK_GETPEERNAME_CMD 30 +#define ESOCK_GETPEERNAME_REP 31 +#define ESOCK_GETPEERNAME_ERR 32 + +#define ESOCK_GETSOCKNAME_CMD 33 +#define ESOCK_GETSOCKNAME_REP 34 +#define ESOCK_GETSOCKNAME_ERR 35 + +#define ESOCK_GETPEERCERT_CMD 36 +#define ESOCK_GETPEERCERT_REP 37 +#define ESOCK_GETPEERCERT_ERR 38 + +#define ESOCK_GETVERSION_CMD 39 +#define ESOCK_GETVERSION_REP 40 + +#define ESOCK_SET_SEED_CMD 41 + +#define ESOCK_GETCONNINFO_CMD 42 +#define ESOCK_GETCONNINFO_REP 43 +#define ESOCK_GETCONNINFO_ERR 44 + +#define ESOCK_SSL_ACCEPT_CMD 45 +#define ESOCK_SSL_ACCEPT_REP 46 +#define ESOCK_SSL_ACCEPT_ERR 47 + +#define ESOCK_DUMP_STATE_CMD 48 +#define ESOCK_SET_DEBUG_CMD 49 +#define ESOCK_SET_DEBUGMSG_CMD 50 + + +/* Option codes for ESOCK_SET_SOCKOPT_CMD */ +#define ESOCK_SET_TCP_NODELAY 1 + +/* SSL want to read or write */ +#define ESOCK_SSL_WANT_READ 1 +#define ESOCK_SSL_WANT_WRITE 2 + +/* Protocol version according to ssl_server */ +#define ESOCK_SSLv2 1 +#define ESOCK_SSLv3 2 +#define ESOCK_TLSv1 4 + + +#endif + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/ssl/c_src/esock_openssl.c b/lib/ssl/c_src/esock_openssl.c new file mode 100644 index 0000000000..2621c9934e --- /dev/null +++ b/lib/ssl/c_src/esock_openssl.c @@ -0,0 +1,1213 @@ +/*<copyright> + * <year>1999-2008</year> + * <holder>Ericsson AB, All Rights Reserved</holder> + *</copyright> + *<legalnotice> + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson AB. + *</legalnotice> + */ +/* + * Purpose: Adaptions for the OpenSSL package. + * + * This file implements the functions defined in esock_ssl.h for + * the OpenSSL package. + * + * The following holds true for non-blockling I/O: + * + * Function Return values + * -------- ------------- + * SSL_accept() success: 1, failure: =<0 + * SSL_connect() success: 1, failure: =<0 + * SSL_read() success: >0, eof: 0, failure: <0 + * SSL_write() success: > 0, failure: =<0 + * SSL_shutdown() success: 1, not finished: 0 + * + * If the return value of any of the above functions is `ret' and the + * ssl connection is `ssl', the call + * + * ssl_error = SSL_get_error(ssl, ret); + * + * returns one of the following eight values: + * + * SSL_ERROR_NONE ret > 0 + * SSL_ERROR_ZERO_RETURN ret = 0 + * SSL_ERROR_WANT_READ ret < 0 and ssl wants to read + * SSL_ERROR_WANT_WRITE ret < 0 and ssl wants to write + * SSL_ERROR_SYSCALL ret < 0 or ret = 0 + * SSL_ERROR_SSL if there was an ssl internal error + * SSL_ERROR_WANT_X509_LOOKUP ret < 0 and ssl wants x509 lookup + * SSL_ERROR_WANT_CONNECT ret < 0 and ssl wants connect + * + * It is the case that SSL_read() sometimes returns -1, even when the + * underlying file descriptor is ready for reading. + * + * Also, sometimes we may have SSL_ERROR_SSL in SSL_accept() and SSL_connect() + * when a retry should be done. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#ifndef __WIN32__ +# include <fcntl.h> +# include <unistd.h> +#endif + +#include "esock.h" +#include "esock_ssl.h" +#include "debuglog.h" +#include "esock_utils.h" +#include "esock_posix_str.h" + +#include <openssl/crypto.h> +#include <openssl/ssl.h> +#include <openssl/err.h> +#include <openssl/rand.h> + +int ephemeral_rsa = 0; +int ephemeral_dh = 0; /* XXX Not used yet */ +int protocol_version = 0; + +char *esock_ssl_errstr = ""; + +#define FLAGSBUFSIZE 512 +#define X509BUFSIZE 256 +#define DEFAULT_VERIFY_DEPTH 1 + +#define SET_WANT(cp, ssl_error) \ + switch((ssl_error)) { \ + case SSL_ERROR_WANT_READ: \ + (cp)->ssl_want = ESOCK_SSL_WANT_READ; \ + break; \ + case SSL_ERROR_WANT_WRITE: \ + (cp)->ssl_want = ESOCK_SSL_WANT_WRITE; \ + break; \ + default: \ + (cp)->ssl_want = 0; \ + break; \ + } + +#define RESET_ERRSTR() \ + esock_ssl_errstr = ""; + +#define MAYBE_SET_ERRSTR(s) \ + if (!esock_ssl_errstr[0]) \ + esock_ssl_errstr = (s); + +typedef struct { + int code; + char *text; +} err_entry; + +typedef struct { + SSL_CTX *ctx; + char *passwd; + int verify_depth; +} callback_data; + +static char *ssl_error_str(int error); +static void end_ssl_call(int ret, Connection *cp, int ssl_error); +static void check_shutdown(Connection *cp); +static int set_ssl_parameters(Connection *cp, SSL_CTX *ctx); +static int verify_callback(int ok, X509_STORE_CTX *ctx); +static int passwd_callback(char *buf, int num, int rwflag, void *userdata); +static void info_callback(const SSL *ssl, int where, int ret); +static void callback_data_free(void *parent, void *ptr, + CRYPTO_EX_DATA *ad, + int idx, long arg1, void *argp); +static RSA *tmp_rsa_callback(SSL *ssl, int is_export, int keylen); +static void restrict_protocols(SSL_CTX *ctx); + +static err_entry errs[] = { + {SSL_ERROR_NONE, "SSL_ERROR_NONE"}, + {SSL_ERROR_ZERO_RETURN, "SSL_ERROR_ZERO_RETURN"}, + {SSL_ERROR_WANT_READ, "SSL_ERROR_WANT_READ"}, + {SSL_ERROR_WANT_WRITE, "SSL_ERROR_WANT_WRITE"}, + {SSL_ERROR_SYSCALL, "SSL_ERROR_SYSCALL"}, + {SSL_ERROR_SSL, "SSL_ERROR_SSL"}, + {SSL_ERROR_WANT_X509_LOOKUP, "SSL_ERROR_WANT_X509_LOOKUP"}, + {SSL_ERROR_WANT_CONNECT, "SSL_ERROR_WANT_CONNECT"} +}; + +static SSL_METHOD *method; /* for listen and connect init */ +static char x509_buf[X509BUFSIZE]; /* for verify_callback */ +static int callback_data_index = -1; /* for ctx ex_data */ +static unsigned char randvec[1024]; /* XXX */ + +#if defined(__WIN32__) || OPEN_MAX > 256 +# define FOPEN_WORKAROUND(var, expr) var = (expr) +# define VOID_FOPEN_WORKAROUND(expr) expr +#else +/* + * This is an ugly workaround. On Solaris, fopen() will return NULL if + * it gets a file descriptor > 255. To avoid that, we'll make sure that + * there is always one low-numbered file descriptor available when + * fopen() is called. + */ +static int reserved_fd; /* Reserve a low-numbered file descriptor */ +# define USE_FOPEN_WORKAROUND 1 + +# define FOPEN_WORKAROUND(var, expr) \ +do { \ + close(reserved_fd); \ + var = (expr); \ + reserved_fd = open("/dev/null", O_RDONLY); \ +} while (0) + +# define VOID_FOPEN_WORKAROUND(expr) \ +do { \ + close(reserved_fd); \ + expr; \ + reserved_fd = open("/dev/null", O_RDONLY); \ +} while (0) +#endif + +esock_version *esock_ssl_version(void) +{ + static esock_version vsn; + + vsn.compile_version = OPENSSL_VERSION_TEXT; + vsn.lib_version = SSLeay_version(SSLEAY_VERSION); + return &vsn; +} + +char *esock_ssl_ciphers(void) +{ + SSL_CTX *ctx; + SSL *ssl; + char *ciphers; + const char *cp; + int i = 0, used = 0, len, incr = 1024; + + if (!(ctx = SSL_CTX_new(method))) + return NULL; + restrict_protocols(ctx); + if (!(ssl = SSL_new(ctx))) { + SSL_CTX_free(ctx); + return NULL; + } + + ciphers = esock_malloc(incr); + len = incr; + *ciphers = '\0'; + + while (1) { + if (!(cp = SSL_get_cipher_list(ssl, i))) + break; + if (i > 0) { + if (used == len) { + len += incr; + ciphers = esock_realloc(ciphers, len); + } + strcat(ciphers, ":"); + used++; + } + if (strlen(cp) + used >= len) { + len += incr; + ciphers = esock_realloc(ciphers, len); + } + strcat(ciphers, cp); + used += strlen(cp); + i++; + } + SSL_free(ssl); + SSL_CTX_free(ctx); + return ciphers; +} + +void esock_ssl_seed(void *buf, int len) +{ + RAND_seed(buf, len); + + /* XXX Maybe we should call RAND_status() and check if we have got + * enough randomness. + */ +} + +int esock_ssl_init(void) +{ + method = SSLv23_method(); /* SSLv2, SSLv3 and TLSv1, may be restricted + in listen and connect */ + SSL_load_error_strings(); + SSL_library_init(); + esock_ssl_seed(randvec, sizeof(randvec)); + callback_data_index = SSL_CTX_get_ex_new_index(0, "callback_data", + NULL, NULL, + callback_data_free); +#ifdef USE_FOPEN_WORKAROUND + reserved_fd = open("/dev/null", O_RDONLY); + DEBUGF(("init: reserved_fd=%d\r\n", reserved_fd)); +#endif + return 0; +} + + +void esock_ssl_finish(void) +{ + /* Nothing */ +} + + +void esock_ssl_free(Connection *cp) +{ + SSL *ssl = cp->opaque; + SSL_CTX *ctx; + + if (ssl) { + ctx = SSL_get_SSL_CTX(ssl); + SSL_free(ssl); + if (cp->origin != ORIG_ACCEPT) + SSL_CTX_free(ctx); + cp->opaque = NULL; + } +} + + +/* + * Print SSL specific errors. + */ +void esock_ssl_print_errors_fp(FILE *fp) +{ + ERR_print_errors_fp(fp); +} + + +int esock_ssl_accept_init(Connection *cp, void *listenssl) +{ + SSL_CTX *listenctx; + SSL *ssl; + + RESET_ERRSTR(); + MAYBE_SET_ERRSTR("esslacceptinit"); + + if(!listenssl) { + DEBUGF(("esock_ssl_accept_init: listenssl null\n")); + return -1; + } + if (!(listenctx = SSL_get_SSL_CTX(listenssl))) { + DEBUGF(("esock_ssl_accept_init: SSL_get_SSL_CTX\n")); + return -1; + } + if (!(ssl = cp->opaque = SSL_new(listenctx))) { + DEBUGF(("esock_ssl_accept_init: SSL_new(listenctx)\n")); + return -1; + } + SSL_set_fd(ssl, cp->fd); + return 0; + +} + + +int esock_ssl_connect_init(Connection *cp) +{ + SSL_CTX *ctx; + SSL *ssl; + + RESET_ERRSTR(); + MAYBE_SET_ERRSTR("esslconnectinit"); + + if (!(ctx = SSL_CTX_new(method))) + return -1; + if (set_ssl_parameters(cp, ctx) < 0) { + SSL_CTX_free(ctx); + return -1; + } + restrict_protocols(ctx); + if (!(ssl = cp->opaque = SSL_new(ctx))) { + SSL_CTX_free(ctx); + return -1; + } + SSL_set_fd(ssl, cp->fd); + return 0; +} + + +int esock_ssl_listen_init(Connection *cp) +{ + SSL_CTX *ctx; + SSL *ssl; + + RESET_ERRSTR(); + MAYBE_SET_ERRSTR("essllisteninit"); + + if (!(ctx = SSL_CTX_new(method))) + return -1; + if (set_ssl_parameters(cp, ctx) < 0) { + SSL_CTX_free(ctx); + return -1; + } + restrict_protocols(ctx); + + /* The allocation of ctx is for setting ssl parameters, so that + * accepts can inherit them. We allocate ssl to be able to + * refer to it via cp->opaque, but will not be used otherwise. + */ + if (!(ssl = cp->opaque = SSL_new(ctx))) { + SSL_CTX_free(ctx); + return -1; + } + /* Set callback for temporary ephemeral RSA key generation. + * Note: for servers only. */ + SSL_CTX_set_tmp_rsa_callback(ctx, tmp_rsa_callback); + return 0; +} + +/* + * esock_ssl_accept(Connection *cp) + * + */ +int esock_ssl_accept(Connection *cp) +{ + int ret, ssl_error; + SSL *ssl = cp->opaque; + + RESET_ERRSTR(); + + DEBUGF(("esock_ssl_accept: calling SSL_accept fd = %d\n" + " state before: %s\n", cp->fd, SSL_state_string(ssl))); + ret = SSL_accept(ssl); + DEBUGF((" sock_errno %d errno %d \n", sock_errno(), errno)); + ssl_error = SSL_get_error(ssl, ret); + DEBUGF((" SSL_accept = %d\n" + " ssl_error: %s\n" + " state after: %s\n", + ret, ssl_error_str(ssl_error), SSL_state_string(ssl))); + DEBUGF((" ret %d os error %s\n", ret, strerror(errno))); + if (ret > 0) + return ret; + else if (ret == 0) { + const char* f; int l; unsigned int e; + while ((e = ERR_get_error_line(&f, &l))) { + DEBUGF((" error %s:%d %s\n", f, l, ssl_error_str(e))); + } + /* permanent accept error */ + sock_set_errno(ERRNO_NONE); + MAYBE_SET_ERRSTR("esslaccept"); + return -1; + } + end_ssl_call(ret, cp, ssl_error); + return ret; +} + +/* + * esock_ssl_connect(Connection *cp) + * + */ +int esock_ssl_connect(Connection *cp) +{ + int ret, ssl_error; + SSL *ssl = cp->opaque; + + RESET_ERRSTR(); + + DEBUGF(("esock_ssl_connect: calling SSL_connect fd = %d\n" + " state before: %s\n", cp->fd, SSL_state_string(ssl))); + ret = SSL_connect(ssl); + ssl_error = SSL_get_error(ssl, ret); + DEBUGF((" SSL_connect() = %d\n" + " ssl_error: %s\n" + " state after: %s\n", + ret, ssl_error_str(ssl_error), SSL_state_string(ssl))); + if (ret > 0) + return ret; + else if (ret == 0) { + /* permanent connect error */ + sock_set_errno(ERRNO_NONE); + MAYBE_SET_ERRSTR("esslconnect"); + return -1; + } + end_ssl_call(ret, cp, ssl_error); + return ret; +} + + +int esock_ssl_session_reused(Connection *cp) +{ + SSL *ssl = cp->opaque; + + return SSL_session_reused(ssl); +} + + +/* esock_ssl_read(Connection *cp, char *buf, int len) + * + * Read at most `len' chars into `buf'. Returns number of chars + * read ( > 0), or 0 at EOF, or -1 on error. Sets cp->eof, cp->bp if + * appropriate. + */ + +int esock_ssl_read(Connection *cp, char *buf, int len) +{ + int ret, ssl_error; + SSL *ssl = cp->opaque; + + RESET_ERRSTR(); + DEBUGF(("esock_ssl_read: calling SSL_read fd = %d\n" + " state before: %s\n", cp->fd, SSL_state_string(ssl))); + + ret = SSL_read(ssl, buf, len); + ssl_error = SSL_get_error(ssl, ret); + + DEBUGF((" SSL_read = %d\n" + " ssl_error: %s\n" + " state after: %s\n", + ret, ssl_error_str(ssl_error), SSL_state_string(ssl))); + + if (ssl_error == SSL_ERROR_NONE) { + DEBUGMSGF(("message (hex) : [%3.*a]\n", ret, buf)); + DEBUGMSGF(("message (char): [%3.*b]\n", ret, buf)); + } + if (ret > 0) + return ret; + if (ret == 0) { + check_shutdown(cp); + return ret; + } + end_ssl_call(ret, cp, ssl_error); + return ret; +} + +/* + * esock_ssl_write(Connection *cp, char *buf, int len) + * + * Writes at most `len' chars from `buf'. Returns number of chars + * written, or -1 on error. + */ +int esock_ssl_write(Connection *cp, char *buf, int len) +{ + int ret, ssl_error; + SSL *ssl = cp->opaque; + + RESET_ERRSTR(); + DEBUGF(("esock_ssl_write: calling SSL_write fd = %d\n" + " state before: %s\n", cp->fd, SSL_state_string(ssl))); + ret = SSL_write(ssl, buf, len); + ssl_error = SSL_get_error(ssl, ret); + DEBUGF((" SSL_write = %d\n" + " ssl_error: %s\n" + " state after: %s\n", + ret, ssl_error_str(ssl_error), SSL_state_string(ssl))); + if (ssl_error == SSL_ERROR_NONE) { + DEBUGMSGF(("message (hex) : [%3.*a]\n", ret, buf)); + DEBUGMSGF(("message (char): [%3.*b]\n", ret, buf)); + } + if (ret > 0) + return ret; + if (ret == 0) { + check_shutdown(cp); + return ret; + } + end_ssl_call(ret, cp, ssl_error); + return ret; +} + + +int esock_ssl_shutdown(Connection *cp) +{ + int ret, ssl_error; + SSL *ssl = cp->opaque; + + RESET_ERRSTR(); + DEBUGF(("esock_ssl_shutdown: calling SSL_shutdown fd = %d\n" + " state before: %s\n", cp->fd, SSL_state_string(ssl))); + ret = SSL_shutdown(ssl); + ssl_error = SSL_get_error(ssl, ret); + DEBUGF((" SSL_shutdown = %d\n" + " ssl_error: %s\n" + " state after: %s\n", + ret, ssl_error_str(ssl_error), SSL_state_string(ssl))); + if (ret >= 0) { + check_shutdown(cp); + return ret; + } + end_ssl_call(ret, cp, ssl_error); + return ret; +} + + +/* Returns total number of bytes in DER encoded cert pointed to by + * *buf, which is allocated by this function, unless return < 0. + * XXX X509_free ?? + */ +int esock_ssl_getpeercert(Connection *cp, unsigned char **buf) +{ + int len; + SSL *ssl = cp->opaque; + X509 *x509; + unsigned char *tmp; + + RESET_ERRSTR(); + if((x509 = SSL_get_peer_certificate(ssl)) == NULL) { + MAYBE_SET_ERRSTR("enopeercert"); /* XXX doc */ + return -1; + } + + if ((len = i2d_X509(x509, NULL)) <= 0) { + MAYBE_SET_ERRSTR("epeercert"); + return -1; + } + + tmp = *buf = esock_malloc(len); + + /* We must use a temporary value here, since i2d_X509(X509 *x, + * unsigned char **out) increments *out. + */ + if (i2d_X509(x509, &tmp) < 0) { + esock_free(tmp); + MAYBE_SET_ERRSTR("epeercert"); + return -1; + } + return len; +} + +/* Returns total number of bytes in chain of certs. Each cert begins + * with a 4-bytes length. The last cert is ended with 4-bytes of + * zeros. The result is returned in *buf, which is allocated unless + * the return value is < 0. + * XXX X509_free ? sk_X509_free ? + * XXX X509_free is reference counting. + */ +int esock_ssl_getpeercertchain(Connection *cp, unsigned char **buf) +{ + SSL *ssl = cp->opaque; + STACK_OF(X509) *x509_stack; + X509 *x509; + int num, i, totlen, pos, *der_len; + unsigned char *vbuf; + + RESET_ERRSTR(); + if((x509_stack = SSL_get_peer_cert_chain(ssl)) == NULL) { + MAYBE_SET_ERRSTR("enopeercertchain"); /* XXX doc */ + return -1; + } + + num = sk_X509_num(x509_stack); + der_len = esock_malloc(num * sizeof(int)); + totlen = 0; + + for (i = 0; i < num; i++) { + x509 = sk_X509_value(x509_stack, i); + totlen += 4; + if ((der_len[i] = i2d_X509(x509, NULL)) < 0) { + MAYBE_SET_ERRSTR("epeercertchain"); + esock_free(der_len); + return -1; + } + totlen += der_len[i]; + } + totlen += 4; + + vbuf = *buf = esock_malloc(totlen); + pos = 0; + + for (i = 0; i < num; i++) { + x509 = sk_X509_value(x509_stack, i); + PUT_INT32(der_len[i], vbuf); + vbuf += 4; + /* Note: i2d_X509 increments vbuf */ + if (i2d_X509(x509, &vbuf) < 0) { + MAYBE_SET_ERRSTR("epeercertchain"); + esock_free(*buf); + esock_free(der_len); + return -1; + } + } + esock_free(der_len); + return totlen; +} + + +int esock_ssl_getprotocol_version(Connection *cp, char **buf) +{ + SSL *ssl = cp->opaque; + + RESET_ERRSTR(); + if (!ssl) { + MAYBE_SET_ERRSTR("enoent"); + return -1; + } + *buf = (char *) SSL_get_version(ssl); + + return 0; +} + + +int esock_ssl_getcipher(Connection *cp, char **buf) +{ + SSL *ssl = cp->opaque; + + RESET_ERRSTR(); + if (!ssl) { + MAYBE_SET_ERRSTR("enoent"); + return -1; + } + *buf = (char *) SSL_get_cipher(ssl); + + return 0; +} + +/* Local functions */ + +static char *ssl_error_str(int ssl_error) +{ + int i; + static char buf[128]; + + for (i = 0; i < sizeof(errs)/sizeof(err_entry); i ++) { + if (ssl_error == errs[i].code) + return errs[i].text; + } + sprintf(buf, "esock_openssl: SSL_error unknown: %d", ssl_error); + return buf; +} + +void end_ssl_call(int ret, Connection *cp, int ssl_error) +{ + SET_WANT(cp, ssl_error); + switch (ssl_error) { + case SSL_ERROR_SYSCALL: + /* Typically sock_errno() is equal to ERRNO_BLOCK */ + MAYBE_SET_ERRSTR(esock_posix_str(sock_errno())); + break; + case SSL_ERROR_SSL: + sock_set_errno(ERRNO_NONE); + MAYBE_SET_ERRSTR("esslerrssl"); + break; + case SSL_ERROR_WANT_X509_LOOKUP: + SSLDEBUGF(); + sock_set_errno(ERRNO_NONE); + MAYBE_SET_ERRSTR("ex509lookup"); + break; + case SSL_ERROR_WANT_CONNECT: + SSLDEBUGF(); + sock_set_errno(ERRNO_NONE); + MAYBE_SET_ERRSTR("ewantconnect"); + break; + default: + break; + } +} + +void check_shutdown(Connection *cp) +{ + int sd_mode; + SSL *ssl = cp->opaque; + + sd_mode = SSL_get_shutdown(ssl); + if (sd_mode & SSL_RECEIVED_SHUTDOWN) + cp->eof = 1; + if (sd_mode & SSL_SENT_SHUTDOWN) { + DEBUGF(("check_shutdown SSL_SENT_SHUTDOWN\n")); + cp->bp = 1; + } +} + +/* + * set_ssl_parameters + * + * Set ssl parameters from connection structure. Only called for + * listen and connect. + * + * Note: The -cacertdir option is not documented. + */ +static int set_ssl_parameters(Connection *cp, SSL_CTX *ctx) +{ + char *cacertfile = NULL, *cacertdir = NULL, *certfile = NULL; + char *keyfile = NULL, *ciphers = NULL, *password = NULL; + int verify = 0, verify_depth = DEFAULT_VERIFY_DEPTH, verify_mode; + int i, argc; + char **argv; + callback_data *cb_data; + + RESET_ERRSTR(); + + argc = esock_build_argv(cp->flags, &argv); + + DEBUGF(("Argv:\n")); + for (i = 0; i < argc; i++) { + DEBUGF(("%d: %s\n", i, argv[i])); + } + + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "-verify") == 0) { + verify = atoi(argv[++i]); + } else if (strcmp(argv[i], "-depth") == 0) { + verify_depth = atoi(argv[++i]); + } else if (strcmp(argv[i], "-log") == 0) { + /* XXX ignored: logging per connection not supported */ + i++; + } else if (strcmp(argv[i], "-certfile") == 0) { + certfile = argv[++i]; + } else if (strcmp(argv[i], "-keyfile") == 0) { + keyfile = argv[++i]; + } else if (strcmp(argv[i], "-password") == 0) { + password = argv[++i]; + } else if (strcmp(argv[i], "-cacertfile") == 0) { + cacertfile = argv[++i]; + } else if (strcmp(argv[i], "-cacertdir") == 0) { + cacertdir = argv[++i]; + } else if (strcmp(argv[i], "-d") == 0) { + /* XXX ignored: debug per connection not supported */ + i++; + } else if (strcmp(argv[i], "-ciphers") == 0) { + ciphers = argv[++i]; + } else { + /* XXX Error: now ignored */ + } + } + DEBUGF(("set_ssl_parameters: all arguments read\n")); + + if (cp->origin == ORIG_LISTEN && !certfile) { + DEBUGF(("ERROR: Server must have certificate\n")); + MAYBE_SET_ERRSTR("enoservercert"); + goto err_end; + } + + /* Define callback data */ + /* XXX Check for NULL */ + cb_data = esock_malloc(sizeof(callback_data)); + cb_data->ctx = ctx; + if (password) { + cb_data->passwd = esock_malloc(strlen(password) + 1); + strcpy(cb_data->passwd, password); + } else + cb_data->passwd = NULL; + cb_data->verify_depth = verify_depth; + SSL_CTX_set_ex_data(ctx, callback_data_index, cb_data); + + /* password callback */ + SSL_CTX_set_default_passwd_cb(ctx, passwd_callback); + SSL_CTX_set_default_passwd_cb_userdata(ctx, cb_data); + + /* Set location for "trusted" certificates */ + if (cacertfile || cacertdir) { + int res; + DEBUGF(("set_ssl_parameters: SSL_CTX_load_verify_locations\n")); + FOPEN_WORKAROUND(res, SSL_CTX_load_verify_locations(ctx, cacertfile, + cacertdir)); + if (!res) { + DEBUGF(("ERROR: Cannot load verify locations\n")); + MAYBE_SET_ERRSTR("ecacertfile"); + goto err_end; + } + } else { + int res; + DEBUGF(("set_ssl_parameters: SSL_CTX_set_default_verify_paths\n")); + FOPEN_WORKAROUND(res, SSL_CTX_set_default_verify_paths(ctx)); + if (!res) { + DEBUGF(("ERROR: Cannot set default verify paths\n")); + MAYBE_SET_ERRSTR("ecacertfile"); + goto err_end; + } + } + + /* For a server the following sets the list of CA distinguished + * names that it sends to its client when it requests the + * certificate from the client. + * XXX The names of certs in cacertdir ignored. + */ + if (cp->origin == ORIG_LISTEN && cacertfile) { + DEBUGF(("set_ssl_parameters: SSL_CTX_set_client_CA_list\n")); + VOID_FOPEN_WORKAROUND(SSL_CTX_set_client_CA_list(ctx, + SSL_load_client_CA_file(cacertfile))); + if (!SSL_CTX_get_client_CA_list(ctx)) { + DEBUGF(("ERROR: Cannot set client CA list\n")); + MAYBE_SET_ERRSTR("ecacertfile"); + goto err_end; + } + } + + /* Use certificate file if key file has not been set. */ + if (!keyfile) + keyfile = certfile; + + if (certfile) { + int res; + DEBUGF(("set_ssl_parameters: SSL_CTX_use_certificate_file\n")); + FOPEN_WORKAROUND(res, SSL_CTX_use_certificate_file(ctx, certfile, + SSL_FILETYPE_PEM)); + if (res <= 0) { + DEBUGF(("ERROR: Cannot set certificate file\n")); + MAYBE_SET_ERRSTR("ecertfile"); + goto err_end; + } + } + if (keyfile) { + int res; + DEBUGF(("set_ssl_parameters: SSL_CTX_use_PrivateKey_file\n")); + FOPEN_WORKAROUND(res, SSL_CTX_use_PrivateKey_file(ctx, keyfile, + SSL_FILETYPE_PEM)); + if (res <= 0) { + DEBUGF(("ERROR: Cannot set private key file\n")); + MAYBE_SET_ERRSTR("ekeyfile"); + goto err_end; + } + } + if(certfile && keyfile) { + DEBUGF(("set_ssl_parameters: SSL_CTX_check_private_key\n")); + if (!SSL_CTX_check_private_key(ctx)) { + DEBUGF(("ERROR: Private key does not match the certificate\n")); + MAYBE_SET_ERRSTR("ekeymismatch"); + goto err_end; + } + } + + /* Ciphers */ + if (ciphers) { + DEBUGF(("set_ssl_parameters: SSL_CTX_set_cipher_list\n")); + if (!SSL_CTX_set_cipher_list(ctx, ciphers)) { + DEBUGF(("ERROR: Cannot set cipher list\n")); + MAYBE_SET_ERRSTR("ecipher"); + goto err_end; + } + } + + /* Verify depth */ + DEBUGF(("set_ssl_parameters: SSL_CTX_set_verify_depth (depth = %d)\n", + verify_depth)); + SSL_CTX_set_verify_depth(ctx, verify_depth); + + /* Verify mode and callback */ + /* XXX Why precisely these modes? */ + switch (verify) { + case 0: + verify_mode = SSL_VERIFY_NONE; + break; + case 1: + verify_mode = SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE; + break; + case 2: + verify_mode = SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE| + SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + break; + default: + verify_mode = SSL_VERIFY_NONE; + } + DEBUGF(("set_ssl_parameters: SSL_CTX_set_verify (verify = %d)\n", + verify)); + SSL_CTX_set_verify(ctx, verify_mode, verify_callback); + + /* Session id context. Should be an option really. */ + if (cp->origin == ORIG_LISTEN) { + unsigned char *sid = "Erlang/OTP/ssl"; + SSL_CTX_set_session_id_context(ctx, sid, strlen(sid)); + } + + /* info callback */ + if (debug) + SSL_CTX_set_info_callback(ctx, info_callback); + + DEBUGF(("set_ssl_parameters: done\n")); + /* Free arg list */ + for (i = 0; argv[i]; i++) + esock_free(argv[i]); + esock_free(argv); + return 0; + + err_end: + DEBUGF(("set_ssl_parameters: error\n")); + /* Free arg list */ + for (i = 0; argv[i]; i++) + esock_free(argv[i]); + esock_free(argv); + return -1; +} + +/* Call back functions */ + +static int verify_callback(int ok, X509_STORE_CTX *x509_ctx) +{ + X509 *cert; + int cert_err, depth; + SSL *ssl; + SSL_CTX *ctx; + callback_data *cb_data; + + cert = X509_STORE_CTX_get_current_cert(x509_ctx); + cert_err = X509_STORE_CTX_get_error(x509_ctx); + depth = X509_STORE_CTX_get_error_depth(x509_ctx); + + ssl = X509_STORE_CTX_get_ex_data(x509_ctx, + SSL_get_ex_data_X509_STORE_CTX_idx()); + ctx = SSL_get_SSL_CTX(ssl); + cb_data = SSL_CTX_get_ex_data(ctx, callback_data_index); + + X509_NAME_oneline(X509_get_subject_name(cert), x509_buf, sizeof(x509_buf)); + DEBUGF((" +vfy: depth = %d\n", depth)); + DEBUGF((" subject = %s\n", x509_buf)); + X509_NAME_oneline(X509_get_issuer_name(cert), x509_buf, sizeof(x509_buf)); + DEBUGF((" issuer = %s\n", x509_buf)); + + if (!ok) { + DEBUGF((" +vfy: error = %d [%s]\n", cert_err, + X509_verify_cert_error_string(cert_err))); + if (depth >= cb_data->verify_depth) + ok = 1; + } + + switch (cert_err) { + case X509_V_OK: + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + ok = 1; + break; + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: + MAYBE_SET_ERRSTR("enoissuercert"); + break; + case X509_V_ERR_CERT_HAS_EXPIRED: + MAYBE_SET_ERRSTR("epeercertexpired"); + break; + case X509_V_ERR_CERT_NOT_YET_VALID: + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + MAYBE_SET_ERRSTR("epeercertinvalid"); + break; + case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: + MAYBE_SET_ERRSTR("eselfsignedcert"); + break; + case X509_V_ERR_CERT_CHAIN_TOO_LONG: + MAYBE_SET_ERRSTR("echaintoolong"); + break; + default: + MAYBE_SET_ERRSTR("epeercert"); + break; + } + DEBUGF((" +vfy: return = %d\n",ok)); + return ok; +} + +static int passwd_callback(char *buf, int num, int rwflag, void *userdata) +{ + callback_data *cb_data = userdata; + int len; + + if (cb_data && cb_data->passwd) { + DEBUGF((" +passwd: %s\n", cb_data->passwd)); + strncpy(buf, cb_data->passwd, num); + len = strlen(cb_data->passwd); + return len; + } + DEBUGF((" +passwd: ERROR: No password set.\n")); + return 0; +} + +static void info_callback(const SSL *ssl, int where, int ret) +{ + char *str; + + if (where & SSL_CB_LOOP) { + DEBUGF((" info: %s\n",SSL_state_string_long(ssl))); + } else if (where & SSL_CB_ALERT) { + str = (where & SSL_CB_READ) ? "read" : "write"; + DEBUGF((" info: SSL3 alert %s:%s:%s\n", str, + SSL_alert_type_string_long(ret), + SSL_alert_desc_string_long(ret))); + } else if (where & SSL_CB_EXIT) { + if (ret == 0) { + DEBUGF((" info: failed in %s\n", SSL_state_string_long(ssl))); + } else if (ret < 0) { + DEBUGF((" info: error in %s\n", SSL_state_string_long(ssl))); + } + } +} + +/* This function is called whenever a SSL_CTX *ctx structure is + * freed. +*/ +static void callback_data_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, + int idx, long arg1, void *argp) +{ + callback_data *cb_data = ptr; + + if (cb_data) { + if (cb_data->passwd) + esock_free(cb_data->passwd); + esock_free(cb_data); + } +} + +static RSA *tmp_rsa_callback(SSL *ssl, int is_export, int keylen) +{ + static RSA *rsa512 = NULL; + static RSA *rsa1024 = NULL; + + switch (keylen) { + case 512: + if (!rsa512) + rsa512 = RSA_generate_key(keylen, RSA_F4, NULL, NULL); + return rsa512; + break; + case 1024: + if (!rsa1024) + rsa1024 = RSA_generate_key(keylen, RSA_F4, NULL, NULL); + return rsa1024; + break; + default: + if (rsa1024) + return rsa1024; + if (rsa512) + return rsa512; + rsa512 = RSA_generate_key(keylen, RSA_F4, NULL, NULL); + return rsa512; + } +} + +/* Restrict protocols (SSLv2, SSLv3, TLSv1) */ +static void restrict_protocols(SSL_CTX *ctx) +{ + long options = 0; + + if (protocol_version) { + if ((protocol_version & ESOCK_SSLv2) == 0) + options |= SSL_OP_NO_SSLv2; + if ((protocol_version & ESOCK_SSLv3) == 0) + options |= SSL_OP_NO_SSLv3; + if ((protocol_version & ESOCK_TLSv1) == 0) + options |= SSL_OP_NO_TLSv1; + SSL_CTX_set_options(ctx, options); + } +} + + +static unsigned char randvec [] = { + 181, 177, 237, 240, 107, 24, 43, 148, + 105, 4, 248, 13, 199, 255, 23, 58, + 71, 181, 57, 151, 156, 25, 165, 7, + 73, 80, 80, 231, 70, 110, 96, 162, + 24, 205, 178, 178, 67, 122, 210, 180, + 92, 6, 156, 182, 84, 159, 85, 6, + 175, 66, 165, 167, 137, 34, 179, 237, + 77, 90, 87, 185, 21, 106, 92, 115, + 137, 65, 233, 42, 164, 153, 208, 133, + 160, 172, 129, 202, 46, 220, 98, 66, + 115, 66, 46, 28, 226, 200, 140, 145, + 207, 194, 58, 71, 56, 203, 113, 34, + 221, 116, 63, 114, 188, 210, 45, 238, + 200, 123, 35, 150, 2, 78, 160, 22, + 226, 167, 162, 10, 182, 75, 109, 97, + 86, 252, 93, 125, 117, 214, 220, 37, + 105, 160, 56, 158, 97, 57, 22, 14, + 73, 169, 111, 190, 222, 176, 14, 82, + 111, 42, 87, 90, 136, 236, 22, 209, + 156, 207, 40, 251, 88, 141, 51, 211, + 31, 158, 153, 91, 119, 83, 255, 60, + 55, 94, 5, 115, 119, 210, 224, 185, + 163, 163, 5, 3, 197, 106, 110, 206, + 109, 132, 50, 190, 177, 133, 175, 129, + 225, 161, 156, 244, 77, 150, 99, 38, + 17, 111, 46, 230, 152, 64, 50, 164, + 19, 78, 3, 164, 169, 175, 104, 97, + 103, 158, 91, 168, 186, 191, 73, 88, + 118, 112, 41, 188, 219, 0, 198, 209, + 206, 7, 5, 169, 127, 180, 80, 74, + 124, 4, 4, 108, 197, 67, 204, 29, + 101, 95, 174, 147, 64, 163, 89, 160, + 10, 5, 56, 134, 209, 69, 209, 55, + 214, 136, 45, 212, 113, 85, 159, 133, + 141, 249, 75, 40, 175, 91, 142, 13, + 179, 179, 51, 0, 136, 63, 148, 175, + 103, 162, 8, 214, 4, 24, 59, 71, + 9, 185, 48, 127, 159, 165, 8, 8, + 135, 151, 92, 214, 132, 151, 204, 169, + 24, 112, 229, 59, 236, 81, 238, 64, + 150, 196, 97, 213, 140, 159, 20, 24, + 79, 210, 191, 53, 130, 33, 157, 87, + 16, 180, 175, 217, 56, 123, 115, 196, + 130, 6, 155, 37, 220, 80, 232, 129, + 240, 57, 199, 249, 196, 152, 28, 111, + 124, 192, 59, 46, 29, 21, 178, 51, + 156, 17, 248, 61, 254, 80, 201, 131, + 203, 59, 227, 191, 71, 121, 134, 181, + 55, 79, 130, 225, 246, 36, 179, 224, + 189, 243, 200, 75, 73, 41, 251, 41, + 71, 251, 78, 146, 99, 101, 104, 69, + 18, 122, 65, 24, 232, 84, 246, 242, + 209, 18, 241, 114, 3, 65, 177, 99, + 49, 99, 215, 59, 9, 175, 195, 11, + 25, 46, 43, 120, 109, 179, 159, 250, + 239, 246, 135, 78, 2, 238, 214, 237, + 64, 170, 50, 44, 68, 67, 111, 232, + 225, 230, 224, 124, 76, 32, 52, 158, + 151, 54, 184, 135, 122, 66, 211, 215, + 121, 90, 124, 158, 55, 73, 116, 137, + 240, 15, 38, 31, 183, 86, 93, 49, + 148, 184, 125, 250, 155, 216, 84, 246, + 27, 172, 141, 54, 80, 158, 227, 254, + 189, 164, 238, 229, 68, 26, 231, 11, + 198, 222, 15, 141, 98, 8, 124, 219, + 60, 125, 170, 213, 114, 24, 189, 65, + 80, 186, 71, 126, 223, 153, 20, 141, + 110, 73, 173, 218, 214, 63, 205, 177, + 132, 115, 184, 28, 122, 232, 210, 72, + 237, 41, 93, 17, 152, 95, 242, 138, + 79, 98, 47, 197, 36, 17, 137, 230, + 15, 73, 193, 1, 181, 123, 0, 186, + 185, 135, 142, 200, 139, 78, 57, 145, + 191, 32, 98, 250, 113, 188, 71, 32, + 205, 81, 219, 99, 60, 87, 42, 95, + 249, 252, 121, 125, 246, 230, 74, 162, + 73, 59, 179, 142, 178, 47, 163, 161, + 236, 14, 123, 219, 18, 6, 102, 140, + 215, 210, 76, 9, 119, 147, 252, 63, + 13, 51, 161, 172, 180, 116, 212, 129, + 116, 237, 38, 64, 213, 222, 35, 14, + 183, 237, 78, 204, 250, 250, 5, 41, + 142, 5, 207, 154, 65, 183, 108, 82, + 1, 43, 149, 233, 89, 195, 25, 233, + 4, 34, 19, 122, 16, 58, 121, 5, + 118, 168, 22, 213, 49, 226, 163, 169, + 21, 78, 179, 232, 125, 216, 198, 147, + 245, 196, 199, 138, 185, 167, 179, 82, + 175, 53, 6, 162, 5, 141, 180, 212, + 95, 201, 234, 169, 111, 175, 138, 197, + 177, 246, 154, 41, 185, 201, 134, 187, + 88, 99, 231, 23, 190, 36, 72, 174, + 244, 185, 205, 50, 230, 226, 210, 119, + 175, 107, 109, 244, 12, 122, 84, 51, + 146, 95, 68, 74, 76, 212, 221, 103, + 244, 71, 63, 133, 149, 233, 48, 3, + 176, 168, 6, 98, 88, 226, 120, 190, + 205, 249, 38, 157, 205, 148, 250, 203, + 147, 62, 195, 229, 219, 109, 177, 119, + 120, 43, 165, 99, 253, 210, 180, 32, + 227, 180, 174, 64, 156, 139, 251, 53, + 205, 132, 210, 208, 3, 199, 115, 64, + 59, 27, 249, 164, 224, 191, 124, 241, + 142, 10, 19, 120, 227, 46, 174, 231, + 48, 65, 41, 56, 51, 38, 185, 95, + 250, 182, 100, 40, 196, 124, 173, 119, + 162, 148, 170, 34, 51, 68, 175, 60, + 242, 201, 225, 34, 146, 157, 159, 0, + 144, 148, 82, 72, 149, 53, 201, 10, + 248, 206, 154, 126, 33, 153, 56, 48, + 5, 90, 194, 22, 251, 173, 211, 202, + 203, 253, 112, 147, 188, 200, 142, 206, + 206, 175, 233, 76, 93, 104, 125, 41, + 64, 145, 202, 53, 130, 251, 23, 90, + 28, 199, 13, 128, 185, 154, 53, 194, + 195, 55, 80, 56, 151, 216, 195, 138, + 7, 170, 143, 236, 74, 141, 229, 174, + 32, 165, 131, 68, 174, 104, 35, 143, + 183, 41, 80, 191, 120, 79, 166, 240, + 123, 55, 60, 2, 128, 56, 4, 199, + 122, 85, 90, 76, 246, 29, 13, 6, + 126, 229, 14, 203, 244, 73, 121, 42, + 169, 35, 44, 202, 18, 69, 153, 120, + 141, 77, 124, 191, 215, 18, 115, 187, + 108, 246, 135, 151, 225, 192, 50, 89, + 128, 45, 39, 253, 149, 234, 203, 84, + 51, 174, 15, 237, 17, 57, 76, 81, + 39, 107, 40, 36, 22, 52, 92, 39}; diff --git a/lib/ssl/c_src/esock_osio.c b/lib/ssl/c_src/esock_osio.c new file mode 100644 index 0000000000..41c5271c16 --- /dev/null +++ b/lib/ssl/c_src/esock_osio.c @@ -0,0 +1,328 @@ +/*<copyright> + * <year>1999-2008</year> + * <holder>Ericsson AB, All Rights Reserved</holder> + *</copyright> + *<legalnotice> + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson AB. + *</legalnotice> + */ +/* + * Purpose: Std filedescriptors, break handler + * + */ + +#include <stdio.h> +#include <stdlib.h> +#ifdef __WIN32__ +#include "esock_winsock.h" +#include <process.h> +#include <io.h> +#include <fcntl.h> +#else +#include <unistd.h> +#include <signal.h> +#endif + +#include "esock.h" +#include "debuglog.h" +#include "esock_utils.h" +#include "esock_osio.h" + +#ifdef __WIN32__ +#define write _write +#define read _read +#define LOCALHOSTADDR "127.0.0.1" +#define LOCBUFSIZE 1024 +#endif + +#define PACKET_SIZE 4 +#define EBUFSIZE 256 + +FD local_read_fd = 0; + +static int inc_rbuf(int size); +static void free_rbuf(void); +static int read_fill(unsigned char *buf, int len); +#ifdef __WIN32__ +static int create_local_thread(void); +static DWORD WINAPI local_thread(LPVOID lpvParam); +static BOOL WINAPI signal_handler(DWORD ctrl); +#endif + +static unsigned char *rbuf = NULL; +static int rbuf_malloced = 0; +#ifdef __WIN32__ +static unsigned long one = 1, zero = 0; +static int local_portno; +static char *local_buf; +#endif + +int set_break_handler(void) +{ +#ifndef __WIN32__ + struct sigaction act; + + /* Ignore SIGPIPE signal */ + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, NULL); + return 0; +#else + SetConsoleCtrlHandler(signal_handler, TRUE); + return 0; +#endif +} + + +#ifdef __WIN32__ + +int set_binary_mode(void) +{ + _setmode(0, _O_BINARY); + _setmode(1, _O_BINARY); + return 0; +} + +int esock_osio_init(void) +{ + return create_local_thread(); +} + +void esock_osio_finish(void) +{ + sock_close(local_read_fd); +} + +#endif + +int read_ctrl(unsigned char **ebufp) +{ + int tbh, cc; + unsigned char *mbuf; + + if (inc_rbuf(EBUFSIZE) < 0) { + fprintf(stderr, "read_ctrl: cannot alloc rbuf\n"); + return -1; + } + cc = read_fill(rbuf, PACKET_SIZE); + if (cc < 0) { + free_rbuf(); + return -1; + } + if (cc == 0) { + free_rbuf(); + return -1; /* XXX 0 ?? */ + } + tbh = GET_INT32(rbuf); + + if (tbh > rbuf_malloced - 4) { + if (inc_rbuf(tbh + 4) < 0) + return -1; + } + + mbuf = rbuf + PACKET_SIZE; + cc = read_fill(mbuf, tbh); + DEBUGF(("-----------------------------------\n")); + DEBUGF(("read_ctrl: cc = %d\n", cc)); + if(cc > 0) { + DEBUGMSGF(("message (hex) : [%3.*a]\n", cc, mbuf)); + DEBUGMSGF(("message (char): [%3.*b]\n", cc, mbuf)); + } + *ebufp = mbuf; + return cc; +} + +int write_ctrl(unsigned char *buf, int len) +{ + unsigned char lb[4]; + + PUT_INT32(len, lb); + DEBUGF(("write_ctrl: len = %d\n", len)); + DEBUGMSGF(("message (hex) : [%3.*a] [%3.*a]\n", PACKET_SIZE, lb, + len, buf)); + DEBUGMSGF(("message (char): [%3.*b] [%3.*b]\n", PACKET_SIZE, lb, + len, buf)); + + if (write(1, lb, PACKET_SIZE) != PACKET_SIZE) { /* XXX */ + fprintf(stderr, "write_ctrl: Bad write \n"); + return -1; + } + if (write(1, buf, len) != len) { /* XXX */ + fprintf(stderr, "write_ctrl: Bad write \n"); + return -1; + } + return len; +} + + +/* + * Local functions + * + */ + +static int inc_rbuf(int size) +{ + unsigned char *nbuf; + + if (rbuf_malloced >= size) + return 0; + if (rbuf != NULL) + nbuf = esock_realloc(rbuf, size); + else + nbuf = esock_malloc(size); + if(nbuf != NULL) { + rbuf = nbuf; + rbuf_malloced = size; + return 0; + } + return -1; +} + +static void free_rbuf(void) +{ + if (rbuf != NULL) { + esock_free(rbuf); + rbuf = NULL; + rbuf_malloced = 0; + } +} + +/* Fill buffer, return buffer length, 0 for EOF, < 0 for error. */ + +static int read_fill(unsigned char *buf, int len) +{ + int i, got = 0; + + do { + if ((i = sock_read(local_read_fd, buf+got, len-got)) <= 0) + return i; + got += i; + } while (got < len); + return len; +} + + +#ifdef __WIN32__ + +/* + * This routine creates a local thread, which reads from standard input + * and writes to a socket. + */ + +static int create_local_thread(void) +{ + struct sockaddr_in iserv_addr; + SOCKET tmpsock; + int length; + unsigned threadaddr; + + local_buf = esock_malloc(LOCBUFSIZE); + if ((tmpsock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { + fprintf(stderr, "create_local_thread could not create socket.\n"); + return -1; + } + memset(&iserv_addr, 0, sizeof(iserv_addr)); + iserv_addr.sin_family = AF_INET; + iserv_addr.sin_addr.s_addr = inet_addr(LOCALHOSTADDR); + iserv_addr.sin_port = htons(0); /* Have any port */ + + if (bind(tmpsock, (struct sockaddr *) &iserv_addr, + sizeof(iserv_addr)) < 0) { + fprintf(stderr, "create_local_thread could not bind.\n"); + closesocket(tmpsock); + return -1; + } + listen(tmpsock, 1); + length = sizeof(iserv_addr); + if (getsockname(tmpsock, (struct sockaddr *) &iserv_addr, &length) < 0) { + fprintf(stderr, "create_local_thread could not getsockname.\n"); + closesocket(tmpsock); + return -1; + } + local_portno = ntohs(iserv_addr.sin_port); + + if (_beginthreadex(NULL, 0, local_thread, NULL, 0, &threadaddr) == 0) { + fprintf(stderr, "create_local_thread could not _beginthreadex().\n"); + closesocket(tmpsock); + return -1; + } + local_read_fd = accept(tmpsock, (struct sockaddr *) NULL, (int *) NULL); + if (local_read_fd == INVALID_FD) { + fprintf(stderr, "create_local_thread could not accept.\n"); + closesocket(tmpsock); + return -1; + } + closesocket(tmpsock); + return 0; +} + +static DWORD WINAPI local_thread(LPVOID lpvParam) +{ + SOCKET sock; + struct hostent *host; + char hostname[64]; + struct sockaddr_in iserv_addr; + unsigned long addr; + int len; + HANDLE thread; + + sock = socket(AF_INET, SOCK_STREAM, 0); + memset(&iserv_addr, 0, sizeof(struct sockaddr_in)); + iserv_addr.sin_family = AF_INET; + iserv_addr.sin_addr.s_addr = inet_addr(LOCALHOSTADDR); + iserv_addr.sin_port = htons(local_portno); + if(connect(sock, (struct sockaddr*)&iserv_addr, sizeof iserv_addr) == + SOCKET_ERROR) { + fprintf(stderr, "local_thread thread could not connect\n"); + closesocket(sock); + return 0; + } + setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)); + + /* read from 0 and write to sock */ + while (1) { + if ((len = read(0, local_buf, LOCBUFSIZE)) <= 0) { + closesocket(sock); + close(0); + return 0; + } + if (send(sock, local_buf, len, 0) != len ) { + closesocket(sock); + close(0); + return 0; + } + } + return 0; +} + +/* Signal handler */ + +static BOOL WINAPI signal_handler(DWORD ctrl) +{ + switch (ctrl) { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + break; + case CTRL_LOGOFF_EVENT: + if (!getenv("ERLSRV_SERVICE_NAME")) + return FALSE; + break; + default: + exit(1); + } + return TRUE; +} + +#endif diff --git a/lib/ssl/c_src/esock_osio.h b/lib/ssl/c_src/esock_osio.h new file mode 100644 index 0000000000..8742c3b05b --- /dev/null +++ b/lib/ssl/c_src/esock_osio.h @@ -0,0 +1,34 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1999-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +#ifndef ESOCK_OSIO_H +#define ESOCK_OSIO_H + +extern FD local_read_fd; + +#ifdef __WIN32__ +int set_binary_mode(void); +int esock_osio_init(void); +void esock_osio_finish(void); +#endif +int set_break_handler(void); +int read_ctrl(unsigned char **ebufp); +int write_ctrl(unsigned char *buf, int len); + +#endif diff --git a/lib/ssl/c_src/esock_poll.c b/lib/ssl/c_src/esock_poll.c new file mode 100644 index 0000000000..e982eba881 --- /dev/null +++ b/lib/ssl/c_src/esock_poll.c @@ -0,0 +1,222 @@ +/*<copyright> + * <year>2005-2008</year> + * <holder>Ericsson AB, All Rights Reserved</holder> + *</copyright> + *<legalnotice> + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson AB. + *</legalnotice> + */ + +/* + * Purpose: Hide poll() and select() behind an API so that we + * can use either one. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef __WIN32__ +#include "esock_winsock.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <time.h> +#include <ctype.h> +#include <sys/types.h> +#include <errno.h> + +#ifdef __WIN32__ +#include <process.h> +#else +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <sys/time.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <fcntl.h> +#endif + +#include "esock.h" +#include "esock_ssl.h" +#include "esock_utils.h" +#include "esock_poll.h" +#include "debuglog.h" + +#if !defined(USE_SELECT) + +/* At least on FreeBSD, we need POLLRDNORM for normal files, not POLLIN. */ +/* Whether this is a bug in FreeBSD, I don't know. */ +#ifdef POLLRDNORM +#define POLL_INPUT (POLLIN | POLLRDNORM) +#else +#define POLL_INPUT POLLIN +#endif + +static void poll_fd_set(EsockPoll *ep, FD fd, short events) +{ + int i, j; + int prev_num_fds = ep->num_fds; + + if (ep->num_fds <= fd) { + ep->num_fds = fd + 64; + ep->fd_to_poll = (int *) esock_realloc(ep->fd_to_poll, + ep->num_fds*sizeof(int)); + for (j = prev_num_fds; j < ep->num_fds; j++) + ep->fd_to_poll[j] = -1; + } + i = ep->fd_to_poll[fd]; + if (i > 0 && i < ep->active && ep->fds[i].fd == fd) { + /* Already present in poll array */ + ep->fds[i].events |= events; + } else { + /* Append to poll array */ + if (ep->active >= ep->allocated) { + ep->allocated *= 2; + ep->fds = (struct pollfd *) + esock_realloc(ep->fds, ep->allocated*sizeof(struct pollfd)); + } + ep->fd_to_poll[fd] = ep->active; + ep->fds[ep->active].fd = fd; + ep->fds[ep->active].events = events; + ep->fds[ep->active].revents = 0; + ep->active++; + } +} + +static int poll_is_set(EsockPoll *ep, FD fd, short mask) +{ + if (fd >= ep->num_fds) { + return 0; + } else { + int i = ep->fd_to_poll[fd]; + return 0 <= i && i < ep->active && ep->fds[i].fd == fd && + (ep->fds[i].revents & mask) != 0; + } +} + +#endif + +void esock_poll_init(EsockPoll *ep) +{ +#ifdef USE_SELECT + /* Nothing to do here */ +#else + ep->allocated = 2; + ep->fds = (struct pollfd *) esock_malloc(ep->allocated*sizeof(struct pollfd)); + ep->num_fds = 1; + ep->fd_to_poll = esock_malloc(ep->num_fds*sizeof(int)); +#endif +} + +void esock_poll_zero(EsockPoll *ep) +{ +#ifdef USE_SELECT + FD_ZERO(&ep->readmask); + FD_ZERO(&ep->writemask); + FD_ZERO(&ep->exceptmask); +#else + int i; + + for (i = 0; i < ep->num_fds; i++) + ep->fd_to_poll[i] = -1; + ep->active = 0; +#endif +} + +void esock_poll_fd_set_read(EsockPoll *ep, FD fd) +{ +#ifdef USE_SELECT + FD_SET(fd, &ep->readmask); +#else + poll_fd_set(ep, fd, POLL_INPUT); +#endif +} + +void esock_poll_fd_set_write(EsockPoll *ep, FD fd) +{ +#ifdef USE_SELECT + FD_SET(fd, &ep->writemask); +#else + poll_fd_set(ep, fd, POLLOUT); +#endif +} + +int esock_poll_fd_isset_read(EsockPoll *ep, FD fd) +{ +#ifdef USE_SELECT + return FD_ISSET(fd, &ep->readmask); +#else + return poll_is_set(ep, fd, (POLL_INPUT|POLLHUP|POLLERR|POLLNVAL)); +#endif +} + +int esock_poll_fd_isset_write(EsockPoll *ep, FD fd) +{ +#ifdef USE_SELECT + return FD_ISSET(fd, &ep->writemask); +#else + return poll_is_set(ep, fd, (POLLOUT|POLLHUP|POLLERR|POLLNVAL)); +#endif +} + +#ifdef __WIN32__ +void esock_poll_fd_set_exception(EsockPoll *ep, FD fd) +{ + FD_SET(fd, &ep->exceptmask); +} + +int esock_poll_fd_isset_exception(EsockPoll *ep, FD fd) +{ + return FD_ISSET(fd, &ep->exceptmask); +} +#endif + +int esock_poll(EsockPoll *ep, int seconds) +{ + int sret; + +#ifdef USE_SELECT + struct timeval tv; + + tv.tv_sec = seconds; + tv.tv_usec = 0; + sret = select(FD_SETSIZE, &ep->readmask, &ep->writemask, &ep->exceptmask, &tv); + if (sret == 0) { + FD_ZERO(&ep->readmask); + FD_ZERO(&ep->writemask); + FD_ZERO(&ep->exceptmask); + } +#else + sret = poll(ep->fds, ep->active, 1000*seconds); +#endif + return sret; +} + +void esock_poll_clear_event(EsockPoll* ep, FD fd) +{ +#ifdef USE_SELECT + FD_CLR(fd, &ep->readmask); + FD_CLR(fd, &ep->writemask); + FD_CLR(fd, &ep->exceptmask); +#else + int i = ep->fd_to_poll[fd]; + if (i > 0 && ep->fds[i].fd == fd) + ep->fds[i].revents = 0; +#endif +} diff --git a/lib/ssl/c_src/esock_poll.h b/lib/ssl/c_src/esock_poll.h new file mode 100644 index 0000000000..639976dfa9 --- /dev/null +++ b/lib/ssl/c_src/esock_poll.h @@ -0,0 +1,60 @@ +/*<copyright> + * <year>2005-2008</year> + * <holder>Ericsson AB, All Rights Reserved</holder> + *</copyright> + *<legalnotice> + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson AB. + *</legalnotice> + */ +#ifndef ESOCK_POLL_SELECT_H +#define ESOCK_POLL_SELECT_H + +#if !defined(USE_SELECT) +#include <poll.h> +#endif + +typedef struct esock_poll { +#ifdef USE_SELECT + fd_set readmask; + fd_set writemask; + fd_set exceptmask; +#else + int* fd_to_poll; /* Map from fd to index into poll + * descriptor array. + */ + int num_fds; /* Number of entries in fd_to_poll. */ + struct pollfd* fds; /* Array of poll descriptors. */ + int allocated; /* Allocated number of fds. */ + int active; /* Active number of fds */ +#endif +} EsockPoll; + +void esock_poll_init(EsockPoll *ep); +void esock_poll_zero(EsockPoll *ep); + +void esock_poll_fd_set_read(EsockPoll *ep, FD fd); +void esock_poll_fd_set_write(EsockPoll *ep, FD fd); + +void esock_poll_clear_event(EsockPoll *ep, FD fd); + +int esock_poll_fd_isset_read(EsockPoll *ep, FD fd); +int esock_poll_fd_isset_write(EsockPoll *ep, FD fd); + +#ifdef __WIN32__ +void esock_poll_fd_set_exception(EsockPoll *ep, FD fd); +int esock_poll_fd_isset_exception(EsockPoll *ep, FD fd); +#endif + +int esock_poll(EsockPoll *ep, int seconds); +#endif diff --git a/lib/ssl/c_src/esock_posix_str.c b/lib/ssl/c_src/esock_posix_str.c new file mode 100644 index 0000000000..31062baaaf --- /dev/null +++ b/lib/ssl/c_src/esock_posix_str.c @@ -0,0 +1,642 @@ +/* + * %ExternalCopyright% + */ + +/* + * Original: tclPosixStr.c -- + * + * This file contains procedures that generate strings + * corresponding to various POSIX-related codes, such + * as errno and signals. + * + * Copyright (c) 1991-1994 The Regents of the University of California. + * Copyright (c) 1994-1996 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * SCCS: @(#) tclPosixStr.c 1.32 96/10/10 10:09:42 + */ + +/* Copy of erl_posix_str.c */ + +#ifdef __WIN32__ +#include "esock_winsock.h" +#endif + +#include <stdio.h> +#include <errno.h> +#include "esock_posix_str.h" + +/* + *---------------------------------------------------------------------- + * + * esock_posix_str -- + * + * Return a textual identifier for the given errno value. + * + * Results: + * This procedure returns a machine-readable textual identifier + * that corresponds to the current errno value (e.g. "eperm"). + * The identifier is the same as the #define name in errno.h, + * except that it is in lowercase. + * + *---------------------------------------------------------------------- + */ + +static char errstrbuf[32]; + +char *esock_posix_str(int error) +{ + switch (error) { +#ifdef E2BIG + case E2BIG: return "e2big"; +#endif +#ifdef EACCES + case EACCES: return "eacces"; +#endif +#ifdef EADDRINUSE + case EADDRINUSE: return "eaddrinuse"; +#endif +#ifdef EADDRNOTAVAIL + case EADDRNOTAVAIL: return "eaddrnotavail"; +#endif +#ifdef EADV + case EADV: return "eadv"; +#endif +#ifdef EAFNOSUPPORT + case EAFNOSUPPORT: return "eafnosupport"; +#endif +#ifdef EAGAIN + case EAGAIN: return "eagain"; +#endif +#ifdef EALIGN + case EALIGN: return "ealign"; +#endif +#if defined(EALREADY) && (!defined(EBUSY) || (EALREADY != EBUSY )) + case EALREADY: return "ealready"; +#endif +#ifdef EBADE + case EBADE: return "ebade"; +#endif +#ifdef EBADF + case EBADF: return "ebadf"; +#endif +#ifdef EBADFD + case EBADFD: return "ebadfd"; +#endif +#ifdef EBADMSG + case EBADMSG: return "ebadmsg"; +#endif +#ifdef EBADR + case EBADR: return "ebadr"; +#endif +#ifdef EBADRPC + case EBADRPC: return "ebadrpc"; +#endif +#ifdef EBADRQC + case EBADRQC: return "ebadrqc"; +#endif +#ifdef EBADSLT + case EBADSLT: return "ebadslt"; +#endif +#ifdef EBFONT + case EBFONT: return "ebfont"; +#endif +#ifdef EBUSY + case EBUSY: return "ebusy"; +#endif +#ifdef ECHILD + case ECHILD: return "echild"; +#endif +#ifdef ECHRNG + case ECHRNG: return "echrng"; +#endif +#ifdef ECOMM + case ECOMM: return "ecomm"; +#endif +#ifdef ECONNABORTED + case ECONNABORTED: return "econnaborted"; +#endif +#ifdef ECONNREFUSED + case ECONNREFUSED: return "econnrefused"; +#endif +#ifdef ECONNRESET + case ECONNRESET: return "econnreset"; +#endif +#if defined(EDEADLK) && (!defined(EWOULDBLOCK) || (EDEADLK != EWOULDBLOCK)) + case EDEADLK: return "edeadlk"; +#endif +#if defined(EDEADLOCK) && (!defined(EDEADLK) || (EDEADLOCK != EDEADLK)) + case EDEADLOCK: return "edeadlock"; +#endif +#ifdef EDESTADDRREQ + case EDESTADDRREQ: return "edestaddrreq"; +#endif +#ifdef EDIRTY + case EDIRTY: return "edirty"; +#endif +#ifdef EDOM + case EDOM: return "edom"; +#endif +#ifdef EDOTDOT + case EDOTDOT: return "edotdot"; +#endif +#ifdef EDQUOT + case EDQUOT: return "edquot"; +#endif +#ifdef EDUPPKG + case EDUPPKG: return "eduppkg"; +#endif +#ifdef EEXIST + case EEXIST: return "eexist"; +#endif +#ifdef EFAULT + case EFAULT: return "efault"; +#endif +#ifdef EFBIG + case EFBIG: return "efbig"; +#endif +#ifdef EHOSTDOWN + case EHOSTDOWN: return "ehostdown"; +#endif +#ifdef EHOSTUNREACH + case EHOSTUNREACH: return "ehostunreach"; +#endif +#if defined(EIDRM) && (!defined(EINPROGRESS) || (EIDRM != EINPROGRESS)) + case EIDRM: return "eidrm"; +#endif +#ifdef EINIT + case EINIT: return "einit"; +#endif +#ifdef EINPROGRESS + case EINPROGRESS: return "einprogress"; +#endif +#ifdef EINTR + case EINTR: return "eintr"; +#endif +#ifdef EINVAL + case EINVAL: return "einval"; +#endif +#ifdef EIO + case EIO: return "eio"; +#endif +#ifdef EISCONN + case EISCONN: return "eisconn"; +#endif +#ifdef EISDIR + case EISDIR: return "eisdir"; +#endif +#ifdef EISNAME + case EISNAM: return "eisnam"; +#endif +#ifdef ELBIN + case ELBIN: return "elbin"; +#endif +#ifdef EL2HLT + case EL2HLT: return "el2hlt"; +#endif +#ifdef EL2NSYNC + case EL2NSYNC: return "el2nsync"; +#endif +#ifdef EL3HLT + case EL3HLT: return "el3hlt"; +#endif +#ifdef EL3RST + case EL3RST: return "el3rst"; +#endif +#ifdef ELIBACC + case ELIBACC: return "elibacc"; +#endif +#ifdef ELIBBAD + case ELIBBAD: return "elibbad"; +#endif +#ifdef ELIBEXEC + case ELIBEXEC: return "elibexec"; +#endif +#ifdef ELIBMAX + case ELIBMAX: return "elibmax"; +#endif +#ifdef ELIBSCN + case ELIBSCN: return "elibscn"; +#endif +#ifdef ELNRNG + case ELNRNG: return "elnrng"; +#endif +#if defined(ELOOP) && (!defined(ENOENT) || (ELOOP != ENOENT)) + case ELOOP: return "eloop"; +#endif +#ifdef EMFILE + case EMFILE: return "emfile"; +#endif +#ifdef EMLINK + case EMLINK: return "emlink"; +#endif +#ifdef EMSGSIZE + case EMSGSIZE: return "emsgsize"; +#endif +#ifdef EMULTIHOP + case EMULTIHOP: return "emultihop"; +#endif +#ifdef ENAMETOOLONG + case ENAMETOOLONG: return "enametoolong"; +#endif +#ifdef ENAVAIL + case ENAVAIL: return "enavail"; +#endif +#ifdef ENET + case ENET: return "enet"; +#endif +#ifdef ENETDOWN + case ENETDOWN: return "enetdown"; +#endif +#ifdef ENETRESET + case ENETRESET: return "enetreset"; +#endif +#ifdef ENETUNREACH + case ENETUNREACH: return "enetunreach"; +#endif +#ifdef ENFILE + case ENFILE: return "enfile"; +#endif +#ifdef ENOANO + case ENOANO: return "enoano"; +#endif +#if defined(ENOBUFS) && (!defined(ENOSR) || (ENOBUFS != ENOSR)) + case ENOBUFS: return "enobufs"; +#endif +#ifdef ENOCSI + case ENOCSI: return "enocsi"; +#endif +#if defined(ENODATA) && (!defined(ECONNREFUSED) || (ENODATA != ECONNREFUSED)) + case ENODATA: return "enodata"; +#endif +#ifdef ENODEV + case ENODEV: return "enodev"; +#endif +#ifdef ENOENT + case ENOENT: return "enoent"; +#endif +#ifdef ENOEXEC + case ENOEXEC: return "enoexec"; +#endif +#ifdef ENOLCK + case ENOLCK: return "enolck"; +#endif +#ifdef ENOLINK + case ENOLINK: return "enolink"; +#endif +#ifdef ENOMEM + case ENOMEM: return "enomem"; +#endif +#ifdef ENOMSG + case ENOMSG: return "enomsg"; +#endif +#ifdef ENONET + case ENONET: return "enonet"; +#endif +#ifdef ENOPKG + case ENOPKG: return "enopkg"; +#endif +#ifdef ENOPROTOOPT + case ENOPROTOOPT: return "enoprotoopt"; +#endif +#ifdef ENOSPC + case ENOSPC: return "enospc"; +#endif +#if defined(ENOSR) && (!defined(ENAMETOOLONG) || (ENAMETOOLONG != ENOSR)) + case ENOSR: return "enosr"; +#endif +#if defined(ENOSTR) && (!defined(ENOTTY) || (ENOTTY != ENOSTR)) + case ENOSTR: return "enostr"; +#endif +#ifdef ENOSYM + case ENOSYM: return "enosym"; +#endif +#ifdef ENOSYS + case ENOSYS: return "enosys"; +#endif +#ifdef ENOTBLK + case ENOTBLK: return "enotblk"; +#endif +#ifdef ENOTCONN + case ENOTCONN: return "enotconn"; +#endif +#ifdef ENOTDIR + case ENOTDIR: return "enotdir"; +#endif +#if defined(ENOTEMPTY) && (!defined(EEXIST) || (ENOTEMPTY != EEXIST)) + case ENOTEMPTY: return "enotempty"; +#endif +#ifdef ENOTNAM + case ENOTNAM: return "enotnam"; +#endif +#ifdef ENOTSOCK + case ENOTSOCK: return "enotsock"; +#endif +#ifdef ENOTSUP + case ENOTSUP: return "enotsup"; +#endif +#ifdef ENOTTY + case ENOTTY: return "enotty"; +#endif +#ifdef ENOTUNIQ + case ENOTUNIQ: return "enotuniq"; +#endif +#ifdef ENXIO + case ENXIO: return "enxio"; +#endif +#if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || EOPNOTSUPP != ENOTSUP) + case EOPNOTSUPP: return "eopnotsupp"; +#endif +#ifdef EPERM + case EPERM: return "eperm"; +#endif +#if defined(EPFNOSUPPORT) && (!defined(ENOLCK) || (ENOLCK != EPFNOSUPPORT)) + case EPFNOSUPPORT: return "epfnosupport"; +#endif +#ifdef EPIPE + case EPIPE: return "epipe"; +#endif +#ifdef EPROCLIM + case EPROCLIM: return "eproclim"; +#endif +#ifdef EPROCUNAVAIL + case EPROCUNAVAIL: return "eprocunavail"; +#endif +#ifdef EPROGMISMATCH + case EPROGMISMATCH: return "eprogmismatch"; +#endif +#ifdef EPROGUNAVAIL + case EPROGUNAVAIL: return "eprogunavail"; +#endif +#ifdef EPROTO + case EPROTO: return "eproto"; +#endif +#ifdef EPROTONOSUPPORT + case EPROTONOSUPPORT: return "eprotonosupport"; +#endif +#ifdef EPROTOTYPE + case EPROTOTYPE: return "eprototype"; +#endif +#ifdef ERANGE + case ERANGE: return "erange"; +#endif +#if defined(EREFUSED) && (!defined(ECONNREFUSED) || (EREFUSED != ECONNREFUSED)) + case EREFUSED: return "erefused"; +#endif +#ifdef EREMCHG + case EREMCHG: return "eremchg"; +#endif +#ifdef EREMDEV + case EREMDEV: return "eremdev"; +#endif +#ifdef EREMOTE + case EREMOTE: return "eremote"; +#endif +#ifdef EREMOTEIO + case EREMOTEIO: return "eremoteio"; +#endif +#ifdef EREMOTERELEASE + case EREMOTERELEASE: return "eremoterelease"; +#endif +#ifdef EROFS + case EROFS: return "erofs"; +#endif +#ifdef ERPCMISMATCH + case ERPCMISMATCH: return "erpcmismatch"; +#endif +#ifdef ERREMOTE + case ERREMOTE: return "erremote"; +#endif +#ifdef ESHUTDOWN + case ESHUTDOWN: return "eshutdown"; +#endif +#ifdef ESOCKTNOSUPPORT + case ESOCKTNOSUPPORT: return "esocktnosupport"; +#endif +#ifdef ESPIPE + case ESPIPE: return "espipe"; +#endif +#ifdef ESRCH + case ESRCH: return "esrch"; +#endif +#ifdef ESRMNT + case ESRMNT: return "esrmnt"; +#endif +#ifdef ESTALE + case ESTALE: return "estale"; +#endif +#ifdef ESUCCESS + case ESUCCESS: return "esuccess"; +#endif +#if defined(ETIME) && (!defined(ELOOP) || (ETIME != ELOOP)) + case ETIME: return "etime"; +#endif +#if defined(ETIMEDOUT) && (!defined(ENOSTR) || (ETIMEDOUT != ENOSTR)) + case ETIMEDOUT: return "etimedout"; +#endif +#ifdef ETOOMANYREFS + case ETOOMANYREFS: return "etoomanyrefs"; +#endif +#ifdef ETXTBSY + case ETXTBSY: return "etxtbsy"; +#endif +#ifdef EUCLEAN + case EUCLEAN: return "euclean"; +#endif +#ifdef EUNATCH + case EUNATCH: return "eunatch"; +#endif +#ifdef EUSERS + case EUSERS: return "eusers"; +#endif +#ifdef EVERSION + case EVERSION: return "eversion"; +#endif +#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN)) + case EWOULDBLOCK: return "ewouldblock"; +#endif +#ifdef EXDEV + case EXDEV: return "exdev"; +#endif +#ifdef EXFULL + case EXFULL: return "exfull"; +#endif +#ifdef WSAEINTR + case WSAEINTR: return "eintr"; +#endif +#ifdef WSAEBADF + case WSAEBADF: return "ebadf"; +#endif +#ifdef WSAEACCES + case WSAEACCES: return "eacces"; +#endif +#ifdef WSAEFAULT + case WSAEFAULT: return "efault"; +#endif +#ifdef WSAEINVAL + case WSAEINVAL: return "einval"; +#endif +#ifdef WSAEMFILE + case WSAEMFILE: return "emfile"; +#endif +#ifdef WSAEWOULDBLOCK + case WSAEWOULDBLOCK: return "ewouldblock"; +#endif +#ifdef WSAEINPROGRESS + case WSAEINPROGRESS: return "einprogress"; +#endif +#ifdef WSAEALREADY + case WSAEALREADY: return "ealready"; +#endif +#ifdef WSAENOTSOCK + case WSAENOTSOCK: return "enotsock"; +#endif +#ifdef WSAEDESTADDRREQ + case WSAEDESTADDRREQ: return "edestaddrreq"; +#endif +#ifdef WSAEMSGSIZE + case WSAEMSGSIZE: return "emsgsize"; +#endif +#ifdef WSAEPROTOTYPE + case WSAEPROTOTYPE: return "eprototype"; +#endif +#ifdef WSAENOPROTOOPT + case WSAENOPROTOOPT: return "enoprotoopt"; +#endif +#ifdef WSAEPROTONOSUPPORT + case WSAEPROTONOSUPPORT: return "eprotonosupport"; +#endif +#ifdef WSAESOCKTNOSUPPORT + case WSAESOCKTNOSUPPORT: return "esocktnosupport"; +#endif +#ifdef WSAEOPNOTSUPP + case WSAEOPNOTSUPP: return "eopnotsupp"; +#endif +#ifdef WSAEPFNOSUPPORT + case WSAEPFNOSUPPORT: return "epfnosupport"; +#endif +#ifdef WSAEAFNOSUPPORT + case WSAEAFNOSUPPORT: return "eafnosupport"; +#endif +#ifdef WSAEADDRINUSE + case WSAEADDRINUSE: return "eaddrinuse"; +#endif +#ifdef WSAEADDRNOTAVAIL + case WSAEADDRNOTAVAIL: return "eaddrnotavail"; +#endif +#ifdef WSAENETDOWN + case WSAENETDOWN: return "enetdown"; +#endif +#ifdef WSAENETUNREACH + case WSAENETUNREACH: return "enetunreach"; +#endif +#ifdef WSAENETRESET + case WSAENETRESET: return "enetreset"; +#endif +#ifdef WSAECONNABORTED + case WSAECONNABORTED: return "econnaborted"; +#endif +#ifdef WSAECONNRESET + case WSAECONNRESET: return "econnreset"; +#endif +#ifdef WSAENOBUFS + case WSAENOBUFS: return "enobufs"; +#endif +#ifdef WSAEISCONN + case WSAEISCONN: return "eisconn"; +#endif +#ifdef WSAENOTCONN + case WSAENOTCONN: return "enotconn"; +#endif +#ifdef WSAESHUTDOWN + case WSAESHUTDOWN: return "eshutdown"; +#endif +#ifdef WSAETOOMANYREFS + case WSAETOOMANYREFS: return "etoomanyrefs"; +#endif +#ifdef WSAETIMEDOUT + case WSAETIMEDOUT: return "etimedout"; +#endif +#ifdef WSAECONNREFUSED + case WSAECONNREFUSED: return "econnrefused"; +#endif +#ifdef WSAELOOP + case WSAELOOP: return "eloop"; +#endif +#ifdef WSAENAMETOOLONG + case WSAENAMETOOLONG: return "enametoolong"; +#endif +#ifdef WSAEHOSTDOWN + case WSAEHOSTDOWN: return "ehostdown"; +#endif +#ifdef WSAEHOSTUNREACH + case WSAEHOSTUNREACH: return "ehostunreach"; +#endif +#ifdef WSAENOTEMPTY + case WSAENOTEMPTY: return "enotempty"; +#endif +#ifdef WSAEPROCLIM + case WSAEPROCLIM: return "eproclim"; +#endif +#ifdef WSAEUSERS + case WSAEUSERS: return "eusers"; +#endif +#ifdef WSAEDQUOT + case WSAEDQUOT: return "edquot"; +#endif +#ifdef WSAESTALE + case WSAESTALE: return "estale"; +#endif +#ifdef WSAEREMOTE + case WSAEREMOTE: return "eremote"; +#endif +#ifdef WSASYSNOTREADY + case WSASYSNOTREADY: return "sysnotready"; +#endif +#ifdef WSAVERNOTSUPPORTED + case WSAVERNOTSUPPORTED: return "vernotsupported"; +#endif +#ifdef WSANOTINITIALISED + case WSANOTINITIALISED: return "notinitialised"; +#endif +#ifdef WSAEDISCON + case WSAEDISCON: return "ediscon"; +#endif +#ifdef WSAENOMORE + case WSAENOMORE: return "enomore"; +#endif +#ifdef WSAECANCELLED + case WSAECANCELLED: return "ecancelled"; +#endif +#ifdef WSAEINVALIDPROCTABLE + case WSAEINVALIDPROCTABLE: return "einvalidproctable"; +#endif +#ifdef WSAEINVALIDPROVIDER + case WSAEINVALIDPROVIDER: return "einvalidprovider"; +#endif +#ifdef WSAEPROVIDERFAILEDINIT + case WSAEPROVIDERFAILEDINIT: return "eproviderfailedinit"; +#endif +#ifdef WSASYSCALLFAILURE + case WSASYSCALLFAILURE: return "syscallfailure"; +#endif +#ifdef WSASERVICE_NOT_FOUND + case WSASERVICE_NOT_FOUND: return "service_not_found"; +#endif +#ifdef WSATYPE_NOT_FOUND + case WSATYPE_NOT_FOUND: return "type_not_found"; +#endif +#ifdef WSA_E_NO_MORE + case WSA_E_NO_MORE: return "e_no_more"; +#endif +#ifdef WSA_E_CANCELLED + case WSA_E_CANCELLED: return "e_cancelled"; +#endif + default: + sprintf(errstrbuf, "unknown:%d", error); + return errstrbuf; + } +} + diff --git a/lib/ssl/c_src/esock_posix_str.h b/lib/ssl/c_src/esock_posix_str.h new file mode 100644 index 0000000000..53916c888a --- /dev/null +++ b/lib/ssl/c_src/esock_posix_str.h @@ -0,0 +1,28 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1999-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +/* esock_posix_str.h */ + +#ifndef ESOCK_POSIX_STR_H +#define ESOCK_POSIX_STR_H + +char *esock_posix_str(int error); + +#endif + diff --git a/lib/ssl/c_src/esock_ssl.h b/lib/ssl/c_src/esock_ssl.h new file mode 100644 index 0000000000..535e9a6491 --- /dev/null +++ b/lib/ssl/c_src/esock_ssl.h @@ -0,0 +1,110 @@ +/*<copyright> + * <year>1999-2008</year> + * <holder>Ericsson AB, All Rights Reserved</holder> + *</copyright> + *<legalnotice> + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson AB. + *</legalnotice> + */ +/* + * Purpose: Header file for adaptions to various SSL packages. + */ + +#ifndef ESOCK_SSL_H +#define ESOCK_SSL_H + +#include <sys/types.h> +#include <stdio.h> +#include "esock.h" + +typedef struct { + const char *compile_version;/* version of OpenSSL when compiling esock */ + const char *lib_version; /* version of OpenSSL in library */ +} esock_version; + +/* Variables to be set by certain functions (see below) */ +char *esock_ssl_errstr; + +/* Ephemeral RSA and DH */ +int ephemeral_rsa, ephemeral_dh; + +/* Protocol version (sslv2, sslv3, tlsv1) */ +int protocol_version; + +/* version info */ +esock_version *esock_ssl_version(void); + +/* ciphers info */ +char *esock_ssl_ciphers(void); + +/* seeding */ +void esock_ssl_seed(void *buf, int len); + +/* Initialization and finalization of SSL */ + +int esock_ssl_init(void); +void esock_ssl_finish(void); + +/* Freeing of SSL resources for a connection */ + +void esock_ssl_free(Connection *cp); + +/* Print error diagnostics to a file pointer */ + +void esock_ssl_print_errors_fp(FILE *fp); + +/* All functions below have to return >= 0 on success, and < 0 on + * failure. + * + * If the return indicates a failure (return value < 0) and the failure + * is temporary the error context (sock_errno()/sock_set_errno()) must + * be set to ERRNO_BLOCK. + * + * If the failure is permanent, the error context must be set to something + * else than ERRNO_BLOCK, and `esock_ssl_errstr' must be set to point to + * short diagnostic string describing the error. + */ + +int esock_ssl_accept_init(Connection *cp, void *listenssl); +int esock_ssl_connect_init(Connection *cp); +int esock_ssl_listen_init(Connection *cp); + +/* All functions below may involve non-blocking I/O with a temporary + * failure. Hence they have to have the error context set to + * ERRNO_BLOCK, or else have esock_ssl_errstr set to point to a + * diagnostic string, in case the return value is < 0. If the return + * value is 0, cp->eof and cp->bp are set, if appropritate. + */ + +int esock_ssl_accept(Connection *cp); +int esock_ssl_connect(Connection *cp); + +int esock_ssl_read(Connection *cp, char *buf, int len); +int esock_ssl_write(Connection *cp, char *buf, int len); + +int esock_ssl_shutdown(Connection *cp); + +/* Peer certificate */ + +int esock_ssl_getpeercert(Connection *cp, unsigned char **buf); +int esock_ssl_getpeercertchain(Connection *cp, unsigned char **buf); + +/* Sessions */ +int esock_ssl_session_reused(Connection *cp); + +/* Protocol version and cipher of established connection */ +int esock_ssl_getprotocol_version(Connection *cp, char **buf); +int esock_ssl_getcipher(Connection *cp, char **buf); + +#endif diff --git a/lib/ssl/c_src/esock_utils.c b/lib/ssl/c_src/esock_utils.c new file mode 100644 index 0000000000..0098a4f5f6 --- /dev/null +++ b/lib/ssl/c_src/esock_utils.c @@ -0,0 +1,150 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1999-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +/* + * Purpose: Safe memory allocation and other utilities. + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "esock_utils.h" + +static char *strtok_quote(char *s1, const char *s2); + + +void *esock_malloc(size_t size) +{ + void *p; + + p = malloc(size); + if (!p) { + fprintf(stderr, "esock_malloc: cannot alloc %d bytes\n", size); + exit(EXIT_FAILURE); + } + return p; +} + +void *esock_realloc(void *p, size_t size) +{ + void *np; + + np = realloc(p, size); + if (!np) { + fprintf(stderr, "esock_realloc: cannot realloc %d bytes\n", size); + exit(EXIT_FAILURE); + } + return np; +} + +void esock_free(void *p) +{ + free(p); +} + +/* Builds an argv array from cmd. Spaces and tabs within double quotes + * are not considered delimiters. Double quotes are removed. + * + * The return value is argc, and the pointer to char ** is set. argc + * is non-negative, argv[0], ..., argv[argc - 1] are pointers to + * strings, and argv[argc] == NULL. All argv[0], ..., argv[argc - 1] + * must be freed by the user, and also the argv pointer itself. + * + * Example: cmd = abc"/program files/"olle nisse, results in + * argv[0] = abc/program files/olle, argv[1] = nisse, argc = 2. + * + */ +int esock_build_argv(char *cmd, char ***argvp) +{ + int argvsize = 10, argc = 0; + char *args, *tokp, *argp; + char **argv; + + argv = esock_malloc(argvsize * sizeof(char *)); + args = esock_malloc(strlen(cmd) + 1); + strcpy(args, cmd); + tokp = strtok_quote(args, " \t"); + while (tokp != NULL) { + if (argc + 1 >= argvsize) { + argvsize += 10; + argv = esock_realloc(argv, argvsize * sizeof(char *)); + } + argp = esock_malloc(strlen(tokp) + 1); + strcpy(argp, tokp); + argv[argc++] = argp; + tokp = strtok_quote(NULL, " \t"); + } + esock_free(args); + argv[argc] = NULL; + *argvp = argv; + return argc; +} + +/* strtok_quote + * Works as strtok, but characters within pairs of double quotes are not + * considered as delimiters. Quotes are removed. + */ +static char *strtok_quote(char *s1, const char *s2) +{ + static char *last; + char *s, *t, *u; + + s = (s1) ? s1 : last; + if (!s) + return last = NULL; + + while (*s != '"' && *s != '\0' && strchr(s2, *s)) + s++; + t = s; + + while (1) { + if (*t == '"') { + t++; + while (*t != '"' && *t != '\0') + t++; + if (*t == '\0') { + last = NULL; + goto end; + } + t++; + } + while(*t != '"' && *t != '\0' && !strchr(s2, *t)) + t++; + if (*t == '\0') { + last = NULL; + goto end; + } else if (*t != '"') { + *t = '\0'; + last = t + 1; + goto end; + } + } +end: + /* Remove quotes */ + u = t = s; + while (*u) { + if (*u == '"') + u++; + else + *t++ = *u++; + } + *t = '\0'; + return s; +} + diff --git a/lib/ssl/c_src/esock_utils.h b/lib/ssl/c_src/esock_utils.h new file mode 100644 index 0000000000..99ed6c23e3 --- /dev/null +++ b/lib/ssl/c_src/esock_utils.h @@ -0,0 +1,32 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1999-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +#ifndef ESOCK_UTILS_H +#define ESOCK_UTILS_H + +#include <stdlib.h> + +void *esock_malloc(size_t size); +void *esock_realloc(void *p, size_t size); +void esock_free(void *p); +int esock_build_argv(char *cmd, char ***argvp); + +#endif + + diff --git a/lib/ssl/c_src/esock_winsock.h b/lib/ssl/c_src/esock_winsock.h new file mode 100644 index 0000000000..069782a18d --- /dev/null +++ b/lib/ssl/c_src/esock_winsock.h @@ -0,0 +1,36 @@ +/*<copyright> + * <year>2003-2008</year> + * <holder>Ericsson AB, All Rights Reserved</holder> + *</copyright> + *<legalnotice> + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson AB. + *</legalnotice> + */ +/* + * Purpose: Control winsock version and setting of FD_SETSIZE. + * + */ + +/* Maybe set FD_SETSIZE */ + +#ifdef ESOCK_WINSOCK2 +#include <winsock2.h> +#else +#include <winsock.h> +/* These are defined in winsock2.h but not in winsock.h */ +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 +#endif + |