Jump to content
Nytro

All-OS-OpenSSL-client-threaded-server-example

Recommended Posts

Posted

All-OS-OpenSSL-client-threaded-server-example

Example-C OpenSSL client and server example

EXAMPLE TEMPLATE

PRODUCT: OpenSSL Version 0.9.8a 11 Oct 2005

OP/SYS: Windows XP SP2

OpenVMS Alpha V8.2

Linux Fedora Core 5

HP-UX 11i

COMPONENT: OpenSSL run-time libraries and tools

SOURCE: Philippe Vouters

Fontainebleau/France

LOW COST HIGH-TECH PRODUCTS: http://techno-star.fr

DOWNLOAD LINKS:

OpenSSL for OpenVMS (Itanium, Alpha, VAX) can be dowloaded from

HP OpenVMS Systems

OpenSSL for HP-UX can be downloaded from

http://h20293.www2.hp.com/portal/swdepot/displayProductInfo.do?productNumber=OPENSSL11I

OpenSSL for Windows can be downloaded from

OpenSSL - Download OpenSSL - SSL, TLS, and a cryptography library. Perl or Cygwin needed for Win32 compile.

For you to simply execute the code showed hereafter, you may download:

../zip/bio_html.tar.gz

OVERVIEW:

This document shows a set of OpenSSL multi-threaded servers and a

single-threaded client that demonstrate a way to secure TCP/IP traffic

using OpenSSL encryption. Unlike conventional TCP/IP traffic, the OpenSSL

traffic cannot be decoded even by Ethereal. The RSA key is checked by the

servers and the X509 certificate is exchanged with the dedicated remote

client in the following example. The sslserver.c code for OpenVMS, Unixes is

also able to accept connections on user specified network IP interfaces. See

its code in the main routine body part.

*** CAUTION ***

These sample programs have been tested using Linux Fedora Core 5, OpenVMS

Alpha V8.2 and Windows XP and HP-UX B.11.11. However, we cannot guarantee

its effectiveness because of the possibility of error in transmitting or

implementing it. It is meant to be used as a template for writing your own

programs, and may require modification for use on your system.

PROGRAMS NOTES:

On Windows:

     Add C:\openssl\include to the INCLUDE environment variable
Add C:\openssl\lib to the LIB environment variable
Add C:\openssl\bin to the PATH environment variable

Enter the following command:

     C:\> nmake

On Unix:

     $ gmake -f makefile_unix

You can eventually do the following command prior to gmake:

     $ export USE_PTHREAD_KILL=yes

Without this export, this uses the pthread_cancel()/pthread_testcancel() calls.

You may also

     $ export BAD_PRACTICE=yes

Running the code with this above export defined emits a warning.

On OpenVMS:

     $ @makefile

Use the following syntax to generate two pem files prior to executing the programs

each on one terminal:

On OpenVMS:

     $ openssl :== $SSL$EXE:openssl
$ openssl req -newkey rsa:1024 -x509 -keyout key.pem -out root.pem -
-config /ssl$root/openssl-vms.cnf

Edit root.pem so that the first and last lines appear as follows if the above command

is not generating the certificate header correctly:

     -----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----

This should be done when using the following:

$ openssl version
OpenSSL 0.9.6g [engine] 9 Aug 2002

On Windows:

C:\> openssl req -newkey rsa:1024 -x509 -keyout key.pem -out root.pem -config \openssl\apps\openssl.cnf

The -config option is at least necessary under the following:

C:\Documents and Settings\vouters>openssl version
OpenSSL 0.9.7c 30 Sep 2003

Use the following command on Unix:

     $ openssl req -newkey rsa:1024 -x509 -keyout key.pem -out root.pem
Generating a 1024 bit RSA private key
......................++++++
..............................................++++++
writing new private key to 'key.pem'
Enter PEM pass phrase:< this must match the const char *pass in sslserver.c
or the #define PASSWORD in sslserver_windows.c >
Verifying - Enter PEM pass phrase:
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [GB]:<2 letters country mnemonic>
State or Province Name (full name) [Berkshire]:<your State or Province>
Locality Name (eg, city) [Newbury]:<your town's name>
Organization Name (eg, company) [My Company Ltd]:<the name of your company>
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:localhost <-- <
Important because bio_html request is https://localhost:60000/ >
Email Address []:<your email address>

Concatenate the two resultant files key.pem and root.pem into server.pem

On Unix:

     $ cat key.pem root.pem > server.pem

On VMS:

     $ copy/concatenate key.pem,root.pem server.pem

On Windows:

     C:\> copy key.perm/A + root.pem/A server.pem/A

Type the following on one terminal:

Parameters between square brackets are optional. If no parameters are supplied

the program binds to 0.0.0.0:60000. IP Interface optional parameter is the

IPv4 IP address of the computer's network interface you wish to limit the

traffic to.

On Unix:

     $ ./sslserver [IP Interface1] ... [IP InterfaceN] [port]

On OpenVMS:

     $ run sslserver
or
$ sslserver :== $disk:[directory]sslserver.exe
$ sslserver [IP Interface1] ... [IP InterfaceN] [port]

On Windows:

     C:\> sslserver_windows

Do the following on another terminal on the same computer:

On Unix:

     $ ./bio_html https://localhost:60000/

On Windows:

     C:\> bio_html https://localhost:60000/

On OpenVMS:

     $ bio_html :== $disk:[directory]bio_html.exe
$ bio_html https://localhost:60000/

Instead of running the bio_html Web browser emulator, you may use any real

Web browser and introduce the following URL:

     https://host:60000/

To check the certificate aspect, you may use the openssl provided client with

the command:

     openssl s_client -connect localhost:60000 -state -showcerts

ALGORYTHM:

     CLIENT                      SERVERS
___________________________________

TCP/IP connect ----------> TCP/IP accept
SSL_connect ----------> SSL_accept
SSL_write ----------> SSL_read
SSL_read <---------- SSL_write (echo)
SSL_read SSL_read

two scenarios:

either

(case close SSL connection from the Web browser)

     BIO_free_all   ---------->  SSL_read returning 0
BIO_free_all
(this shutdowns and closes the socket)
or
(socket activity timeout in the sslserver)
SSL_read <---------- BIO_free_all
SSL_shutdown
close socket

ADVISE:

If you are running VMS or any Unix, you will need the sslserver.c. This code is

over documented, hiding the actual code. With the commentss he got, the author

of this article feels necessary to strongly document the solution. Also as the

same C source file runs on many platforms having each different possibilities

