Nytro Posted January 29, 2013 Report Posted January 29, 2013 All-OS-OpenSSL-client-threaded-server-exampleExample-C OpenSSL client and server example EXAMPLE TEMPLATEPRODUCT: OpenSSL Version 0.9.8a 11 Oct 2005OP/SYS: Windows XP SP2 OpenVMS Alpha V8.2 Linux Fedora Core 5 HP-UX 11iCOMPONENT: OpenSSL run-time libraries and tools SOURCE: Philippe Vouters Fontainebleau/FranceLOW COST HIGH-TECH PRODUCTS: http://techno-star.frDOWNLOAD LINKS:OpenSSL for OpenVMS (Itanium, Alpha, VAX) can be dowloaded from HP OpenVMS SystemsOpenSSL for HP-UX can be downloaded from http://h20293.www2.hp.com/portal/swdepot/displayProductInfo.do?productNumber=OPENSSL11IOpenSSL 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.gzOVERVIEW:This document shows a set of OpenSSL multi-threaded servers and asingle-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 isalso able to accept connections on user specified network IP interfaces. Seeits code in the main routine body part.*** CAUTION ***These sample programs have been tested using Linux Fedora Core 5, OpenVMSAlpha V8.2 and Windows XP and HP-UX B.11.11. However, we cannot guaranteeits 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 variableEnter the following command: C:\> nmakeOn 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: $ @makefileUse the following syntax to generate two pem files prior to executing the programseach 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.cnfEdit root.pem so that the first and last lines appear as follows if the above commandis not generating the certificate header correctly: -----BEGIN CERTIFICATE----- -----END CERTIFICATE-----This should be done when using the following:$ openssl versionOpenSSL 0.9.6g [engine] 9 Aug 2002On Windows:C:\> openssl req -newkey rsa:1024 -x509 -keyout key.pem -out root.pem -config \openssl\apps\openssl.cnfThe -config option is at least necessary under the following:C:\Documents and Settings\vouters>openssl versionOpenSSL 0.9.7c 30 Sep 2003Use 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.pemOn Unix: $ cat key.pem root.pem > server.pemOn VMS: $ copy/concatenate key.pem,root.pem server.pemOn Windows: C:\> copy key.perm/A + root.pem/A server.pem/AType the following on one terminal:Parameters between square brackets are optional. If no parameters are suppliedthe program binds to 0.0.0.0:60000. IP Interface optional parameter is theIPv4 IP address of the computer's network interface you wish to limit thetraffic 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_windowsDo 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 realWeb browser and introduce the following URL: https://host:60000/To check the certificate aspect, you may use the openssl provided client withthe command: openssl s_client -connect localhost:60000 -state -showcertsALGORYTHM: 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 socketADVISE:If you are running VMS or any Unix, you will need the sslserver.c. This code isover documented, hiding the actual code. With the commentss he got, the authorof this article feels necessary to strongly document the solution. Also as thesame C source file runs on many platforms having each different possibilitiesand constraints, there are lots of conditional preprocessing (all the #ifdef's).The best advise the author may give the reader is to preprocess this sourcefile. You will then get a C source code containing only C statements that fitsyour own preprocess conditions. To solely use the preprocessor, you would:On Linux: $ cpp [-DUSE_PTHREAD_KILL [-DBAD_PRATICE]] -P sslserver.c sslserver.ccEach item surrounded by square brackets is optional.On OpenVMS systems: $ cc/preprocess=sslserver.cc/noline sslserver.cThe output preprocessor file is sslserver.cc. You can edit view it from thebottom and climb lines up to the real start of this sslserver.c code. You willsee how simple both OpenSSL and C phtreads programming can summarize using asupported or unsupported way that proves to work running it. To fully appreciatethis summary with the preprocessor output, you must have a strong TCP/IPprogramming culture and not forget OpenSSL is based upon TCP/IP. You shouldas well possess a strong real-time programming culture on various Real-Timeoperating systems. The best profile is a programmer with 10 years programmingexperience whose job nature is either developping code or assisting developers.If you are used to concurrent programming on Windows, the algorythm of theWindows sslserver.c code below is an almost direct translation using Windowsprogramming of the OpenSSL/pthreaded OpenVMS version because therun-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:/OPTSYS$LIBRARY:SSL$LIBCRYPTO_SHR32.EXE/SHARESYS$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:/OPTSYS$LIBRARY:SSL$LIBCRYPTO_SHR32.EXE/SHARESYS$LIBRARY:SSL$LIBSSL_SHR32.EXE/SHARE$exit********* Windows XP Makefile ********PROGRAM1=bio_htmlPROGRAM2=sslserver_windowsDEFINES=/D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS"CCFLAGS=/c /MD /W3 /GX /O1 /TcLIBS=libeay32.lib ssleay32.lib kernel32.lib user32.lib advapi32.lib ws2_32.libLDFLAGS=/LIBPATH:"\openssl\bin" /incremental:yes /subsystem:consoleOBJS=encodeall: $(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).cclean: - 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_htmlPROGRAM2=sslserverDEFINES_HP-UX=-D_XOPEN_SOURCE_EXTENDEDDEFINES_OSF1=-D_OSF_SOURCE=500 -D_XOPEN_SOURCE=500DEFINES_Linux=CCFLAGS_HP-UX=-mtCCFLAGS_OSF1=-std -pthreadCCFLAGS_Linux=-pthreadLIBS_HP-UX=-lssl -lcryptoLIBS_Linux=-lssl -lcryptoLIBS_OSF1=-lssl -lcryptoLDFLAGS=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_PRACTICEendifDEFINES += -DUSE_PTHREAD_KILLendifall: $(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).cclean: 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#endifconst 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");#endifprintf("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");}#endifstatic 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#endifstatic 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 WIN32int 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 GoogleThe Multithreaded Programming guide THREADTIMEfrom Scott J.NORTON & Mark D/DIPASQUALEOrder number : ISBN 0-13-190067-bPublisher : Hewlett-Packard Professionnel BooksSursa: Example-C OpenSSL client and server example Quote