- From: olga <olga@goliath.eai.com>
- Date: Thu, 01 Apr 1999 13:46:36 -0600 (CST)
- To: Henrik Frystyk Nielsen <frystyk@w3.org>
- Cc: www-lib@w3.org, olga@goliath.eai.com
Hi, There is my solution for www to support SSL: Changes are to HTInit.h HTInit.c and new files are: HTSSL.h HTSSLMan.h HTSSLReader.h HTSSLReader.c HTSSLWriter.h HTSSLWriter.c HTSSL.c has list of htssl structures (active ssl connections) that can be used by multiple HTSSLReaders and HTSSLwriters. The active ssl connection is found by identifier which is a socket descriptor (there should be a better way to do this). Pointer to htssl structure is returned to interested and reference count is updated. As soon as ref_count is 0 - structure is destroyed and removed from the list. If the socket is closed. Then ssl member "disconnected" is set saying that this htssl structure should not be used by any reader or writer - should be freed and new structure should be created (or found in list). (I think this part is not completely implemented in this version. I am currently working on another version that is very much company specific and will not work just with wwwlib.) This is my "try-version". So, please excuse possible errors. The new files could be modified to add features or change solution completely. Regards, Olga. ==================================================================== Changes to HTInit.c: /* should be included somewhere else (Where HTReader and HTWriter are included...) */ #include "www/HTSSLReader.h" #include "www/HTSSLWriter.h" //////////////////////////////////////////// Initialize protocol PUBLIC void HTProtocolInit (void) { .... /* for ssl add line */ HTProtocol_add("https", "secure_tcp", SSL_PORT, NO, HTLoadHTTP, NULL); .... } PUBLIC void HTProtocolPreemptiveInit (void) { .... HTProtocol_add("https", "secure_tcp", SSL_PORT, YES, HTLoadHTTP, NULL); .... } //////////////////////////////////////////////////////// Add Transport PUBLIC void HTTransportInit (void) { ..... /* ssl */ HTTransport_add("secure_tcp", HT_TP_SINGLE, HTSSLReader_new, HTSSLWriter_new); .... } //////////////////////////////////////////////////// Initialize Before Filters to work with https: PUBLIC void HTBeforeInit (void) { .... /* add these lines */ HTNet_addBefore(HTCacheFilter, "https://*", NULL, HT_FILTER_MIDDLE); HTNet_addBefore(HTCredentialsFilter, "https://*", NULL, HT_FILTER_LATE); HTNet_addBefore(HTPEP_beforeFilter, "https://*", NULL, HT_FILTER_LATE); } /////////////////////////////////////////////////// Initialize After Filters to work with https: PUBLIC void HTAfterInit (void) { .... /* add these lines */ HTNet_addAfter(HTAuthFilter, "https://*", NULL, HT_NO_ACCESS, HT_FILTER_MIDDLE); HTNet_addAfter(HTAuthFilter, "https://*", NULL, HT_REAUTH, HT_FILTER_MIDDLE); HTNet_addAfter(HTPEP_afterFilter, "https://*", NULL, HT_ALL, HT_FILTER_MIDDLE); HTNet_addAfter(HTRedirectFilter, "https://*", NULL, HT_PERM_REDIRECT, HT_FILTER_MIDDLE); HTNet_addAfter(HTRedirectFilter, "https://*", NULL, HT_FOUND, HT_FILTER_MIDDLE); HTNet_addAfter(HTRedirectFilter, "https://*", NULL, HT_SEE_OTHER, HT_FILTER_MIDDLE); HTNet_addAfter(HTRedirectFilter, "https://*", NULL, HT_TEMP_REDIRECT, HT_FILTER_MIDDLE); HTNet_addAfter(HTUseProxyFilter, "https://*", NULL, HT_USE_PROXY, HT_FILTER_MIDDLE); HTNet_addAfter(HTCacheUpdateFilter, "https://*", NULL, HT_NOT_MODIFIED, HT_FILTER_MIDDLE); } ========================================================== ========================================================== Change to HTInit.h: #ifndef SSL_PORT #define SSL_PORT 443 #endif ========================================================== ========================================================== new file: HTSSL.h /* This module declares a wrapper around SSLeay for added SSL support. HTSSL will keep context of the application (SSL_CTX), provide SSLeay functinality. Cipher negotiation, certificate directory path setting could be added here. */ #ifndef _HTSSL_H #define _HTSSL_H #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <memory.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> /*#include "e_os.h" */ #include "bio.h" #include "crypto.h" #include "x509.h" #include "ssl.h" #include "err.h" #include "www/WWWCore.h" #define PERROR(err,s) if (PROT_TRACE && ((err)==-1)) perror(s) typedef struct _HTSSL HTSSL; /* initialize SSL application context */ extern BOOL HTSSL_CTX_init (void); #endif /* _HTSSL_H */ =============================================================== =============================================================== New File: HTSSLMan.h ===================== /* THE HTSSL CLASS DEFINITION - defines, manages and frees single SSL connection. */ #ifndef HTSSLMAN_H #define HTSSLMAN_H #ifdef __cplusplus extern "C" { #endif #include "www/HTSSL.h" struct _HTSSL { SSL * ssl; int sd; /* socket descriptor */ BOOL connected; int ref_count; }; extern HTSSL * HTSSL_new(int sd); extern void HTSSL_free(HTSSL *); extern BOOL HTSSL_connected(HTSSL * ssl); extern void HTSSL_set_disconnected(HTSSL * htssl); extern BOOL HTSSL_connect(HTSSL * ssl, int sd); extern int HTSSL_read(HTSSL * htssl, int sd, char * buff, int len); extern int HTSSL_write(HTSSL * htssl, int sd, char * buff, int len); #ifdef __cplusplus } #endif #endif /*HTSSLMAN_H*/ =============================================================== =============================================================== New file: HTSSL.c /* HTSSL.c Implementation of HTSSL.h and HTSSLMan.h declarations. */ #include "www/HTSSL.h" #include "www/HTSSLMan.h" /********************************************************************/ /* GLOBALS */ PRIVATE SSL_CTX * app_ctx = NULL; /*PRIVATE SSL * active_ssl = NULL;*/ /* list of all HTSSL structures currently used by readers and writers */ PRIVATE HTList *ssl_list = NULL; /*********************************************************************/ /* APPLICATION CONTEXT */ PUBLIC BOOL HTSSL_CTX_init() { if (PROT_TRACE) HTTrace(" HTSSL_CTX_init()~~~~~~~~~~~~\n"); /* Create application's SSL context if it doesn't exist yet */ if (!app_ctx) { SSL_METHOD *meth = NULL; SSLeay_add_ssl_algorithms(); SSL_load_error_strings(); meth = SSLv2_method(); app_ctx = SSL_CTX_new(meth); SSL_CTX_set_session_cache_mode(app_ctx, SSL_SESS_CACHE_CLIENT); if (app_ctx != NULL) { printf("context created!\n\n"); return YES; } else return NO; } return YES; } /*****************************************************************************/ PRIVATE BOOL HTSSL_verifyServerCertificate(SSL * ssl) { int i = 0; char buff[100]; X509_STORE * store = NULL; X509_STORE_CTX store_ctx; X509 * server_cert = NULL; store = X509_STORE_new(); X509_STORE_set_default_paths(store); server_cert = SSL_get_peer_certificate(ssl); if (!server_cert) return NO; /* print out names of certificate's issuer and subject */ if (PROT_TRACE) { memset(buff, 0, 100); HTTrace("HTSSL: === Certificate with subject = %s\n", X509_NAME_oneline(X509_get_subject_name(server_cert), buff, 99)); memset(buff, 0, 100); HTTrace("HTSSL: === Certificate with issuer = %s\n", X509_NAME_oneline(X509_get_issuer_name(server_cert), buff, 99)); } X509_STORE_CTX_init(&store_ctx, store, server_cert, NULL); i = X509_verify_cert(&store_ctx); X509_STORE_CTX_cleanup(&store_ctx); if (i) return YES; else { if (PROT_TRACE) HTTrace("HTSSL: === ERROR: Certificate verification failed.\n"); return NO; } } /*****************************************************************************/ /* Initialization of HTSSL structure. Try to do SSL_connect. If fails still OK - will connect later. */ PRIVATE BOOL HTSSL_init(HTSSL * htssl, int sd) { int err = 0; if (PROT_TRACE) HTTrace("HTSSL_init~~~~~~~~~~~~~~~~\n"); htssl->sd = sd; htssl->connected = NO; htssl->ref_count = 0; htssl->ssl = SSL_new(app_ctx); if (!htssl->ssl) return NO; /* set socket descriptor */ SSL_set_fd (htssl->ssl, sd); /* try to connect */ /* do SSL using sertificate and key */ err = SSL_connect (htssl->ssl); PERROR(err, "\n\nHTSSL_init(): SSL_connect failed"); if (err != -1) { htssl->connected = YES; /* print out what kind of cipher is used */ if (PROT_TRACE) HTTrace("HTSSL_init === Connection using cipher = %s\n", SSL_get_cipher(htssl->ssl)); if (HTSSL_verifyServerCertificate(htssl->ssl)) return YES; else return NO; } else { SSL_free(htssl->ssl); htssl->ssl = NULL; return NO; } return YES; } /*****************************************************************************/ /* This function should be called whenever HTSSL_new creates a new structure or returns a pointer to an existing structure to the caller. This function is not PUBLIC because I want to centralize reference counting in one module in order to minimize possibility of errors. */ PRIVATE void HTSSL_addRef(HTSSL * htssl) { if (PROT_TRACE) HTTrace("HTSSL_addRef ~~~~~~~~~~~~~~~\n"); (htssl->ref_count)++; if (PROT_TRACE) HTTrace("HTSSL_addRef: ref_count = %d ~~~~~~~~~~\n", htssl->ref_count ); } /*****************************************************************************/ /* Using a socket descriptor as a key try to find the HTSSL object in ssl_table. If not there then create a new object and add it to ssl_table. */ PUBLIC HTSSL * HTSSL_new(int sd) { HTSSL * htssl = NULL; int err = 0; HTList * ssls = NULL; if (PROT_TRACE) HTTrace("HTSSL_new ~~~~~~~~~~~~~~~~~~\n"); /* if application context doesn't exist */ if (!app_ctx) { if (!HTSSL_CTX_init()) return NULL; } /* check ssl_table */ if (!ssl_list) ssl_list = HTList_new(); ssls = ssl_list; while (htssl = HTList_nextObject(ssls)){ if (htssl->sd == sd) { if (PROT_TRACE) HTTrace("HTSSL_new: found ssl %p with sd = %d\n", (void*) htssl, sd); /* add reference count before giving pointer to caller */ HTSSL_addRef(htssl); return htssl; } } /* ssl not found : create new */ if ((htssl = (HTSSL *) HT_CALLOC(1, sizeof (HTSSL))) == NULL) HT_OUTOFMEM("HTSSL_new"); /* if initialization fails... */ if (!HTSSL_init(htssl, sd)) { HTSSL_addRef(htssl); HTSSL_free(htssl); return NULL; } HTSSL_addRef(htssl); HTList_addObject (ssl_list, htssl); return htssl; } /*****************************************************************************/ /* destroy HTSSL object if no references to it remain in allpication */ PUBLIC void HTSSL_free(HTSSL * htssl) { if (PROT_TRACE) HTTrace("HTSSL_free ~~~~~~~~~~~~~~\n"); (htssl->ref_count)--; if (PROT_TRACE) HTTrace("HTSSL_free: ref_count = %d ~~~~~~~~~~\n", htssl->ref_count ); if (htssl->ref_count == 0) { if (PROT_TRACE) HTTrace("HTSSL_free: FINAL RELEASE ~~~~~~~~~\n"); if (htssl->ssl) { SSL_free(htssl->ssl); htssl->ssl = NULL; } HTList_removeObject(ssl_list, htssl); /* releases itself */ HT_FREE(htssl); } } /*****************************************************************************/ PUBLIC BOOL HTSSL_connected(HTSSL * htssl) { return htssl->connected; } /*****************************************************************************/ PUBLIC void HTSSL_set_disconnected(HTSSL * htssl) { if (PROT_TRACE) HTTrace("HTSSL_set_disconnected: ~~~~~~~~\n"); htssl->connected = NO; SSL_free(htssl->ssl); htssl->ssl = NULL; } /*****************************************************************************/ PUBLIC BOOL HTSSL_connect(HTSSL * htssl, int sd) { int err = 0; if (PROT_TRACE) HTTrace("HTSSL_connect ~~~~~~~~~~~\n"); if (htssl->connected) return YES; if (!htssl->ssl) { htssl->ssl = SSL_new(app_ctx); if (!htssl->ssl) return NO; /* set socket descriptor */ SSL_set_fd (htssl->ssl, sd); htssl->sd = sd; /* try to connect */ /* do SSL using sertificate and key */ err = SSL_connect (htssl->ssl); PERROR(err, "\n\nHTSSL_init(): SSL_connect failed"); if (err != -1) { htssl->connected = YES; return YES; } return NO; } if (PROT_TRACE) HTTrace("HTSSL_connect WARNING: desconnected HTSSL has non-NULL ssl\n"); return NO; } /*****************************************************************************/ PUBLIC int HTSSL_read(HTSSL * htssl, int sd, char * buff, int len) { /* HTSSL_connect(htssl, sd); */ return SSL_read(htssl->ssl, buff, len); } /*****************************************************************************/ PUBLIC int HTSSL_write(HTSSL * htssl, int sd, char * buff, int len) { /* HTSSL_connect(htssl, sd); */ return SSL_write(htssl->ssl, buff, len); } ===================================================== ===================================================== THESE FILES ARE ALMOST THE SAME AS HTReader.h HTReader.c HTWriter.h HTWriter.c The main differences are: - htssl member is added to stream structures - in HTSSL***_new htssl is initialized - in HT***_free - htssl is destroyed and CloseNotiifcation is set (htssl should be destroyed in ***_abort too...) - in ***_read (***_write) before reading htssl is created (if not yet) and read is done through it. - in HTSSLWriter.h .c one function is renamed (to avoid duplicate with one in HTWriter.c) Note that my HTWriter and HTReader were not latest wwwlib versions, so just copy might not work. New file: HTSSLReader.h ======================= #ifndef HTSSLREADER_H #define HTSSLREADER_H #include "www/HTIOStream.h" #include "www/HTSSLMan.h" #define INPUT_BUFFER_SIZE 32*1024 extern HTInput_new HTSSLReader_new; #endif ===================================================== ===================================================== New file: HTSSLReader.c ======================= /* Library Include files */ #include "www/sysdep.h" #include "www/WWWUtil.h" #include "www/WWWCore.h" #include "www/HTNetMan.h" #include "www/HTSSLReader.h" struct _HTStream { const HTStreamClass * isa; /* ... */ }; struct _HTInputStream { const HTInputStreamClass * isa; HTChannel * ch; HTHost * host; char * write; /* Last byte written */ char * read; /* Last byte read */ int b_read; char data [INPUT_BUFFER_SIZE]; /* buffer */ /******************************* HERE IS NEW MEMEBER ***********/ HTSSL * htssl; }; /* ------------------------------------------------------------------------- */ PRIVATE int HTReader_flush (HTInputStream * me) { HTNet * net = HTHost_getReadNet(me->host); return net && net->readStream ? (*net->readStream->isa->flush)(net->readStream) : HT_OK; } PRIVATE int HTReader_free (HTInputStream * me) { HTNet * net = HTHost_getReadNet(me->host); /******************************* SSL STUFF *********************/ if (PROT_TRACE) HTTrace("HTReader_free ~~~~~~~~~~\n"); if (me->htssl){ HTSSL_free(me->htssl); me->htssl = NULL; /* do it just because SSLeay doesn't want to create a new ssl structure on a non-fresh socket */ HTHost_setCloseNotification (me->host, YES); } if (net && net->readStream) { int status = (*net->readStream->isa->_free)(net->readStream); if (status == HT_OK) net->readStream = NULL; return status; } return HT_OK; } PRIVATE int HTReader_abort (HTInputStream * me, HTList * e) { HTNet * net = HTHost_getReadNet(me->host); if (PROT_TRACE) HTTrace("HTReader_abort ********* ~~~~~~~~~~\n"); if (net && net->readStream) { int status = (*net->readStream->isa->abort)(net->readStream, NULL); if (status != HT_IGNORE) net->readStream = NULL; } return HT_ERROR; } /* Push data from a socket down a stream ** ------------------------------------- ** ** This routine is responsible for creating and PRESENTING any ** graphic (or other) objects described by the file. As this function ** max reads a chunk of data on size INPUT_BUFFER_SIZE, it can be used ** with both blocking or non-blocking sockets. It will always return to ** the event loop, however if we are using blocking I/O then we get a full ** buffer read, otherwise we get what's available. ** ** Returns HT_LOADED if finished reading ** HT_OK if OK, but more to read ** HT_ERROR if error, ** HT_WOULD_BLOCK if read or write would block ** HT_PAUSE if stream is paused */ char * strnstr_1(char * haystack, int *pLen, char * needle) { int found = 0; int need = strlen(needle); int i, start; for (start = i = 0; i < *pLen; i++) if (haystack[i] == needle[found]) { if (++found == need) { i -= need - 1; /* beginning of string */ *pLen -= i; return haystack+i; } } else { found = 0; } *pLen = 0; return NULL; } /* int DebugBufferSize = INPUT_BUFFER_SIZE; */ PRIVATE int HTReader_read (HTInputStream * me) { HTHost * host = me->host; SOCKET soc = HTChannel_socket(me->ch); HTNet * net = HTHost_getReadNet(host); HTRequest * request = HTNet_request(net); int status; if (!net->readStream) { if (PROT_TRACE) HTTrace("Read Socket. No read stream for net object %p\n", net); return HT_ERROR; } /* me->b_read = me->read - me->data; */ /* Read from socket if we got rid of all the data previously read */ do { /* don't read if we have to push unwritten data from last call */ if (me->write >= me->read) { reread_after_interrupt: me->b_read = 0; me->data[0] ='\0'; /* me->b_read = NETREAD(soc, me->data, INPUT_BUFFER_SIZE); */ if (!me->htssl) { me->htssl = HTSSL_new(soc); if (!me->htssl) { /* notify socket close */ HTHost_setCloseNotification (me->host, YES); host->broken_pipe = YES; HTRequest_addSystemError(net->request, ERR_FATAL, socerrno, NO, "NETWRITE"); return HT_ERROR; } } /******************************* read using SSL **********************/ me->b_read = HTSSL_read(me->htssl, soc, me->data, INPUT_BUFFER_SIZE); if (errno == EINTR) { if ( me->b_read < 0 ) { goto reread_after_interrupt; } } if(me->b_read < 0) { #ifdef EAGAIN if (socerrno==EAGAIN || socerrno==EWOULDBLOCK) /* POSIX */ #else if (socerrno==EWOULDBLOCK) /* BSD */ #endif { if (PROT_TRACE) HTTrace("Read Socket. WOULD BLOCK fd %d\n",soc); HTHost_register(host, net, HTEvent_READ); return HT_WOULD_BLOCK; #ifdef ECONNRESET } else if (!me->b_read || socerrno==ECONNRESET) { goto socketClosed; #else } else if (!me->b_read) { goto socketClosed; #endif #ifdef EPIPE } else if (socerrno == EPIPE) { goto socketClosed; #endif /* EPIPE */ } else { /* We have a real error */ /* HERE WE SHOULD RETURN target abort */ if (request) HTRequest_addSystemError(request, ERR_FATAL, socerrno, NO, "NETREAD"); HTSSL_set_disconnected(me->htssl); return HT_ERROR; } /*#ifdef ECONNRESET } else if (!me->b_read || socerrno==ECONNRESET) { #else */ } else if (!me->b_read) { /*#endif*/ socketClosed: if (PROT_TRACE) HTTrace("Read Socket. FIN received on socket %d\n", soc); if (request) { HTAlertCallback *cbf = HTAlert_find(HT_PROG_DONE); if (PROT_TRACE) if (cbf) (*cbf)(request, HT_PROG_DONE, HT_MSG_NULL, NULL, NULL, NULL); } HTHost_unregister(host, net, HTEvent_READ); HTHost_register(host, net, HTEvent_CLOSE); HTSSL_set_disconnected(me->htssl); HTSSL_free(me->htssl); me->htssl = NULL; return HT_CLOSED; } /* Remember how much we have read from the input socket */ HTTraceData(me->data, me->b_read, "HTReader.... Reading"); me->write = me->data; me->read = me->data + me->b_read; #ifdef FIND_SIGNATURES { char * ptr = me->data; int len = me->b_read; while ((ptr = strnstr_1(ptr, &len, "HTTP/1.1 200 OK")) != NULL) { if (PROT_TRACE) HTTrace("Read Socket. Signature found at 0x%x of 0x%x.\n", ptr - me->data, me->b_read); ptr++; len--; } } #endif /* FIND_SIGNATURES */ #ifdef NOT_ASCII { char *p = me->data; while (p < me->read) { *p = FROMASCII(*p); p++; } } #endif /* NOT_ASCII */ if (PROT_TRACE) HTTrace("Read Socket. %d bytes read from socket %d\n", me->b_read, soc); if (request) { HTAlertCallback * cbf = HTAlert_find(HT_PROG_READ); #if 0 /* byte account is done later */ net->bytesRead += me->b_read; #endif if (cbf) (*cbf)(request, HT_PROG_READ, HT_MSG_NULL, NULL, NULL, NULL); } } /* Now push the data down the stream */ if ((status = (*net->readStream->isa->put_block) (net->readStream, me->write, me->b_read)) != HT_OK) { if (status == HT_WOULD_BLOCK) { if (PROT_TRACE) HTTrace("Read Socket. Target WOULD BLOCK\n"); HTHost_unregister(host, net, HTEvent_READ); return HT_WOULD_BLOCK; } else if (status == HT_PAUSE) { if (PROT_TRACE) HTTrace("Read Socket. Target PAUSED\n"); HTHost_unregister(host, net, HTEvent_READ); return HT_PAUSE; /* CONTINUE code or stream code means data was consumed */ } else if (status == HT_CONTINUE || status > 0) { if (status == HT_CONTINUE) { if (PROT_TRACE) HTTrace("Read Socket. CONTINUE\n"); } else if (PROT_TRACE) HTTrace("Read Socket. Target returns %d\n", status); /* me->write = me->read; */ return status; } else { /* We have a real error */ if (PROT_TRACE) HTTrace("Read Socket. Target ERROR %d\n", status); return status; } } me->write = me->read; { int remaining = HTHost_remainingRead(host); if (remaining > 0) { if (PROT_TRACE) HTTrace("Read Socket. DIDN'T CONSUME %d BYTES: `%s\'\n", remaining, me->read); HTHost_setConsumed(host, remaining); } } } while (net->preemptive); HTHost_register(host, net, HTEvent_READ); return HT_WOULD_BLOCK; } /* ** The difference between the close and the free method is that we don't ** close the connection in the free method - we only call the free method ** of the target stream. That way, we can keep the input stream as long ** as the channel itself. */ PRIVATE int HTReader_close (HTInputStream * me) { int status = HT_OK; HTNet * net = HTHost_getReadNet(me->host); if (net && net->readStream) { if ((status = (*net->readStream->isa->_free)(net->readStream))==HT_WOULD_BLOCK) return HT_WOULD_BLOCK; net->readStream = NULL; } if (PROT_TRACE) HTTrace("Socket read. FREEING....\n"); HT_FREE(me); return status; } PUBLIC int HTSSLReader_consumed (HTInputStream * me, size_t bytes) { me->write += bytes; me->b_read -= bytes; HTHost_setRemainingRead(me->host, me->b_read); return HT_OK; } PRIVATE const HTInputStreamClass HTSSLReader = { "SocketReader", HTReader_flush, HTReader_free, HTReader_abort, HTReader_read, HTReader_close, HTSSLReader_consumed }; /* ** Create a new input read stream. Before we actually create it we check ** to see whether we already have an input stream for this channel and if ** so we just return that. This means that we can reuse input streams ** in persistent connections, for example. */ PUBLIC HTInputStream * HTSSLReader_new (HTHost * host, HTChannel * ch, void * param, int mode) { if (host && ch) { HTInputStream * me = HTChannel_input(ch); if (me == NULL) { if ((me=(HTInputStream *) HT_CALLOC(1, sizeof(HTInputStream))) == NULL) HT_OUTOFMEM("HTReader_new"); me->isa = &HTSSLReader; me->ch = ch; me->host = host; me->htssl = NULL; } return me; } return NULL; } ===================================================== ===================================================== New file: HTSSLWriter.h ======================= #ifndef HTSSLWRITE_H #define HTSSLWRITE_H #include "www/HTIOStream.h" #include "www/HTSSLMan.h" /* */ extern HTOutput_new HTSSLWriter_new; extern BOOL HTSSLWriter_set (HTOutputStream * me, HTNet * net, HTChannel * ch, void * param, int mode); /* */ #endif ===================================================== ===================================================== New file: HTSSLWriter.c ======================= /* Library include files */ #include "www/sysdep.h" #include "www/WWWUtil.h" #include "www/WWWCore.h" #include "www/HTNet.h" #include "www/HTNetMan.h" #include "www/HTSSLWriter.h" /* Implemented here */ #include "www/HTHstMan.h" struct _HTStream { const HTStreamClass * isa; /* ... */ }; struct _HTOutputStream { const HTOutputStreamClass * isa; HTChannel * ch; HTHost * host; int offset; #ifdef NOT_ASCII char * ascbuf; /* Buffer for TOASCII conversion */ #endif HTSSL * htssl; }; /* ------------------------------------------------------------------------- */ PRIVATE int HTWriter_flush (HTOutputStream * me) { return HT_OK; /* As we don't have any output buffer */ } PRIVATE int HTWriter_free (HTOutputStream * me) { if (PROT_TRACE) HTTrace("HTWriter_free ~~~~~~~~~~\n"); if (me->htssl){ HTSSL_free(me->htssl); me->htssl = NULL; /* do it just because SSLeay doesn't want to create a new ssl structure on a non-fresh socket */ HTHost_setCloseNotification (me->host, YES); } return HT_OK; } PRIVATE int HTWriter_abort (HTOutputStream * me, HTList * e) { if (PROT_TRACE) HTTrace("HHWriter_abort *********~~~~~~~~~~\n"); return HT_ERROR; } /* Write to the socket ** ** According to Solaris 2.3 man on write: ** ** o If O_NONBLOCK and O_NDELAY are clear, write() blocks ** until the data can be accepted. ** ** o If O_NONBLOCK or O_NDELAY is set, write() does not ** block the process. If some data can be written ** without blocking the process, write() writes what it ** can and returns the number of bytes written. Other- ** wise, if O_NONBLOCK is set, it returns - 1 and sets ** errno to EAGAIN or if O_NDELAY is set, it returns 0. ** ** According to SunOS 4.1.1 man on write: ** ** + If the descriptor is marked for non-blocking I/O ** using fcntl() to set the FNONBLOCK or O_NONBLOCK ** flag (defined in <sys/fcntl.h>), write() requests ** for {PIPE_BUF} (see pathconf(2V)) or fewer bytes ** either succeed completely and return nbyte, or ** return -1 and set errno to EAGAIN. A write() request ** for greater than {PIPE_BUF} bytes either transfers ** what it can and returns the number of bytes written, ** or transfers no data and returns -1 and sets errno ** to EAGAIN. If a write() request is greater than ** {PIPE_BUF} bytes and all data previously written to ** the pipe has been read, write() transfers at least ** {PIPE_BUF} bytes. */ PRIVATE int HTWriter_write (HTOutputStream * me, const char * buf, int len) { HTHost * host = me->host; SOCKET soc = HTChannel_socket(HTHost_channel(host)); HTNet * net = HTHost_getWriteNet(host); int b_write; char * wrtp; const char *limit = buf+len; /*#ifdef EAI_DBG printf ("\nIn HTWriter_write\n"); fflush(stdout); fflush(stderr); #endif */ /* If we don't have a Net object then return right away */ if (!net) { if (STREAM_TRACE) HTTrace("Write Socket WOULD BLOCK %d (offset %d)\n",soc, me->offset); return HT_ERROR; } #ifdef NOT_ASCII if (len && !me->ascbuf) { /* Generate new buffer */ const char *orig = buf; char *dest; int cnt; if ((me->ascbuf = (char *) HT_MALLOC(len)) == NULL) HT_OUTOFMEM("HTWriter_write"); dest = me->ascbuf; for (cnt=0; cnt<len; cnt++) { *dest = TOASCII(*orig); dest++, orig++; } wrtp = me->ascbuf; limit = me->ascbuf+len; } #else if (!me->offset) wrtp = (char *) buf; else { wrtp = (char *) buf + me->offset; len -= me->offset; me->offset = 0; } #endif /*HTTrace("\nGoinig to NETWRITE = %s\n\n", wrtp);*/ /* setting ssl */ if (!me->htssl) { me->htssl = HTSSL_new(soc); if (!me->htssl) { /* notify socket close - doesn't work... */ HTHost_setCloseNotification (me->host, YES); host->broken_pipe = YES; HTRequest_addSystemError(net->request, ERR_FATAL, socerrno, NO, "NETWRITE"); return HT_ERROR; } } /* Write data to the network */ while (wrtp < limit) { /*b_write = NETWRITE(soc, wrtp, len);*/ b_write = HTSSL_write(me->htssl, soc, wrtp, len); if (b_write < 0) { #ifdef EAI_DBG printf ("\nIn SOCKERRNO = \n"); fflush(stdout); fflush(stderr); #endif #ifdef EAGAIN if (socerrno == EAGAIN || socerrno == EWOULDBLOCK)/* POSIX, SVR4 */ #else if (socerrno == EWOULDBLOCK) /* BSD */ #endif { HTHost_register(host, net, HTEvent_WRITE); me->offset = wrtp - buf; if (STREAM_TRACE) HTTrace("Write Socket WOULD BLOCK %d (offset %d)\n",soc, me->offset); return HT_WOULD_BLOCK; #ifdef EINTR } else if (socerrno == EINTR) { /* ** EINTR A signal was caught during the write opera- ** tion and no data was transferred. */ if (STREAM_TRACE) HTTrace("Write Socket call interruted - try again\n"); continue; #endif } else { #ifdef EPIPE if (socerrno == EPIPE) if (STREAM_TRACE) HTTrace("Write Socket got EPIPE\n"); #endif /* EPIPE */ host->broken_pipe = YES; HTRequest_addSystemError(net->request, ERR_FATAL, socerrno, NO, "NETWRITE"); /* notify HTSSL (shared) object that sock connection is broken so that next read/write will reconnect */ HTSSL_set_disconnected(me->htssl); return HT_ERROR; } } /* We do this unconditionally, should we check to see if we ever blocked? */ HTTraceData(wrtp, b_write, "Writing to socket %d", soc); HTNet_addBytesWritten(net, b_write); wrtp += b_write; len -= b_write; if (STREAM_TRACE) HTTrace("Write Socket %d bytes written to %d\n", b_write, soc); { HTAlertCallback *cbf = HTAlert_find(HT_PROG_WRITE); if (cbf) (*cbf)(net->request, HT_PROG_WRITE, HT_MSG_NULL, NULL, NULL, NULL); } } #ifdef NOT_ASCII HT_FREE(me->ascbuf); #endif return HT_OK; } /* Character handling ** ------------------ */ PRIVATE int HTWriter_put_character (HTOutputStream * me, char c) { return HTWriter_write(me, &c, 1); } /* String handling ** --------------- ** ** Strings must be smaller than this buffer size. */ PRIVATE int HTWriter_put_string (HTOutputStream * me, const char * s) { return HTWriter_write(me, s, (int) strlen(s)); } /* ** The difference between the close and the free method is that we don't ** close the connection in the free method - we only call the free method ** of the target stream. That way, we can keep the output stream as long ** as the channel itself. */ PRIVATE int HTWriter_close (HTOutputStream * me) { if (STREAM_TRACE) HTTrace("Socket write FREEING....\n"); HT_FREE(me); return HT_OK; } PRIVATE const HTOutputStreamClass HTWriter = { "SocketWriter", HTWriter_flush, HTWriter_free, HTWriter_abort, HTWriter_put_character, HTWriter_put_string, HTWriter_write, HTWriter_close }; PUBLIC HTOutputStream * HTSSLWriter_new (HTHost * host, HTChannel * ch, void * param, int mode) { if (CORE_TRACE) HTTrace("\nHTSSLWriter_new: creating --------------------------\n\n "); if (host && ch) { HTOutputStream * me = HTChannel_output(ch); if (!me) { if ((me=(HTOutputStream *) HT_CALLOC(1, sizeof(HTOutputStream)))==NULL) HT_OUTOFMEM("HTWriter_new"); me->isa = &HTWriter; me->ch = ch; me->host = host; me->htssl = NULL; } return me; } return NULL; }
Received on Thursday, 1 April 1999 14:42:15 UTC