and constraints, there are lots of conditional preprocessing (all the #ifdef's).

The best advise the author may give the reader is to preprocess this source

file. You will then get a C source code containing only C statements that fits

your own preprocess conditions. To solely use the preprocessor, you would:

On Linux:

        $ cpp [-DUSE_PTHREAD_KILL [-DBAD_PRATICE]] -P sslserver.c sslserver.cc

Each item surrounded by square brackets is optional.

On OpenVMS systems:

       $ cc/preprocess=sslserver.cc/noline sslserver.c

The output preprocessor file is sslserver.cc. You can edit view it from the

bottom and climb lines up to the real start of this sslserver.c code. You will

see how simple both OpenSSL and C phtreads programming can summarize using a

supported or unsupported way that proves to work running it. To fully appreciate

this summary with the preprocessor output, you must have a strong TCP/IP

programming culture and not forget OpenSSL is based upon TCP/IP. You should

as well possess a strong real-time programming culture on various Real-Time

operating systems. The best profile is a programmer with 10 years programming

experience whose job nature is either developping code or assisting developers.

If you are used to concurrent programming on Windows, the algorythm of the

Windows sslserver.c code below is an almost direct translation using Windows

programming of the OpenSSL/pthreaded OpenVMS version because the

run-time constraints are almost identical.

MAKEFILES:

********
* VMS makefile.com
********
$CC/POINTER_SIZE=32/PREFIX_LIBRARY_ENTRIES=ALL_ENTRIES sslserver.C
$LINK/THREAD sslserver.OBJ,sys$input:/OPT
SYS$LIBRARY:SSL$LIBCRYPTO_SHR32.EXE/SHARE
SYS$LIBRARY:SSL$LIBSSL_SHR32.EXE/SHARE

$CC/POINTER_SIZE=32/PREFIX_LIBRARY_ENTRIES=ALL_ENTRIES bio_html.C
$CC/POINTER_SIZE=32/PREFIX_LIBRARY_ENTRIES=ALL_ENTRIES encode.C
$LINK bio_html.OBJ,encode.OBJ,sys$input:/OPT
SYS$LIBRARY:SSL$LIBCRYPTO_SHR32.EXE/SHARE
SYS$LIBRARY:SSL$LIBSSL_SHR32.EXE/SHARE

$exit

********

* Windows XP Makefile

********

PROGRAM1=bio_html
PROGRAM2=sslserver_windows
DEFINES=/D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS"
CCFLAGS=/c /MD /W3 /GX /O1 /Tc
LIBS=libeay32.lib ssleay32.lib kernel32.lib user32.lib advapi32.lib ws2_32.lib
LDFLAGS=/LIBPATH:"\openssl\bin" /incremental:yes /subsystem:console
OBJS=encode

all: $(PROGRAM1).exe $(PROGRAM2).exe

$(PROGRAM1).exe: $(PROGRAM1).c $(OBJS).obj
cl $(DEFINES) $(CCFLAGS) $(PROGRAM1).c
link $(LDFLAGS) $(PROGRAM1).obj $(OBJS) $(LIBS)

$(PROGRAM2).exe: $(PROGRAM2).c $(OBJS).obj
cl $(DEFINES) $(CCFLAGS) $(PROGRAM2).c
link $(LDFLAGS) $(PROGRAM2).obj $(LIBS)

$(OBJS).obj: $(OBJS).c
cl $(DEFINES) $(CCFLAGS) $(OBJS).c

clean:
- if exist $(PROGRAM1).ilk del $(PROGRAM1).ilk
- if exist $(PROGRAM1).obj del $(PROGRAM1).obj
- if exist $(PROGRAM2).ilk del $(PROGRAM2).ilk
- if exist $(PROGRAM2).obj del $(PROGRAM2).obj
- if exist $(OBJS).obj del $(OBJS).obj

********

* Linux and HP-UX Makefile

********

PROGRAM1=bio_html
PROGRAM2=sslserver
DEFINES_HP-UX=-D_XOPEN_SOURCE_EXTENDED
DEFINES_OSF1=-D_OSF_SOURCE=500 -D_XOPEN_SOURCE=500
DEFINES_Linux=
CCFLAGS_HP-UX=-mt
CCFLAGS_OSF1=-std -pthread
CCFLAGS_Linux=-pthread
LIBS_HP-UX=-lssl -lcrypto
LIBS_Linux=-lssl -lcrypto
LIBS_OSF1=-lssl -lcrypto
LDFLAGS=
OBJS=encode
#
OS= $(shell uname -a | awk '{print $$1}')
CCFLAGS=$(CCFLAGS_$(OS))
LIBS=$(LIBS_$(OS))
DEFINES=$(DEFINES_$(OS))
#
ifneq ($(USE_PTHREAD_KILL),)
ifneq ($(BAD_PRACTICE),)
DEFINES += -DBAD_PRACTICE
endif
DEFINES += -DUSE_PTHREAD_KILL
endif

all: $(PROGRAM1) $(PROGRAM2)

$(PROGRAM1): $(PROGRAM1).c $(OBJS).o
$(CC) -o $(PROGRAM1) $(DEFINES) $(CCFLAGS) $(OBJS).o $(LIBS) $(PROGRAM1).c

$(PROGRAM2): $(PROGRAM2).c
$(CC) -o $(PROGRAM2) $(DEFINES) $(CCFLAGS) $(LIBS) $(PROGRAM2).c

$(OBJS).o: $(OBJS).c
$(CC) -c $(OBJS).c

clean:
rm -f *.o $(PROGRAM1) $(PROGRAM2)

***********

PROGRAMS:

********

* sslserver.c for Linux/HP-UX and OpenVMS

********

/*
* ? Copyright 1976, 2005 Hewlett-Packard Development Company, L.P.
*
* Confidential computer software. Valid license from HP and/or
* its subsidiaries required for possession, use, or copying.
*
* Consistent with FAR 12.211 and 12.212, Commercial Computer Software,
* Computer Software Documentation, and Technical Data for Commercial
* Items are licensed to the U.S. Government under vendor's standard
* commercial license.
*
* Neither HP nor any of its subsidiaries shall be liable for technical
* or editorial errors or omissions contained herein. The information
* in this document is provided "as is" without warranty of any kind and
* is subject to change without notice. The warranties for HP products
* are set forth in the express limited warranty statements accompanying
* such products. Nothing herein should be construed as constituting an
* additional warranty.
*/
#include <errno.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> /* change hostent to comply with BSD 4.3 */
#include <arpa/inet.h>
#include <pthread.h>
#include <signal.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#ifdef __VMS
#include <unixio.h>
#else
#include <unistd.h>
#endif
#if defined (_XOPEN_SOURCE_EXTENDED) && defined (__hpux)
#include <sys/byteorder.h>
#endif

#ifndef PTHREAD_STACK_MIN
/*
* PTHREAD_STACK_MIN is undefined on my SunOS V5.8
* likely due to out dated patches.
*/
#define PTHREAD_STACK_MIN 65535
#endif

#if !defined (USE_PTHREAD_KILL) && defined (BAD_PRACTICE)
/*
* BAD_PRACTICE is only valid with USE_PTHREAD_KILL
*/
#undef BAD_PRACTICE
#endif

const int AUXSERVER_PORT=60000;
const int MAX_MESSAGE_SIZE=50000;
const char *KEYFILE="server.pem";
const char *CA_LIST="root.pem";
const char *HTML_PREFIX="HTTP/1.1 200 OK\n\
Content-Type:text/html\n\n\
<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n\
<HTML>\n\
<HEAD>\n\
<TITLE>sslserver echo</TITLE>\n\
<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=iso-8859-1\">\n\
</HEAD>\n\
<BODY>\n\
<PRE>\n";
const char *HTML_SUFFIX="</PRE>\n\
</BODY>\n\
</HTML>\n";

#if defined (__VMS)
#define socklen_t unsigned int
#endif
/*
* A printf is not guaranteed to be atomic on all systems.
* Hence the ScreenMutex
*/
pthread_mutex_t ScreenMutex;
const char *pass="password";

#if defined (BAD_PRACTICE)
/*
* The #warning preprocessor directive is Linux specific
*/
void warning(void){
printf ("******** WARNING **********\n");
#if defined (__osf__)
printf ("pthread_sigmask has an undefined behavior in \
signal-handling functions on Tru64\n");
#endif
printf("pthread_exit has an undefined behavior in signal-handling functions.\n\
It is better advised to not -DBAD_PRACTICE.\n\
This call in the context of this program appears to behave correctly on \n\
Linux FC5, HP-UX 11iV1, HP Tru64 V5.1B PK4\n");
printf ("\nRead comments in SIGUSR1_handler routine\n\n");
printf ("***************************\n");
}
#endif

static int password_cb(char *buf,int num,int rwflag, void *userdata) {
if (num<strlen(pass)+1) return 0;
strcpy(buf,pass);
return(strlen(pass));
}
typedef struct {
int s;
int http_request;
SSL *ssl;
BIO *sbio;
char *message;
pthread_t connection_thread;
pthread_t watchdog_thread;
#if !defined (USE_PTHREAD_KILL) || defined (BAD_PRACTICE)
int signalled; /* to handle possible spurious wakeups */
pthread_mutex_t mutex;
pthread_cond_t cond;
#endif
} arg_t;

/*
* Small error handling routine, that prints out the error, plus OpenSSL
* relevant information upon why the call failed.
*/
void berr_exit(char *str){
fprintf(stderr,"%s\n",str);
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);

}

void finish(void *ptr){
arg_t *arg =(arg_t *)ptr;
int retval;

/*
* Shutdown SSL connection and close socket.
*/
if (arg->http_request)
SSL_write(arg->ssl,HTML_SUFFIX,strlen(HTML_SUFFIX));
if (arg->message)
free(arg->message);
BIO_free_all(arg->sbio);
/*
* wait until watchdog thread has termined.
* If the SSL_accept returns in error (because
* one specify http instead of https), it calls
* pthread_exit which will invoke this
* finish routine. As the *arg is allocated in main
* with a calloc, arg->watchdog_thread will be NULL.
* Free used resources.
*/
if (arg->watchdog_thread){
/*
* Unlocking a mutex which is unlocked can yield to
* unpredictable results. The mutex is locked again
* after the pthread_cond_timedwait and is
* unlocked in case of timeout.
*/
#if defined (BAD_PRACTICE)
/*
* a pthread_kill on a pthread_cond_wait has an
* undefined behavior regarding the mutex lock.
*/
pthread_mutex_trylock(&arg->mutex);
#endif
#if !defined (USE_PTHREAD_KILL) || defined (BAD_PRACTICE)
pthread_mutex_unlock(&arg->mutex);
#endif
pthread_join(arg->watchdog_thread,NULL);
}
#if !defined (USE_PTHREAD_KILL) || defined (BAD_PRACTICE)
pthread_mutex_destroy(&arg->mutex);
pthread_cond_destroy(&arg->cond);
#endif
free(arg);
}
#if defined (BAD_PRACTICE)
void SIGUSR1_handler(int sig){
/*
* Here is a good description in the book THREADTIME
* (see the REFERENCE section)
* From CHAPTER 7
* Threads and Signals
* Async-Signal Safe Thread Functions
* 2nd paragraph.

* ===================
*
* pp. 181:
*
* What is an async-signal safe function? It is a function that may be
* invoked from a signal handler. No function should be considered
* async-signal safe unless it is explicitly stated to be safe.
*
* None of the pthread_*() functions are async-signal safe. Do not call any
* of these functions from within a signal handler. Initially, this may not
* seem like a severe restriction. However, a thread may want to terminate
* upon receipt of a specific signal. A thread cannot safely call
* pthread_exit() from a signal handler.
* ====================
*
* See also:
*
* http://mailgate.supereva.com/comp/comp.programming.threads/msg12068.html
*
* From the following URL:
* http://www.calpoly.edu/cgi-bin/man-cgi?sigaction+2
*
* The following table defines a set of functions that are either
* reentrant or not interruptible by signals. Therefore
* applications may invoke them, without restriction, from
* signal-catching functions:
*
* So what about adding the following small code before pthread_exit to prevent it
* from being interruptible by signals ?
sigset_t set;

sigfillset(&set);
sigprocmask(SIG_BLOCK,&set,NULL);
*
* On Linux FC5, the following is specified:
* The use of sigprocmask() is unspecified in a multithreaded process;
* see pthread_sigmask(3).
* The (pthread_sigmask) function shall be equivalent to sigprocmask(),
* without the restriction the call be made in a single-threaded process.
*
* On HP-UX, man sigprocmask specifies the following:
* APPLICATION USAGE
* Threads Considerations
* Since each thread maintains its own blocked signal mask, sigprocmask()
* modifies only the calling thread's blocked signal mask.
*
* For more information regarding signals and threads, refer to
* signal(5).
*
* LWP (Lightweight Processes) Considerations
* sigprocmask() modifies only the calling LWP's blocked signal mask.
*
* On Tru64 V5.1B, man 4 signal indicates:
*
* When signal-catching functions are invoked asynchronously with process exe-
* cution, the behavior of some of the functions defined by this standard is
* unspecified if they are called from a signal-catching function. The fol-
* lowing set of functions are reentrant with respect to signals (that is,
* applications can invoke them, without restriction, from signal-catching
* functions):
*
* _exit() access() alarm() chdir()
* chmod() chown() close() creat()
* dup2() dup() exec() fcntl()
* fork() fstat() getegid() geteuid()
* getgid() getgroups() getpgrp() getpid()
* getppid() getuid() kill() link()
* lseek() mkdir() mkfifo() open()
* pause() pipe() read() rename()
* rmdir() sem_post() setgid() setpgrp()
* setuid() sigaction() sigaddset() sigdelset()
* sigfillset() siginitset() sigismember() signal()
* sigpending() sigprocmask() sigsuspend() sleep()
* statx() tcdrain() tcflow() tcflush()
* tcgetattr() tcgetprgp() tcsendbreak() tcsetattr()
* tcsetpgrp() time() times() umask()
* uname() unlink() ustat() utime()
* wait2() wait() write()
*
* All other system calls should not be called from signal-catching functions
* since their behavior is undefined.
*
* Still on Tru64, man sigprocmask shows it acts on process whereas pthread_sigmask
* acts on a thread.
* How useful a pthread_kill can be on Tru64 unless it is used with a sigwait ???
*/
sigset_t set;

sigfillset(&set);
#if defined (__hpux)
sigprocmask(SIG_BLOCK,&set,NULL);
#elif defined (__linux__)
pthread_sigmask(SIG_BLOCK,&set,NULL);
#elif defined (__osf__)
pthread_sigmask(SIGBLOCK,&set,NULL);
#endif
pthread_exit(NULL);
}
/*
* The pthread_cond_timedwait can be interrupted by a SIGUSR1 signal
* from the the serve_connection thread.
* This call is NOT async-signal safe. The pthread_exit inside the
* SIGUSR1_handler also not being async-signal safe, this leads to an
* undefined behavior on all Unixes including Linux.
*/
void *connection_watchdog(void *ptr){
arg_t *arg =(arg_t *)ptr;
struct timespec delta;
struct timespec abstime;
int ret;
struct timeval tm;
/*
* On Unix systems the signal mask is inherited from its creator.
* As we pthread_kill(this_thread,SIGUSR1), unblock this signal.
*/
sigset_t set;
sigemptyset(&set);
sigaddset(&set,SIGUSR1);
pthread_sigmask(SIG_UNBLOCK,&set,NULL);
delta.tv_sec=2;
delta.tv_nsec=0;
for ({
gettimeofday(&tm,NULL);
abstime.tv_sec = delta.tv_sec+tm.tv_sec;
abstime.tv_nsec = delta.tv_nsec+tm.tv_usec*1000;
if (abstime.tv_nsec >= 1000000000){
++abstime.tv_sec;
abstime.tv_nsec -= 1000000000;
}
pthread_mutex_lock(&arg->mutex);
/*
* From HP-UX pthread_cond_timedwait man
* Spurious wakeups may occur when waiting on
* a condition variable. A spurious wakeup occurs when a thread returns
* from a condition wait when it should really continue waiting. A normal
* signal being delivered to a thread may cause a spurious wakeup during
* a condition wait. Since the return values from pthread_cond_wait()
* and pthread_cond_timedwait() do not imply anything about the value of
* the predicate, the predicate should be re-evaluated.
*
*/
ret=0;
while ((!arg->signalled) && (ret==0))
ret=pthread_cond_timedwait(&arg->cond,&arg->mutex,&abstime);
arg->signalled=0;
/*
* If timeout elapsed, just cancel the serve_connection thread
* It shall do socket cleanups
*/
if (ret==ETIMEDOUT){
pthread_kill(arg->connection_thread,SIGUSR1);
return(NULL);
}
pthread_mutex_unlock(&arg->mutex);
}
}
#else /* Good pratice */

#if !defined (USE_PTHREAD_KILL)
void *connection_watchdog(void *ptr){
arg_t *arg =(arg_t *)ptr;
struct timespec delta;
struct timespec abstime;
int ret;
struct timeval tm;

delta.tv_sec=2;
delta.tv_nsec=0;
for ({
gettimeofday(&tm,NULL);
abstime.tv_sec = delta.tv_sec+tm.tv_sec;
abstime.tv_nsec = delta.tv_nsec+tm.tv_usec*1000;
if (abstime.tv_nsec >= 1000000000){
++abstime.tv_sec;
abstime.tv_nsec -= 1000000000;
}
pthread_mutex_lock(&arg->mutex);
/*
* From HP-UX pthread_cond_timedwait man
* Spurious wakeups may occur when waiting on
* a condition variable. A spurious wakeup occurs when a thread returns
* from a condition wait when it should really continue waiting. A normal
* signal being delivered to a thread may cause a spurious wakeup during
* a condition wait. Since the return values from pthread_cond_wait()
* and pthread_cond_timedwait() do not imply anything about the value of
* the predicate, the predicate should be re-evaluated.
*/
ret=0;
while ((!arg->signalled) && (ret==0))
ret=pthread_cond_timedwait(&arg->cond,&arg->mutex,&abstime);
arg->signalled=0;
/*
* If timeout elapsed, just cancel the serve_connection thread
* It shall do socket cleanups
*/
if (ret==ETIMEDOUT){
pthread_cancel(arg->connection_thread);
return(NULL);
}
pthread_mutex_unlock(&arg->mutex);
}/*end for */
}
#else /* good practice and use pthread_kill */
void *connection_watchdog(void *ptr){
arg_t *arg =(arg_t *)ptr;
/*
* A good practice when using signals in multi-threaded application
* is to use the pthread_kill/pthread_sigmmask/sigwait. No other calls
* should be considered.
* CAUTION:
* Special care ought to be taken with such a method which indeed works
* fine as long as there is one client of this threaded server. This is
* because the SIGALRM signal raised by alarm in this case ought to be common
* to all threads that run this code. The signal handling by Unix is very
* likely not thread-safe, but only process-safe.
*/
sigset_t set;
int sig;

sigemptyset(&set);
sigaddset(&set,SIGUSR1);
sigaddset(&set,SIGALRM);
sigaddset(&set,SIGUSR2);
for({
alarm(2);
sigwait(&set,&sig);
alarm(0);
switch (sig){
case SIGUSR2:
continue;
case SIGALRM:
pthread_cancel(arg->connection_thread);
case SIGUSR1:
return(NULL);
}
}
}
#endif /* #else !defined (USE_PTHREAD_KILL) */
#endif /* else defined (BAD_PRACTICE) */
/*
* This is our per socket thread.
*/
void *serve_connection (void *ptr){
arg_t *arg =(arg_t *)ptr;
pthread_attr_t attr;
int len=0;
char *current_cipher_name;
#if defined (__VMS)
struct timeval timeout={0,10000};
fd_set rmask;
#endif
#if defined (BAD_PRACTICE)
sigset_t set;

/*
* On Unix systems the signal mask is inherited from its creator.
*/
sigemptyset(&set);
sigaddset(&set,SIGUSR1);
#endif
pthread_cleanup_push(finish,ptr);
/*
* initialize the synchronization pthread mutex
* and condition.
*/
#if !defined (USE_PTHREAD_KILL) || defined (BAD_PRACTICE)
/*
* Synchronization needed for the pthread_cond_timedwait
* These two calls must occur once the finish cleanup handler
* has been established and before any pthread_exit/pthread_testcancel
* or the creation of the watchdog timer which may pthread_kill this thread.
* Doing this initialization here is safe.
*/
pthread_mutex_init(&arg->mutex,NULL);
pthread_cond_init(&arg->cond,NULL);
#endif
/*
* Complete the SSL connection from the remote peer.
*/
arg->sbio=BIO_new_socket(arg->s,BIO_CLOSE);
SSL_set_bio(arg->ssl,arg->sbio,arg->sbio);
/*
* Choose the encryption algorythm by selecting a cipher.
* As bio_html is a SSL V2 client, one must choose a sslv2 cipher.
* To get available ciphers to choose for this application, enter the
* command:
* openssl ciphers -v -ssl2
* The DES-CBC3-MD5 cipher is a sslv2 cipher and can only be used
* with a sslv2 client.
* Here we let the client chose its cipher or have OpenSSL chose one
* during the SSL_accept/SSL_connect negociation.
*/
/*
if (SSL_set_cipher_list(arg->ssl,"DES-CBC3-MD5") <= 0)
berr_exit("cipher could not be set");
*/
/*
* This server will send its X509 certificate to the client when the
* connection is accepted, but using SSL_VERIFY_NONE, this program does
* not require a certificate from the remote SSL peer.
*/
SSL_set_verify(arg->ssl,SSL_VERIFY_NONE,NULL);
/*
* Do accept the SSL connection.
*/
if ((len=SSL_accept(arg->ssl))<=0){
fprintf(stderr,"SSL accept error\n");
ERR_print_errors_fp(stderr);
pthread_exit(NULL);
}
current_cipher_name=(char *)SSL_get_cipher_name(arg->ssl);
pthread_mutex_lock(&ScreenMutex);
printf("Using cipher %s\n",current_cipher_name);
pthread_mutex_unlock(&ScreenMutex);
/*
* Start the connection activity watchdog.
*/
pthread_attr_init(&attr);
/*
* This call is not necessary as it is a default. However it is
* there to highlight the fact we pthread_join the watchdog thread
* in the finish cleanup handler.
*/
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE);
pthread_create(&arg->watchdog_thread,&attr,connection_watchdog,arg);
/*
* endless loop waiting for activity on the socket.
*/
for ({
#if defined (__VMS)
FD_ZERO(&rmask);
FD_SET(arg->s,&rmask);
/*
* A select is usually cancelable on most Unix by a pthread_cancel.
* Look at pthread man to ensure it is actually cancelable.
* On Unixes, the code can be much simplified, withdrawing the need for
* the select, as behind the SSL_read ought to be either a recv or read
* eventually with a select which are all cancelable points on most
* Unixes.
*
* On VMS, select/read/recv are not currently cancelable points. Hence,
* for OpenVMS, the use of a select's timeout in order to execute the
* portable pthread_testcancel() which will test a pending
* pthread_cancel and will, via the pthread_cleanup_push/pop execute
* the finish routine which closes the SSL link.
*
* The bio_html Web browser emulator or an actual Web browser such as
* Firefox or Internet Explorer partner program blocking on a SSL_read
* will therefore be unblocked, the SSL_read returning zero and
* gracefully end.
*/
switch (select(arg->s+1,&rmask,NULL,NULL,&timeout)){
case 0: pthread_testcancel();
continue;
case -1: perror("select");
pthread_exit(NULL);
break;
default:
#elif !defined (__unix__) && !defined (__unix)
#error "Unsupported operating system"
#endif
#if defined (USE_PTHREAD_KILL) && !defined (BAD_PRACTICE)
/*
* Unblock the sigwait with the SIGUSR2 signal to tell
* we have activity.
*/
pthread_kill(arg->watchdog_thread,SIGUSR2);
#else
/*
* Signal the watchdog thread we have socket activity
*/
pthread_mutex_lock(&arg->mutex);
arg->signalled=1;
pthread_cond_signal(&arg->cond);
pthread_mutex_unlock(&arg->mutex);
#endif
/*
* Read the available data on the SSL link.
* Allocate the buffer here with a calloc, just like
* in any real SSL application. We will receive len
* character and we later strcat(msg,arg->message).
* the received data is not necessarily zero terminated.
* This message buffer is freed just before the for loop
* end.
*/
arg->message=(char *)calloc(MAX_MESSAGE_SIZE,sizeof(char));
#if defined (BAD_PRACTICE)
/*
* Change this thread signal mask so we can be killed
* with the SIGUSR1 signal if the SSL_read is hanging.
*/
pthread_sigmask(SIG_UNBLOCK,&set,NULL);
#endif
/*
* On Linux, the SSL_read man says:
* The underlying BIO is blocking, SSL_read will return,
* once the read operation has been finished or an error
* occured, except when a renegociation take place, in
* which case a SSL_ERROR_WANT_READ may occur.
*/
len=SSL_read(arg->ssl,arg->message,MAX_MESSAGE_SIZE);
#if defined (BAD_PRATICE)
/*
* Block the SIGUSR1 signal again.
*/
pthread_sigmask(SIG_BLOCK,&set,NULL);
#endif
#if !defined (BAD_PRACTICE)
/*
* We come here either because the SSL_read has been
* canceled, either after a completed alarm(2) or
* after a cond_timedwait returning a timeout.
*
* The testcancel is compulsary on HP-UX and Tru64, but
* not on Linux FC5. The fprintf (write) is a cancelation
* point. On HP-UX and Tru64 this leaves the ScreenMutex
* locked.
*/
#if !defined (__linux__) && !defined (HPUX_READ_EINTR_PATCH)
pthread_testcancel();
#endif
#endif
if (len <=0){
pthread_mutex_lock(&ScreenMutex);
fprintf(stdout,"Client disconnected\n");
pthread_mutex_unlock(&ScreenMutex);
/*
* Abort the pthread_cond_timedwait or the sigwait
* waiting watchdog thread.
*/
#if defined (USE_PTHREAD_KILL)
pthread_kill(arg->watchdog_thread,SIGUSR1);
#else
pthread_cancel(arg->watchdog_thread);
#endif
pthread_exit(NULL);
}
pthread_mutex_lock(&ScreenMutex);
fprintf(stdout,"Received %.*s\n",len,arg->message);
pthread_mutex_unlock(&ScreenMutex);
/*
* Echo via the SSL link the data just received.
*/
{
int i;
char *msg;
int sent_html=0;
int size=0;
if (strstr(arg->message,"GET / HTTP"))
arg->http_request=1;
for (i=0;i<2;i++){
switch(i){
case 0: if ((arg->http_request) && !(sent_html)){
size=strlen(HTML_PREFIX);
}
break;
case 1: size+=len;
break;
}
}/* end for */
msg=(char *)calloc(size+1,sizeof(char));
if (!sent_html){
strcpy(msg,HTML_PREFIX);
sent_html++;
}
strcat(msg,arg->message);
size=SSL_write(arg->ssl,msg,strlen(msg));
free(msg);
if (size<=0){
/*
* The remote partner exited.
* Exit the thread meanwhile invoking the
* finish routine.
*/
pthread_mutex_lock(&ScreenMutex);
fprintf(stdout,"Client disconnected\n");
pthread_mutex_unlock(&ScreenMutex);
/*
* Abort the pthread_cond_timedwait or the sigwait
* waiting watchdog thread.
*/
#if defined (USE_PTHREAD_KILL)
pthread_kill(arg->watchdog_thread,SIGUSR1);
#else
pthread_cancel(arg->watchdog_thread);
#endif
pthread_exit(NULL);
}
}
free(arg->message);
arg->message=NULL;
#if defined (__VMS)
}/* end switch */
#endif
}/* end for */
pthread_cleanup_pop(1);
return(NULL);
}
long get_ia(char *str)
{
unsigned long ia;
unsigned long temp[4];
char ch;

for( {
if (sscanf (str, "%d.%d.%d.%d%c",
&temp[0],&temp[1],&temp[2],
&temp[3],&ch) != 4) return (-1);

if ((temp[0] > 255) ||
(temp[1] > 255) ||
(temp[2] > 255) ||
(temp[3] > 255)) return(-1);

break;
}
ia = temp[0] + (temp[1] << 8) + (temp[2] << 16) + (temp[3] << 24);
return (ia);
}
/*
* The routine setup_sockets enables to create sockets, bind and listen on them
* on a variable number of computer's network interfaces, specified on the
* command line . The last parameter in the command line is an optional port.
* If no argument is specified then it creates one socket bound to
* 0.0.0.0:default_port. For a program, it enables specifying the following
* syntax : $ ./<prog> [IP of Interface1 .. [IP of InterfaceN]] [port]
* All arguments between square brackets are optional.
*/
int setup_sockets(int argc, char **argv, int *s, unsigned short default_port)
{
struct sockaddr_in s_name; /* Address struct for socket2.*/
int on = 1,i;

argc--;
argv++;
if (argc == 0){
i=argc;
memset(&s_name,0,sizeof(s_name));
s_name.sin_port = htons(default_port) ;
s_name.sin_addr.s_addr = htonl(INADDR_ANY);
s_name.sin_family = AF_INET ;
if ((s[i] = socket (AF_INET, SOCK_STREAM, 0)) == -1){
perror("socket error") ;
exit(EXIT_FAILURE);
}
if (setsockopt(s[i], SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0){
perror("setsockopt error") ;
exit(EXIT_FAILURE);
}
if (bind (s[i],(struct sockaddr *)&s_name, sizeof (s_name))<0){
perror("bind error") ;
exit(EXIT_FAILURE);
}
if (listen (s[i], 5) < 0){
perror("listen error") ;
exit(EXIT_FAILURE);
}
}
else{
for (i=0;i<argc;i++){
memset(&s_name,0,sizeof(s_name));
s_name.sin_family = AF_INET ;
if ((argc >= 1) && ((get_ia(argv[argc-1]) == -1)))
s_name.sin_port = htons(atoi(argv[argc-1])) ;
else
s_name.sin_port = htons(default_port) ;
if ((argc == 1) && ((get_ia(argv[argc-1]) == -1)))
s_name.sin_addr.s_addr = htonl(INADDR_ANY);
else
if ((s_name.sin_addr.s_addr =get_ia(argv[i])) == -1)
break;
if ((s[i] = socket (AF_INET, SOCK_STREAM, 0)) == -1){
perror("socket error") ;
exit(EXIT_FAILURE);
}
if (setsockopt(s[i], SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0){
perror("setsockopt error") ;
exit(EXIT_FAILURE);
}
if (bind (s[i],(struct sockaddr *)&s_name, sizeof (s_name))<0){
perror("bind error") ;
exit(EXIT_FAILURE);
}
if (listen (s[i], 5) < 0){
perror("listen error") ;
exit(EXIT_FAILURE);
}
}/* end for */
}/* end else */
argc=i;
if (argc == 0) argc++;
return argc;
}
int main(int argc,char **argv)
{
int s[10]; /* sockets */
int i;
struct sockaddr_in s_name; /* Address struct for socket2.*/
socklen_t namelength;
fd_set rd_msk;
arg_t *arg;
pthread_t dynthread;
SSL_CTX *ctx;
pthread_attr_t attr;
#if defined (__unix__) || defined (__unix)
sigset_t set;
#endif
/*
* For Unix systems we use a pthread_kill instead of a pthread_cancel
* So set signal mask and use pthread_sigmask to block or unblock
* SIGUSR1 signal. If good practice, then block SIGALRM as well.
*/
#if defined (BAD_PRACTICE)
struct sigaction action;

warning();
#endif
fprintf(stderr," beginning of the program\n ");
argc=setup_sockets(argc,argv,s,AUXSERVER_PORT);
pthread_mutex_init(&ScreenMutex,NULL);
SSL_load_error_strings();
// Initialize the SSL library.
SSL_library_init();
OpenSSL_add_all_ciphers();
/*
* Create an SSL context that will be used to communicate over SSLv2, SSLv3,
* or TLSv1 protocol, depending on the choice made by the remote partner
* program.
*/
if ((ctx=SSL_CTX_new(SSLv23_server_method()))==NULL)
berr_exit("Can't initialize SSLv23 context");
if (!SSL_CTX_use_certificate_chain_file(ctx,KEYFILE))
berr_exit("Can't read certificate");
SSL_CTX_set_default_passwd_cb(ctx,password_cb);
if (!SSL_CTX_use_PrivateKey_file(ctx, KEYFILE, SSL_FILETYPE_PEM))
berr_exit("Can't read key file");
if (!SSL_CTX_load_verify_locations(ctx,CA_LIST,0))
berr_exit("Can't read CA list");
#if defined (__unix__) || defined (__unix)
sigemptyset(&set);
sigaddset(&set,SIGPIPE);
#if defined (USE_PTHREAD_KILL)
sigaddset(&set,SIGUSR1);
#if !defined (BAD_PRATICE)
sigaddset(&set,SIGUSR2);
sigaddset(&set,SIGALRM);
#endif
#endif
pthread_sigmask(SIG_SETMASK,&set,NULL);
pthread_sigmask(SIG_BLOCK,&set,NULL);
#endif
#if defined (BAD_PRACTICE)
/*
* On Unix, a signal handler is shared by all threads.
* This handler is established for both the serve_connection
* thread and the watchdog thread. We do not use the signal
* statement as on Linux it has an undefined behavior in
* multi-threaded applications.
*/
action.sa_handler=SIGUSR1_handler;
action.sa_flags=SA_RESTART;
sigemptyset(&action.sa_mask);
sigaction(SIGUSR1,&action,NULL);
#endif
pthread_attr_init(&attr);
#if 0
/* the call below is unnecessary in this case as the default stack size
* seems to be sufficient on all tested operating systems. It is there
* to highlight how one can set a thread stacksize before it gets created.
* The stacksize parameter for the CreateThread on Windows must be
* specified.
*/
pthread_attr_setstacksize(&attr,10*PTHREAD_STACK_MIN);
#endif
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
for ({
namelength = sizeof (s_name);
arg = (arg_t *)malloc(sizeof (arg_t));
memset(arg,0,sizeof(arg_t));
FD_ZERO(&rd_msk);
for (i=0;i<argc;i++)
FD_SET(s[i],&rd_msk);
select(getdtablesize(),&rd_msk,NULL,NULL,NULL);
for (i=0;i<argc;i++){
if (FD_ISSET(s[i],&rd_msk))
arg->s = accept (s[i],(struct sockaddr *)&s_name,&namelength) ;
}
if (arg->s == -1){
pthread_mutex_lock(&ScreenMutex);
perror ("accept error") ;
pthread_mutex_unlock(&ScreenMutex);
free(arg);
exit(EXIT_FAILURE);
}
pthread_mutex_lock(&ScreenMutex);
fprintf (stderr,"Client address : %s\n",inet_ntoa(s_name.sin_addr));
fprintf (stderr,"Client port : %d\n",ntohs(s_name.sin_port) ) ;
pthread_mutex_unlock(&ScreenMutex);
/*
* Create a new SSL context based upon what we chose on the CTX context
* which will handle the SSL link with the remote peer.
*/
arg->ssl=SSL_new(ctx);
/*
* Start the thread that will serve the connection
*/
pthread_create(&arg->connection_thread,
&attr,
serve_connection,
arg);
}
return 1;
} /* end main */

********

* sslserver_windows.c

*******

#ifdef WIN32
#include <io.h>
#include <winsock2.h>
#include <process.h>
#else
#include <netdb.h>
#endif
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <errno.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#ifdef __VMS
#include <unixio.h>
#elif !defined (WIN32)
#include <unistd.h>
#endif
#define AUXSERVER_PORT 60000
#define MAX_MESSAGE_SIZE 50000
#define KEYFILE "server.pem"
#define PASSWORD "password"
#define CA_LIST "root.pem"
#define MAX_OUTSTANDING_CONNECTIONS 5
#define HTML_PREFIX "HTTP/1.1 200 OK\n\
Content-Type:text/html\n\n\
<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n\
<HTML>\n\
<HEAD>\n\
<TITLE>sslserver echo</TITLE>\n\
<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=iso-8859-1\">\n\
</HEAD>\n\
<BODY>\n\
<PRE>\n"
#define HTML_SUFFIX "</PRE>\n\
</BODY>\n\
</HTML>\n"

#if defined (__VMS)
#define socklen_t unsigned int
#endif
#if defined (WIN32)
#define socklen_t int
#define close closesocket
#endif

static char *pass;

static int password_cb(char *buf,int num,int rwflag, void *userdata) {
if (num<(int)strlen(pass)+1) return 0;
strcpy(buf,pass);
return(strlen(pass));
}
typedef struct {
SOCKET s;
SSL *ssl;
BIO *sbio;
DWORD ThreadId;
HANDLE cond;
} arg_t;

// Small error handling routine, that prints out the error, plus OpenSSL
// relevant information upon why the call failed.
void berr_exit(char *str){
fprintf(stderr,"%s\n",str);
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}

void cleanup(arg_t *arg)
{
int retval;

/*
* write html suffix and close the connection
*/
retval=SSL_write(arg->ssl,HTML_SUFFIX,strlen(HTML_SUFFIX));
BIO_free_all(arg->sbio);
retval = close (arg->s);
if (retval)
perror ("close");
} /* end cleanup*/

void finish(void *ptr){
arg_t *arg =(arg_t *)ptr;
cleanup (arg) ;
CloseHandle(arg->cond);
free(arg);
}

char *WSAStrError (int WSAError){
/*
* All Windows Sockets error constants are biased by WSABASEERR from
* the "normal"
*/
struct WSAerrno {
char *text;
int value;
};

static struct WSAerrno ErrorTable [] ={
"WSABASEERR", WSABASEERR,
/*
* Windows Sockets definitions of regular Microsoft C error constants
*/
"WSAEINTR", (WSABASEERR+4),
"WSAEBADF", (WSABASEERR+9),
"WSAEACCES", (WSABASEERR+13),
"WSAEFAULT", (WSABASEERR+14),
"WSAEINVAL", (WSABASEERR+22),
"WSAEMFILE", (WSABASEERR+24),

/*
* Windows Sockets definitions of regular Berkeley error constants
*/
"WSAEWOULDBLOCK", (WSABASEERR+35),
"WSAEINPROGRESS", (WSABASEERR+36),
"WSAEALREADY", (WSABASEERR+37),
"WSAENOTSOCK", (WSABASEERR+38),
"WSAEDESTADDRREQ", (WSABASEERR+39),
"WSAEMSGSIZE", (WSABASEERR+40),
"WSAEPROTOTYPE", (WSABASEERR+41),
"WSAENOPROTOOPT", (WSABASEERR+42),
"WSAEPROTONOSUPPORT", (WSABASEERR+43),
"WSAESOCKTNOSUPPORT", (WSABASEERR+44),
"WSAEOPNOTSUPP", (WSABASEERR+45),
"WSAEPFNOSUPPORT", (WSABASEERR+46),
"WSAEAFNOSUPPORT", (WSABASEERR+47),
"WSAEADDRINUSE", (WSABASEERR+48),
"WSAEADDRNOTAVAIL", (WSABASEERR+49),
"WSAENETDOWN", (WSABASEERR+50),
"WSAENETUNREACH", (WSABASEERR+51),
"WSAENETRESET", (WSABASEERR+52),
"WSAECONNABORTED", (WSABASEERR+53),
"WSAECONNRESET", (WSABASEERR+54),
"WSAENOBUFS", (WSABASEERR+55),
"WSAEISCONN", (WSABASEERR+56),
"WSAENOTCONN", (WSABASEERR+57),
"WSAESHUTDOWN", (WSABASEERR+58),
"WSAETOOMANYREFS", (WSABASEERR+59),
"WSAETIMEDOUT", (WSABASEERR+60),
"WSAECONNREFUSED", (WSABASEERR+61),
"WSAELOOP", (WSABASEERR+62),
"WSAENAMETOOLONG", (WSABASEERR+63),
"WSAEHOSTDOWN", (WSABASEERR+64),
"WSAEHOSTUNREACH", (WSABASEERR+65),
"WSAENOTEMPTY", (WSABASEERR+66),
"WSAEPROCLIM", (WSABASEERR+67),
"WSAEUSERS", (WSABASEERR+68),
"WSAEDQUOT", (WSABASEERR+69),
"WSAESTALE", (WSABASEERR+70),
"WSAEREMOTE", (WSABASEERR+71),

"WSAEDISCON", (WSABASEERR+101),

/*
* Extended Windows Sockets error constant definitions
*/
"WSASYSNOTREADY", (WSABASEERR+91),
"WSAVERNOTSUPPORTED", (WSABASEERR+92),
"WSANOTINITIALISED", (WSABASEERR+93),

/*
* Error return codes from gethostbyname() and gethostbyaddr()
* (when using the resolver). Note that these errors are
* retrieved via WSAGetLastError() and must therefore follow
* the rules for avoiding clashes with error numbers from
* specific implementations or language run-time systems.
* For this reason the codes are based at WSABASEERR+1001.
* Note also that [WSA]NO_ADDRESS is defined only for
* compatibility purposes.
*/

/* Authoritative Answer: Host not found */
"WSAHOST_NOT_FOUND", (WSABASEERR+1001),

/* Non-Authoritative: Host not found, or SERVERFAIL */
"WSATRY_AGAIN", (WSABASEERR+1002)
};
int i;
static char buffer[100];

for (i=0;i<sizeof (ErrorTable)/sizeof (struct WSAerrno);i++)
if (ErrorTable[i].value == WSAError)
return (ErrorTable[i].text);
if (i== sizeof (ErrorTable)/sizeof (struct WSAerrno))
sprintf (buffer,"WSAUNKNOWNERR %d",WSAError);
return (buffer);
}

DWORD WINAPI connection_watchdog(arg_t *arg){

#define SLEEP_TIME 1000 // 1 second
DWORD ret;
for ({
ret = WaitForSingleObject(arg->cond,SLEEP_TIME);
if (ret == WAIT_TIMEOUT){
/*
* Time elapsed without any SSL activity. Post a WM_USER+1 message to the
* target serve_connection thread's message queue and exit this thread.
*/
PostThreadMessage(arg->ThreadId,WM_USER+1,0,0);
_endthreadex(0);
}
if (ret != WAIT_OBJECT_0)
_endthreadex(0);
}
}

DWORD WINAPI serve_connection (arg_t *arg){
int len;
int sent_html=0;
char *message=NULL;
HANDLE hThread;
DWORD hThreadId;
MSG msg;
fd_set rmask;
struct timeval timeout={0,10000};
char *current_cipher_name;

/*
* Initialize a message queue for this thread.
*/
PeekMessage(&msg,NULL,WM_USER,WM_USER,PM_NOREMOVE);
/*
* Complete the SSL connection from the remote peer.
*/
arg->sbio=BIO_new_socket((int)arg->s,BIO_NOCLOSE);
if (arg->sbio == NULL){
printf("sbio NULL\n");
exit(EXIT_FAILURE);
}
SSL_set_bio(arg->ssl,arg->sbio,arg->sbio);
/*
* Choose the encryption algorythm by selecting a cipher.
* As bio_html is a SSL V2 client, one must choose a sslv2 cipher.
* To get available ciphers to choose for this application, enter the
* command:
* openssl ciphers -v -sslv2
* The EXP1024-DHE-DSS-RC4-SHA cipher is a sslv3 cipher and cannot be used
* here with a sslv2 client.
*/
// if (SSL_set_cipher_list(arg->ssl,"EXP1024-DHE-DSS-RC4-SHA") <= 0)
// berr_exit("cipher could not be set");
/*
* This server will send its X509 certificate to the client when the
* connection is accepted, but using SSL_VERIFY_NONE, this program does
* not require a certificate from the remote SSL peer.
*/
SSL_set_verify(arg->ssl,SSL_VERIFY_NONE,NULL);
/*
* Do accept the SSL connection.
*/
if ((len=SSL_accept(arg->ssl))<=0){
fprintf(stderr,"SSL accept error\n");
ERR_print_errors_fp(stderr);
goto out;
}
current_cipher_name=(char *)SSL_get_cipher_name(arg->ssl);
printf("Using cipher %s\n",current_cipher_name);
/*
* Create a unnamed counting semaphore with initial value
* of 0 (blocking).
*/
arg->cond = CreateSemaphore (NULL, // Seamphore attributes
0, // initial count
1, // maximum count
NULL); // Semaphore name

/*
* Start the connection activity watchdog.
*/
hThread = (HANDLE)_beginthreadex(NULL, // Thread Attributes.
1000, // Initial Stack size.
connection_watchdog, // Thread entry point
arg, // Thread paramet
0, // Thread is to start imediadetly
&hThreadId);

if(hThread == 0){
fprintf (stdout, "Error creating socket's link Thread handler\n");
goto out;
}
/*
* loop waiting for activity on the socket.
*/
for ({
FD_ZERO(&rmask);
FD_SET(arg->s,&rmask);
/*
* Use a timeout on the select in order to be able to PeekMessage
*/
switch (select(arg->s+1,&rmask,NULL,NULL,&timeout)){
case 0:
/*
* If we have a WM_USER+1 message on our thread message queue,
* exit this thread.
*/
if (PeekMessage(&msg,(HWND)(-1),WM_USER+1,WM_USER+1,PM_REMOVE))
goto out;
else continue;

case -1:
fprintf (stdout,"Select Error (%s)",WSAStrError(WSAGetLastError()));
goto out;
break ;
default:
/*
* Signal the connection watchdog we have socket activity
*/
ReleaseSemaphore (arg->cond,1,NULL);
/*
* Read the available data on the SSL link.
*/
message=(char *)calloc(MAX_MESSAGE_SIZE,sizeof(char));
len=SSL_read(arg->ssl,message,MAX_MESSAGE_SIZE);
if (len <=0){
fprintf(stdout,"Client disconnected\n");
goto out;
}
fprintf(stdout,"Received %.*s\n",len,message);
/*
* Echo via the SSL link the data just received.
*/
{
int i;
char *msg;
size_t size=0;
for (i=0;i<2;i++){
switch(i){
case 0: if (!sent_html){
size=strlen(HTML_PREFIX);
}
break;
case 1: size+=len;break;
break;
}
}/* end for */
msg=(char *)calloc(size+1,sizeof(char));
if (!sent_html){
strcpy(msg,HTML_PREFIX);
sent_html++;
}
strcat(msg,message);
size=SSL_write(arg->ssl,msg,size);
free(msg);
if (size==0)
/*
* The remote partner exited.
* Exit the thread meanwhile invoking the
* finish routine.
*/
goto out;
if (size<0)
berr_exit("Error writing to SSL socket");
}
free(message);
}/* end switch */
}/* end for */
out:;
finish(arg);
_endthreadex(0);
return 0;
}
int main (int argc, char **argv) {
SOCKET s,lsock;
int address_len;
arg_t *arg;
char *level = "PF_INET";
struct sockaddr_in this,from;
WORD wVersionRequested;
WSADATA wsaData;
HANDLE hThread;
SSL_CTX *ctx;
// int on;

wVersionRequested = MAKEWORD(2,0);
if(WSAStartup(wVersionRequested,&wsaData)!=0){
fprintf (stderr,"WSAStartup failed\n");
return (EXIT_FAILURE);
}

if ((LOBYTE(wsaData.wVersion) != 2) ||
(HIBYTE(wsaData.wVersion) != 0)) {
fprintf (stderr,"WSAStartup bad version %d,%d\n",
LOBYTE(wsaData.wVersion),
HIBYTE(wsaData.wVersion));
WSACleanup();
return (EXIT_FAILURE);
}
fprintf(stdout," beginning of the program\n ");
SSL_load_error_strings();
// Initialize the SSL library.
SSL_library_init();
OpenSSL_add_all_ciphers();

/*
* Create a TCPIP (stream) socket.
*/

if ((lsock = socket ( PF_INET,SOCK_STREAM,0)) == INVALID_SOCKET) {
fprintf (stderr,"socket lsock error = %d\n",WSAGetLastError());
WSACleanup();
return (EXIT_FAILURE);
}
/*
* Now bind lsock to a TCPIP port number
*/
memset (&this, 0, sizeof (this));
this.sin_family = PF_INET ;
this.sin_port = htons(AUXSERVER_PORT);
this.sin_addr.s_addr = htonl(INADDR_ANY);

if ( bind (lsock,(struct sockaddr *)&this,sizeof (this))== SOCKET_ERROR) {
fprintf (stderr,"bind lsock error = %s\n",WSAStrError(WSAGetLastError()));
WSACleanup();
return (EXIT_FAILURE);
}

/* Wait for connections requests (listen) and redirect the connection to a
* dynamic socket s (accept).
*/
if (listen (lsock, MAX_OUTSTANDING_CONNECTIONS)== SOCKET_ERROR) {
fprintf (stderr,"listen error = %s\n",WSAStrError(WSAGetLastError()));
WSACleanup();
return (EXIT_FAILURE);
}
/*
* Create an SSL context that will be used to communicate over SSLv2, or SSLv3
* or TLSv1 protocol depending on the choice of the remote partner program.
*/
if ((ctx=SSL_CTX_new(SSLv23_server_method()))==NULL)
berr_exit("Can't initialize SSLv23 context");
if (!SSL_CTX_use_certificate_chain_file(ctx,KEYFILE))
berr_exit("Can't read certificate");
pass=PASSWORD;
SSL_CTX_set_default_passwd_cb(ctx,password_cb);
if (!SSL_CTX_use_PrivateKey_file(ctx, KEYFILE, SSL_FILETYPE_PEM))
berr_exit("Can't read key file");
if (!SSL_CTX_load_verify_locations(ctx,CA_LIST,0))
berr_exit("Can't read CA list");

for ({
address_len = sizeof(from);
if((s = accept (lsock, (struct sockaddr *)&from,&address_len)) == INVALID_SOCKET){
fprintf (stderr,"accept error %s",WSAStrError(WSAGetLastError()));
WSACleanup();
return (EXIT_FAILURE);
}

fprintf (stdout,"Client address : %s\n",inet_ntoa(from.sin_addr)) ;
fprintf (stderr,"Client port : %d\n",ntohs(from.sin_port) ) ;
/* Now we can create a thread that will serve the connection on socket s
* this will be simulated by a call to a subroutine.
*/
// Create the dynamic thread
arg = malloc(sizeof(arg_t));
arg->s = s;
arg->ssl=SSL_new(ctx);
hThread = (HANDLE)_beginthreadex(NULL, // Thread Attributes.
1000000, // Initial Stack size.
serve_connection, // Thread entry point
arg, // Thread parameter.
0, // Thread is to start imediadetly
&arg->ThreadId);

if(hThread == 0){
fprintf (stderr, "Error creating socket's link Thread handler\n");
WSACleanup();
return (EXIT_FAILURE);
}
} /* end for( */
return (EXIT_SUCCESS);
}

*******

* bio_html.c

*******

#ifdef WIN32
#include <winsock2.h>
#else
#include <netdb.h>
#endif
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <errno.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#ifdef __VMS
#include <unixio.h>
#else
#include <unistd.h>
#endif
#if defined (_XOPEN_SOURCE_EXTENDED) && defined (__hpux)
#include <sys/byteorder.h>
#endif

// Define possible WEB ports.
#define HTTP_PROXY_PORT 8080
#define HTTP_PORT 80
#define HTTPS_PORT 443

#ifndef WIN32
#define SOCKET int
#define SOCKET_ERROR -1
#endif

// Setup constants.
char const *http = "http://";
char const *https = "https://";
char const *CRLF = "\015\012";
extern char *encode_64(char *str, const char *eol);
//
// This routine parses the command line to determine the remote host and port
// where to connect to, according to most Web browsers rules. It recognizes if
// the http command contains an authentification string (Username/password).
char *parse(char *command,char **filters,
short number_filters,
char *which,unsigned short *port, char **userpasswd){
char const slash = '/';
char const star = '*';
char *cp,*cp1,*cp2;
static char *host;
int j;

cp = strstr(command,http);
cp2 = strstr(command,https);
if ((cp != command) && (cp2 != command)){
fprintf(stderr,"Bad http:// command : %s\n",command);;
exit(0);
}
if (cp) cp += strlen(http);
if (cp2) cp = cp2 + strlen(https);
if ((cp1 = strchr(cp,'@')) == NULL){
*userpasswd = NULL;
// host located, return string after the first single slash.
host = cp;
}
else{
*cp1++ = '\0';
host = cp1;
*userpasswd = cp;
cp = cp1;
}
cp = strchr(host,slash);
if (cp == NULL)
strcpy(which,"/");
else {
strcpy(which,cp);
*cp = '\0';
}
// Now determine the port we will use.
// The rule is that if found in the set of filters,
// we use port 80, otherwise port=8080.
// The star character in an address filter must be in
// first position.
if (number_filters >= 0)
*port = HTTP_PROXY_PORT; // Assume not found in the filters.
else{
if (strstr(command,https))
*port=HTTPS_PORT;
else
*port = HTTP_PORT;
}
for (j=0; j< number_filters;j++){
if (strstr(host,filters[j] + sizeof(star)) != NULL){
if (strstr(command,https))
*port=HTTPS_PORT;
else
*port=HTTP_PORT;
break;
}
}
return host;
}
void usage(char *str){
fprintf(stderr,"Syntax : %s <http command> [<proxy server name>]",str);
fprintf(stderr,"[<filter1> .. <filterN>]\n");
fprintf(stderr,"Examples : ");
fprintf(stderr,"%s http://www.compaq.com/\n",str);
fprintf(stderr,"With authentification string : %s http://vouters:vouters@sos6.evt.cpqcorp.net/\n",str);
fprintf(stderr,"With alternate HTTP port (default 80) : %s http://sos6.evt.cpqcorp.net:82/\n",str);
fprintf(stderr,"With alternate HTTP_PROXY port (default 8080) : %s http://www.compaq.fr/ proxy:8086\n",str);
exit(0);
}
// Small error handling routine, that prints out the error, plus OpenSSL
// relevant information upon why the call failed.
void berr_exit(char *str){
fprintf(stderr,str);
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
#ifdef WIN32
int strcasecmp(char *s1,char *s2){
while ((*s1 != '\0') && (*s2 != '\0')){
if (toupper(*s1) > toupper(*s2)) return 1;
if (toupper(*s1++) < toupper(*s2++)) return -1;
}
return 0;
}
#endif

/*
* Check that the common name matches the
* host name
*/

void check_cert(SSL *ssl, char *host){
X509 *peer;
char peer_CN[256];

// This statement is compulsary to get the certificate verified.
SSL_set_verify_result(ssl,X509_V_OK);
// Verify certificate
//if ((peer=SSL_get_peer_certificate(ssl)) == NULL)
// berr_exit("Could not get peer certificate\n");
if (SSL_get_verify_result(ssl) != X509_V_OK)
berr_exit("Certificate doesn't verify\n");
/*Check the cert chain. The chain length
is automatically checked by OpenSSL when
we set the verify depth in the ctx */

/*Check the common name*/
peer=SSL_get_peer_certificate(ssl);
X509_NAME_get_text_by_NID
(X509_get_subject_name(peer),
NID_commonName, peer_CN, 256);

if(strcasecmp(peer_CN,host)){
fprintf(stderr,"Peer name %s doesn't match host name %s\n",peer_CN,host);
exit(EXIT_FAILURE);
}
}

SOCKET tcp_connect(char *connect_str){
struct hostent *hp;
struct sockaddr_in addr;
SOCKET sock;
char *Webhost=connect_str;
unsigned short port;
char *cp;
char error[80];

if((cp=strrchr(connect_str,':')) == NULL){
fprintf(stderr,"Bad syntax %s\n", connect_str);
exit(EXIT_FAILURE);
}
*cp='\0';
cp++;
port=(unsigned short)atoi(cp);
if(!(hp=gethostbyname(Webhost)))
berr_exit("Couldn't resolve host");
memset(&addr,0,sizeof(addr));
addr.sin_addr=*(struct in_addr*)hp->h_addr_list[0];
addr.sin_family=AF_INET;
addr.sin_port=htons(port);
if((sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))<0){
sprintf(error,"Couldn't create socket %s\n",strerror(errno));
berr_exit(error);
}
if (connect(sock,(struct sockaddr *)&addr,sizeof(addr))<0){
sprintf(error,"Couldn't connect socket %s\n",strerror(errno));
#ifndef WIN32
close(sock);
#else
closesocket(sock);
#endif
berr_exit(error);
}
return sock;
}

// This routine handle a https request. It uses a Secure Socket handle to
// communicate with the remote HTTP server.
void https_connection_handle(char *connect_str, char *tmpbuf){
SSL_CTX *ctx;
SSL *ssl;
BIO *cbio,*out;
int len;
char str[80];
SOCKET s;
char *cp=tmpbuf;
int r;

SSL_load_error_strings();
// Initialize the SSL library.
SSL_library_init();

OpenSSL_add_all_ciphers();
// Create an SSL context that will be used to communicate over TLSv1 or SSLv2 or SSLv3
// protocols.
// See RFC 2818.
if ((ctx=SSL_CTX_new(SSLv23_client_method()))==NULL)
berr_exit("Can't initialize SSLv23 context");
SSL_CTX_set_verify(ctx,SSL_VERIFY_NONE,NULL);
// Connect to remote peer either proxy or https server
// using classic Socket API. Get the actual host name through host variable.
s=tcp_connect(connect_str);
//Connect SSL to CTX
ssl=SSL_new(ctx);
// Associate BIO cbio with socket s
cbio=BIO_new_socket((int)s,BIO_NOCLOSE);
SSL_set_bio(ssl,cbio,cbio);
// Set mode no retry.
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);

out = BIO_new_fp(stdout, BIO_NOCLOSE);
// Establish a link with the remote SSL session using chosen transport.
if(SSL_connect(ssl) <= 0){
sprintf(str,"Error connecting to server's session\n");
berr_exit(str);
}
check_cert(ssl,connect_str);
// Now emit over the network the https:// string.
len=(int)strlen(cp);
while (len){
r = SSL_write(ssl,cp,len);
if (r>0){
len -=r;
cp += r;
}
else{
if (r==0){
fprintf (stderr,"Partner exited...\n");
goto end;
}
else
berr_exit("SSL_write error");
}
}
for( {
len = SSL_read(ssl, tmpbuf, 1024);
if(len <= 0) break;
BIO_write(out, tmpbuf, len);
//fwrite(tmpbuf,1,len,stdout);
}
BIO_write(out,"\n",sizeof("\n"));
//fwrite("\n",1,sizeof("\n"),stdout);
end:;
SSL_shutdown(ssl);
BIO_free_all(cbio);
BIO_free(out);


}
// This routine handles a http request. Also it uses OpenSSL BIO functions,
// the connection is unsecure. It can be expressed with a classic TCP/IP
// communication.
void http_connection_handle(char *connect_str,char *tmpbuf){
BIO *cbio, *out;
int len;
char str[80];

ERR_load_crypto_strings();
cbio = BIO_new_connect(connect_str);
out = BIO_new_fp(stdout,BIO_NOCLOSE);
if(BIO_do_connect(cbio) <= 0) {
sprintf(str, "Error connecting to server %s\n",connect_str);
berr_exit(str);
}
BIO_puts(cbio, tmpbuf);
for( {
len = BIO_read(cbio, tmpbuf, 1024);
if(len <= 0) break;
BIO_write(out, tmpbuf, len);
//fwrite(tmpbuf,1,len,stdout);
}
BIO_write(out,"\n",sizeof("\n"));
//fwrite("\n",1,sizeof("\n"),stdout);
BIO_free(cbio);
BIO_free(out);
}
int main(int argc, char **argv){

char *proxy_server;
char *host;
char tmpbuf[1024];
unsigned short port, http_proxy_port = HTTP_PROXY_PORT;
char Webpage[80];
char *userpwd;
char *cp;
char connect_str[80];
#ifdef WIN32
WORD wVersionRequested = MAKEWORD (2,0);
WSADATA wsaData;
int err;
#endif

#ifdef WIN32
CRYPTO_malloc_init();
#endif
if (argc < 2)
usage(argv[0]);
proxy_server=argv[2];
host = parse(argv[1],&argv[3],argc-3,Webpage,&port,&userpwd);
if ((proxy_server != NULL) && (strcmp(proxy_server,""))){
// Parse a proxy server with a colon followed by
// the http default proxy port.
cp = strchr(proxy_server,':');
if (cp != NULL){
*cp = 0;
cp++;
if (port != HTTP_PORT)
port = http_proxy_port = (unsigned short)atoi(cp);
}
}
// did the user specified a HTTP_PORT ?
if ((cp = strchr(host,':')) != NULL){
*cp++ = '\0';
port = (unsigned short)atoi(cp);
if ((port == http_proxy_port) && (!proxy_server))
proxy_server = host;
}
// Setup the HTTP command.
strcpy(tmpbuf,"GET ");
if (port == http_proxy_port){
if (strstr(argv[1],https))
strcat(tmpbuf,https);
else
strcat(tmpbuf,http);
strcat(tmpbuf,host);
}
strcat(tmpbuf,Webpage);
strcat(tmpbuf," HTTP/1.1");
strcat(tmpbuf,CRLF);
strcat(tmpbuf,"Host: ");
strcat(tmpbuf,host);
strcat(tmpbuf,CRLF);
if (userpwd){
strcat (tmpbuf,"Authorization: Basic ");
strcat (tmpbuf,encode_64(userpwd,"\0"));
strcat(tmpbuf,CRLF);
}
strcat(tmpbuf,CRLF);
sprintf(connect_str,"%s:%1u",
port==http_proxy_port?proxy_server:host,
port);
#ifdef WIN32
err = WSAStartup(wVersionRequested,&wsaData);
if (err != 0){
printf("%%TELNET-F-CANTLOAD, cannot load winsock.dll");
exit (1);
}
if ((LOBYTE(wsaData.wVersion) != 2) ||
(HIBYTE (wsaData.wVersion) != 0)){
printf ("%%TELNET-F-BADVER, incorrect winsock.dll version\r\n");
WSACleanup();
exit (1);
}
#endif

if (strstr(argv[1],https))
https_connection_handle(connect_str,tmpbuf);
else
http_connection_handle(connect_str,tmpbuf);
#ifdef WIN32
WSACleanup();
#endif

}

******** encode.c *********

#include <stdlib.h>
#include <string.h>

#define MAX_LINE 76 /* size of encoded lines */

static char basis_64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

char *encode_64(char *str,char *eol){
unsigned int rlen;
int len = strlen(str);
int chunk;
unsigned char c1,c2,c3;
unsigned int eollen;
char *r,*out;

if (eol)
eollen = strlen(eol);
else{
eol = "\n";
eollen = 1;
}

/* calculate the length of the result */
rlen = (len+2) / 3 * 4; /* encoded bytes */
if (rlen) {
/* add space for EOL */
rlen += ((rlen-1) / MAX_LINE + 1) * eollen;
}

/* allocate a result buffer */
out = r = malloc(rlen ? rlen : 1);

for (chunk=0; len > 0; len -= 3, chunk++) {
if (chunk == (MAX_LINE/4)) {
char *c = eol;
char *e = eol + eollen;
while (c < e)
*r++ = *c++;
chunk = 0;
}
c1 = *str++;
c2 = *str++;
*r++ = basis_64[c1>>2];
*r++ = basis_64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)];
if (len > 2) {
c3 = *str++;
*r++ = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)];
*r++ = basis_64[c3 & 0x3F];
} else if (len == 2) {
*r++ = basis_64[(c2 & 0xF) << 2];
*r++ = '=';
} else { /* len == 1 */
*r++ = '=';
*r++ = '=';
}
}
if (rlen) {
/* append eol to the result string */
char *c = eol;
char *e = eol + eollen;
while (c < e)
*r++ = *c++;
}
*r = '\0'; /* every SV in perl should be NUL-terminated */
return(out);
}

REFERENCE:

OpenSSL on-line help and searches on Google

The Multithreaded Programming guide THREADTIME

from Scott J.NORTON & Mark D/DIPASQUALE

Order number : ISBN 0-13-190067-b

Publisher : Hewlett-Packard Professionnel Books

Sursa: Example-C OpenSSL client and server example

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...