diff options
Diffstat (limited to 'base/tps/src/httpClient')
-rw-r--r-- | base/tps/src/httpClient/Cache.cpp | 496 | ||||
-rw-r--r-- | base/tps/src/httpClient/engine.cpp | 775 | ||||
-rw-r--r-- | base/tps/src/httpClient/http.cpp | 307 | ||||
-rw-r--r-- | base/tps/src/httpClient/httpClient.cpp | 130 | ||||
-rw-r--r-- | base/tps/src/httpClient/nscperror.cpp | 358 | ||||
-rw-r--r-- | base/tps/src/httpClient/request.cpp | 431 | ||||
-rw-r--r-- | base/tps/src/httpClient/response.cpp | 1115 |
7 files changed, 3612 insertions, 0 deletions
diff --git a/base/tps/src/httpClient/Cache.cpp b/base/tps/src/httpClient/Cache.cpp new file mode 100644 index 000000000..2ea628f8c --- /dev/null +++ b/base/tps/src/httpClient/Cache.cpp @@ -0,0 +1,496 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + */ +/** BEGIN COPYRIGHT BLOCK + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * Copyright (C) 2007 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/** + * Simple cache implementation + */ +#include <string.h> +#include <time.h> + +// NSS includes +#include "pk11func.h" +#include "hasht.h" + +// NSPR includes +#include "nspr.h" +#include "plhash.h" +#include "plstr.h" +#include "plbase64.h" + +// Always before PSCommonLib.h +#define COMMON_LIB_DLL +#include "httpClient/httpc/PSCommonLib.h" +#include "httpClient/httpc/Defines.h" +//-- #include "httpClient/httpc/PSError.h" +#include "httpClient/httpc/Iterator.h" +#include "httpClient/httpc/Cache.h" +//-- #include "httpClient/httpc/DebugLogger.h" +//-- #include "httpClient/httpc/ErrorLogger.h" + +#include "engine/RA.h" +#include "main/Memory.h" + +//-- static const char *DEBUG_MODULE = NULL; +//-- static const char *DEBUG_CLASS_NAME = "StringKeyCache"; + +// From the NSPR implementation of hashtables +/* Compute the number of buckets in ht */ +#define NBUCKETS(ht) (1 << (PL_HASH_BITS - (ht)->shift)) + + +/** + * Called from the destructor + */ +extern "C" { +static PRIntn onCacheRelease( PLHashEntry* he, PRIntn index, void* arg ); +/** + * Called to allocate and return copies of keys + */ +static PRIntn getKeys( PLHashEntry* he, PRIntn index, void* arg ); +} + +/** + * Constructor + * + * @param key Pointer to the key being cached + * @param data Pointer to the data being cached + */ +CacheEntry::CacheEntry( const char* key, void *data ) { + if( key != NULL ) { + m_key = strdup( key ); + } else { + m_key = NULL; + } + m_data = data; + // NSPR counts in microseconds + m_startTime = (time_t)(PR_Now() / 1000000); +} + +/** + * Destructor + */ +CacheEntry::~CacheEntry() { + if( m_key != NULL ) { + free( m_key ); + m_key = NULL; + } +} + +/** + * Returns a pointer to the cached key + * + * @return A pointer to the cached key + */ +const char *CacheEntry::GetKey() { + return m_key; +} + +/** + * Returns a pointer to the cached data + * + * @return A pointer to the cached data + */ +void *CacheEntry::GetData() { + return m_data; +} + + +/** + * Returns the time when the entry was created + * + * @return The time when the entry was created + */ +long CacheEntry::GetStartTime() { + return (long)m_startTime; +} + + +/** + * Default constructor + */ +Cache::Cache() { + m_cache = NULL; + m_cacheLock = NULL; +} + +/** + * Constructor + * + * @param name of the cache + * @param ttl Time to live of each cache entry + * @param implicitLock true if the Cache is to do locking internally + * when required; false if the caller will take responsibility + */ +Cache::Cache( const char *name, int ttl, bool implicitLock ) { + + Initialize( name, ttl, implicitLock ); +} + +/** + * Destructor + */ +Cache::~Cache() { + + if( m_cacheLock ) { + PR_DestroyRWLock( m_cacheLock ); + m_cacheLock = NULL; + } + if( m_cache ) { + PL_HashTableEnumerateEntries( m_cache, onCacheRelease, NULL ); + PL_HashTableDestroy( m_cache ); + m_cache = NULL; + } + +} + +/** + * Initializes the object - to be called from the constructor + * + * @param name of the cache + * @param ttl Time to live of each cache entry + * @param implicitLock true if the Cache is to do locking internally + * when required; false if the caller will take responsibility + */ +void Cache::Initialize( const char *name, int ttl, bool implicitLock ) { + + if ( !m_cache ) { + m_implicitLock = implicitLock; + m_ttl = ttl; + m_cache = PL_NewHashTable( 0, + PL_HashString, + PL_CompareStrings, + PL_CompareValues, + NULL, + NULL + ); + m_cacheLock = PR_NewRWLock( PR_RWLOCK_RANK_NONE, name ); + m_name = name; + } + +} + +/** + * Acquires a read lock on the cache. Multiple threads may simultaneously + * have a read lock, but attempts to acquire a read lock will block + * if another thread already has a write lock. It is illegal to request + * a read lock if the thread already has one. + */ +void Cache::ReadLock() { + PR_RWLock_Rlock( m_cacheLock ); +} + +/** + * Acquires a write lock on the cache. Only one thread may have a write + * lock at any given time; attempts to acquire a write lock will block + * if another thread already has one. It is illegal to request + * a write lock if the thread already has one. + */ +void Cache::WriteLock() { + PR_RWLock_Wlock( m_cacheLock ); +} + +/** + * Releases a read or write lock that the thread has on the cache + */ +void Cache::Unlock() { + PR_RWLock_Unlock( m_cacheLock ); +} + +/** + * Returns the number of entries in the cache + * + * @return The number of entries in the cache + */ +int Cache::GetCount() { + int nKeys = 0; + if ( m_implicitLock ) { + ReadLock(); + } + nKeys = m_cache->nentries; + if ( m_implicitLock ) { + Unlock(); + } + return nKeys; +} + +class KeyIterator : public Iterator { +public: + /** + * Constructor + * + * @param ht A hashtable to iterate on + * @param cacheLock Lock for accessing the hashtable + * @param implictLock true if hashtable locking is to be done + * internally + */ + KeyIterator( PLHashTable *ht, PRRWLock *cacheLock, bool implicitLock ) { + m_table = ht; + m_bucketIndex = 0; + m_entry = m_table->buckets[m_bucketIndex]; + m_cacheLock = cacheLock; + m_implicitLock = implicitLock; + } + + /** + * Destructor + */ + virtual ~KeyIterator() { + } + + /** + * Returns true if there is at least one more key + * + * @return true if there is at least one more key + */ + bool HasMore() { + if ( NULL == m_entry ) { + Next(); + } + return ( NULL != m_entry ); + } + + /** + * Returns the next key, if any; the key is deallocated by the Iterator + * in its destructor + * + * @return The next key, if any, or NULL + */ + void *Next() { + PLHashEntry *he = m_entry; + m_entry = (m_entry != NULL) ? m_entry->next : NULL; + int nBuckets = NBUCKETS(m_table); + if ( m_implicitLock ) { + PR_RWLock_Rlock( m_cacheLock ); + } + while ( (NULL == m_entry) && (m_bucketIndex < (nBuckets-1)) ) { + m_bucketIndex++; + m_entry = m_table->buckets[m_bucketIndex]; + } + if ( m_implicitLock ) { + PR_RWLock_Unlock( m_cacheLock ); + } + return ( he != NULL ) ? (void *)he->key : NULL; + } + +private: + PLHashTable *m_table; + PLHashEntry *m_entry; + int m_bucketIndex; + PRRWLock* m_cacheLock; + bool m_implicitLock; +}; + +/** + * Constructor + * + * @param name of the cache + * @param ttl Time to live of each cache entry + * @param implicitLock true if the Cache is to do locking internally + * when required; false if the caller will take responsibility + */ +StringKeyCache::StringKeyCache( const char *name, int ttl, + bool implicitLock ) { + + Initialize( name, ttl, implicitLock ); + +} + +/** + * Destructor + */ +StringKeyCache::~StringKeyCache() { +} + +/** + * Returns a cache entry + * + * @param key The name of the cache entry + * @return The corresponding cache entry, or NULL if not found + */ +CacheEntry *StringKeyCache::Get( const char *key ) { + // Avoid recursion when the debug log is starting up + + if ( m_implicitLock ) { + ReadLock(); + } + CacheEntry *entry = + (CacheEntry *)PL_HashTableLookupConst( m_cache, key ); + if ( m_implicitLock ) { + Unlock(); + } + if ( entry && m_ttl ) { + // Check if the cache entry has expired + // NSPR counts in microseconds + time_t now = (time_t)(PR_Now() / 1000000); + if ( ((long)now - entry->GetStartTime()) > m_ttl ) { + if( key != NULL ) { + Remove( key ); + key = NULL; + } + if( entry != NULL ) { + delete entry; + entry = NULL; + } + // Avoid recursion when the debug log is starting up + if ( PL_strcasecmp( m_name, "DebugLogModuleCache" ) ) { +//-- DebugLogger *logger = DebugLogger::GetDebugLogger( DEBUG_MODULE ); +//-- logger->Log( LOGLEVEL_FINER, DEBUG_CLASS_NAME, +//-- "Get", + RA::Debug( LL_PER_PDU, + "StringKeyCache::Get: ", + "Entry %s expired from cache %s", + key, + m_name ); + } + } + } + + return entry; +} + +/** + * Adds a cache entry + * + * @param key The name of the cache entry; an internal copy is made + * @param value The value of the cache entry + * @return The corresponding cache entry, or NULL if it couldn't be added + */ +CacheEntry *StringKeyCache::Put( const char *key, void *value ) { + CacheEntry *entry = new CacheEntry( key, value ); + if ( m_implicitLock ) { + WriteLock(); + } + PL_HashTableAdd( m_cache, entry->GetKey(), entry ); + if ( m_implicitLock ) { + Unlock(); + } + + return entry; +} + +/** + * Removes a cache entry; does not free the entry object + * + * @param key The name of the cache entry + * @return The corresponding cache entry, or NULL if not found + */ +CacheEntry *StringKeyCache::Remove( const char *key ) { + + if ( m_implicitLock ) { + WriteLock(); + } + CacheEntry *entry = + (CacheEntry *)PL_HashTableLookupConst( m_cache, key ); + if( entry ) { + PL_HashTableRemove( m_cache, key ); + } + if ( m_implicitLock ) { + Unlock(); + } + + return entry; +} + +class KeyArray { +public: + KeyArray( int nKeys ) { + m_nKeys = nKeys; + m_keys = new char *[m_nKeys]; + m_currentKey = 0; + } + virtual ~KeyArray() { + } + int m_currentKey; + int m_nKeys; + char **m_keys; +}; + +/** + * Returns an iterator over keys in the cache + * + * @return An iterator over keys in the cache + */ +Iterator *StringKeyCache::GetKeyIterator() { + return new KeyIterator( m_cache, m_cacheLock, m_implicitLock ); +} + +/** + * Allocates and returns a list of keys in the cache + * + * @param keys Returns an array of names; each name and also the + * array itself are to be freed by the caller with delete + * @return The number of keys found + */ +int StringKeyCache::GetKeys( char ***keys ) { + + int nKeys = GetCount(); + if ( m_implicitLock ) { + ReadLock(); + } + KeyArray keyArray( nKeys ); + PL_HashTableEnumerateEntries( m_cache, getKeys, &keyArray ); + if ( m_implicitLock ) { + Unlock(); + } + if( ( keyArray.m_nKeys < 1 ) && keyArray.m_keys ) { + delete [] keyArray.m_keys; + keyArray.m_keys = NULL; + } + *keys = keyArray.m_keys; + + return keyArray.m_nKeys; +} + +/** + * Adds cache entry keys to an accumulator + */ +extern "C" { +static PRIntn getKeys( PLHashEntry* he, PRIntn index, void* arg ) { + PRIntn result = HT_ENUMERATE_NEXT; + if ( he != NULL ) { + if ( he->key ) { + KeyArray *keys = (KeyArray *)arg; + int len = strlen( (char *)he->key ); + int i = keys->m_currentKey; + keys->m_keys[i] = new char[len+1]; + strcpy( keys->m_keys[i], (char *)he->key ); + keys->m_currentKey++; + } + } + return result; +} + +/** + * Frees keys of entries in cache; does not free values + */ +static PRIntn onCacheRelease( PLHashEntry* he, PRIntn index, void* arg ) { + PRIntn result = HT_ENUMERATE_NEXT; + if( he != NULL ) { + if( he->key != NULL ) { + free( (char *) he->key ); + he->key = NULL; + result = HT_ENUMERATE_REMOVE; + } + } + return result; +} +} // extern "C" diff --git a/base/tps/src/httpClient/engine.cpp b/base/tps/src/httpClient/engine.cpp new file mode 100644 index 000000000..621a37244 --- /dev/null +++ b/base/tps/src/httpClient/engine.cpp @@ -0,0 +1,775 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/** BEGIN COPYRIGHT BLOCK + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * Copyright (C) 2007 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include "nspr.h" +#include "sslproto.h" +#include "prerror.h" + +#include "ssl.h" +#include "nss.h" +#include "pk11func.h" +#include "cert.h" +#include "certt.h" +#include "sslerr.h" +#include "secerr.h" + +#include "httpClient/httpc/engine.h" +#include "httpClient/httpc/http.h" +#include "httpClient/httpc/PSPRUtil.h" +#include "httpClient/httpc/Defines.h" +//-- #include "httpClient/httpc/DebugLogger.h" +#include "engine/RA.h" +#include "main/Memory.h" + +char* certName = NULL; +char* password = NULL; +int ciphers[32]; +int cipherCount = 0; +int _doVerifyServerCert = 1; + +//-- static const char *DEBUG_MODULE = "httpclient"; +//-- static const char *DEBUG_CLASS_NAME = "HttpEngine"; + +PRIntervalTime Engine::globaltimeout = PR_TicksPerSecond()*30; + +static char * ownPasswd( PK11SlotInfo *slot, PRBool retry, void *arg) { + if (!retry) { + if( password != NULL ) { + return PL_strdup(password); + } else { + return PL_strdup( "httptest" ); + } + } else { + return NULL; + } +} + +/** + * Function: SECStatus myBadCertHandler() + * <BR> + * Purpose: This callback is called when the incoming certificate is not + * valid. We define a certain set of parameters that still cause the + * certificate to be "valid" for this session, and return SECSuccess to cause + * the server to continue processing the request when any of these conditions + * are met. Otherwise, SECFailure is return and the server rejects the + * request. + */ +SECStatus myBadCertHandler( void *arg, PRFileDesc *socket ) { + + SECStatus secStatus = SECFailure; + PRErrorCode err; + + /* log invalid cert here */ + + if ( !arg ) { + return secStatus; + } + + *(PRErrorCode *)arg = err = PORT_GetError(); + + /* If any of the cases in the switch are met, then we will proceed */ + /* with the processing of the request anyway. Otherwise, the default */ + /* case will be reached and we will reject the request. */ + + switch (err) { + case SEC_ERROR_INVALID_AVA: + case SEC_ERROR_INVALID_TIME: + case SEC_ERROR_BAD_SIGNATURE: + case SEC_ERROR_EXPIRED_CERTIFICATE: + case SEC_ERROR_UNKNOWN_ISSUER: + case SEC_ERROR_UNTRUSTED_CERT: + case SEC_ERROR_CERT_VALID: + case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: + case SEC_ERROR_CRL_EXPIRED: + case SEC_ERROR_CRL_BAD_SIGNATURE: + case SEC_ERROR_EXTENSION_VALUE_INVALID: + case SEC_ERROR_CA_CERT_INVALID: + case SEC_ERROR_CERT_USAGES_INVALID: + case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION: + case SEC_ERROR_EXTENSION_NOT_FOUND: // Added by Rob 5/21/2002 + secStatus = SECSuccess; + break; + default: + secStatus = SECFailure; + break; + } + + return secStatus; +} + + +PRBool __EXPORT InitSecurity(char* certDir, char* certname, char* certpassword, char *prefix,int verify ) { + if (certpassword) { + password = PL_strdup(certpassword); + } else { + password = PL_strdup( "httptest" ); + } + if (certname) { + certName = PL_strdup(certname); + } + + SECStatus stat; + PR_Init( PR_USER_THREAD, PR_PRIORITY_NORMAL, 0 ); + if (!NSS_IsInitialized()) { + stat = NSS_Initialize( certDir, prefix, prefix,"secmod.db", + NSS_INIT_READONLY); + } else { + stat = SECSuccess; + RA::Debug( LL_PER_PDU, + "initSecurity: ", + "NSS Already initialized" ); + + } + + if (SECSuccess != stat) { + // int err = PR_GetError(); + return PR_FAILURE; + } + PK11_SetPasswordFunc(ownPasswd); + + stat = NSS_SetDomesticPolicy(); + SSL_CipherPrefSetDefault( SSL_RSA_WITH_NULL_MD5, PR_TRUE ); + + _doVerifyServerCert = verify; + + + return PR_TRUE; +} + + +int ssl2Suites[] = { + SSL_EN_RC4_128_WITH_MD5, /* A */ + SSL_EN_RC4_128_EXPORT40_WITH_MD5, /* B */ + SSL_EN_RC2_128_CBC_WITH_MD5, /* C */ + SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5, /* D */ + SSL_EN_DES_64_CBC_WITH_MD5, /* E */ + SSL_EN_DES_192_EDE3_CBC_WITH_MD5, /* F */ + 0 +}; + +int ssl3Suites[] = { + SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA, /* a */ + SSL_FORTEZZA_DMS_WITH_RC4_128_SHA, /* b */ + SSL_RSA_WITH_RC4_128_MD5, /* c */ + SSL_RSA_WITH_3DES_EDE_CBC_SHA, /* d */ + SSL_RSA_WITH_DES_CBC_SHA, /* e */ + SSL_RSA_EXPORT_WITH_RC4_40_MD5, /* f */ + SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, /* g */ + SSL_FORTEZZA_DMS_WITH_NULL_SHA, /* h */ + SSL_RSA_WITH_NULL_MD5, /* i */ + SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, /* j */ + SSL_RSA_FIPS_WITH_DES_CBC_SHA, /* k */ + TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, /* l */ + TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, /* m */ + 0 +}; + +int tlsSuites[] = { +// TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, +// TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, +// TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, +// TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, +// TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, +// TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + TLS_DHE_DSS_WITH_AES_128_CBC_SHA, + TLS_DHE_DSS_WITH_AES_256_CBC_SHA, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA +}; + +void disableAllCiphersOnSocket(PRFileDesc* sock) { + int i; + int numsuites = SSL_NumImplementedCiphers; + + /* disable all the cipher suites for that socket */ + for (i = 0; i<numsuites; i++) { + SSL_CipherPrefSet(sock, SSL_ImplementedCiphers[i], SSL_NOT_ALLOWED); + } +} + +void __EXPORT EnableAllSSL3Ciphers(PRFileDesc* sock) { + int i =0; + while (ssl3Suites[i]) { + SSL_CipherPrefSet(sock, ssl3Suites[i], SSL_ALLOWED); + } +} + +void __EXPORT EnableAllTLSCiphers(PRFileDesc* sock) { + int i =0; + while (tlsSuites[i]) { + SSL_CipherPrefSet(sock, tlsSuites[i++], SSL_ALLOWED); + } +} + +PRBool __EXPORT EnableCipher(const char* cipherString) { + int ndx; + + if (!cipherString) { + return PR_FALSE; + } + + while (0 != (ndx = *cipherString++)) { + int* cptr; + int cipher; + + if (! isalpha(ndx)) { + continue; + } + cptr = islower(ndx) ? ssl3Suites : ssl2Suites; + for (ndx &= 0x1f; (cipher = *cptr++) != 0 && --ndx > 0; ) { + /* do nothing */; + } + ciphers[cipherCount++] = cipher; + } + + return PR_TRUE; +} + +SECStatus certcallback ( + void *arg, + PRFileDesc *fd, + PRBool checksig, + PRBool isServer) { + return SECSuccess; // always succeed +} + +/** + * Function: SECStatus myAuthCertificate() + * <BR> + * Purpose: This function is our custom certificate authentication handler. + * <BR> + * Note: This implementation is essentially the same as the default + * SSL_AuthCertificate(). + */ +extern "C" { + +static SECStatus myAuthCertificate( void *arg, + PRFileDesc *socket, + PRBool checksig, + PRBool isServer ) { + + SECCertUsage certUsage; + CERTCertificate * cert; + void * pinArg; + char * hostName = NULL; + SECStatus secStatus = SECSuccess; +//-- static const char *DEBUG_METHOD_NAME = "myAuthCertificate"; +//-- DebugLogger *logger = DebugLogger::GetDebugLogger( "httpclient"); + + if ( !arg || !socket ) { + return SECFailure; + } + + /* Define how the cert is being used based upon the isServer flag. */ + + certUsage = isServer ? certUsageSSLClient : certUsageSSLServer; + + cert = SSL_PeerCertificate( socket ); + + pinArg = SSL_RevealPinArg( socket ); + + // Skip the server cert verification fconditionally, because our test + // servers do not have a valid root CA cert. + if ( _doVerifyServerCert ) { + + PRLock *verify_lock = RA::GetVerifyLock(); + if (verify_lock == NULL) { + return SECFailure; + } + PR_Lock(verify_lock); + /* This function is not thread-safe. So we need to use a global lock */ + secStatus = CERT_VerifyCertNow( (CERTCertDBHandle *)arg, + cert, + checksig, + certUsage, + pinArg); + PR_Unlock(verify_lock); + + if( SECSuccess != secStatus ) { +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + if (cert == NULL) { + RA::Debug( LL_PER_PDU, + "myAuthCertificate: ", + "Server Certificate Not Found" ); + } else { + if (cert->subjectName == NULL) { + RA::Debug( LL_PER_PDU, + "myAuthCertificate: ", + "Untrusted server certificate" ); + } else { + RA::Debug( LL_PER_PDU, + "myAuthCertificate: ", + "Untrusted server certificate error=%d subject='%s'", PORT_GetError(), cert->subjectName ); + } + } + } + } + + /* If this is a server, we're finished. */ + if (isServer || secStatus != SECSuccess) { + return secStatus; + } + + /* Certificate is OK. Since this is the client side of an SSL + * connection, we need to verify that the name field in the cert + * matches the desired hostname. This is our defense against + * man-in-the-middle attacks. + */ + + /* SSL_RevealURL returns a hostName, not an URL. */ + hostName = SSL_RevealURL( socket ); + + if (hostName && hostName[0]) { + secStatus = CERT_VerifyCertName( cert, hostName ); + if( SECSuccess != secStatus ) { +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "myAuthCertificate: ", + "Server name does not match that in certificate" ); + } + } else { + secStatus = SECFailure; +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "myAuthCertificate: ", + "server name has been specified" ); + } + + if( hostName != NULL ) { + PR_Free( hostName ); + hostName = NULL; + } + + return secStatus; +} + + +/* Function: SECStatus ownGetClientAuthData() + * + * Purpose: This callback is used by SSL to pull client certificate + * information upon server request. + */ +static SECStatus ownGetClientAuthData(void *arg, PRFileDesc *socket, + CERTDistNames *caNames, + CERTCertificate **pRetCert,/*return */ + SECKEYPrivateKey **pRetKey) { + CERTCertificate * cert = NULL; + SECKEYPrivateKey * privKey = NULL; + void * proto_win = NULL; + SECStatus rv = SECFailure; + char * localNickName = (char *)arg; + + proto_win = SSL_RevealPinArg(socket); + + if (localNickName) { + RA::Debug( LL_PER_PDU, + "ownGetClientAuthData: ", + "ownGetClientAuthData looking for nickname=%s", + localNickName ); + cert = PK11_FindCertFromNickname(localNickName, proto_win); + if (cert) { + RA::Debug( LL_PER_PDU, + "ownGetClientAuthData: ", + "ownGetClientAuthData found cert" ); + privKey = PK11_FindKeyByAnyCert(cert, proto_win); + if (privKey) { + RA::Debug( LL_PER_PDU, + "ownGetClientAuthData: ", + "ownGetClientAuthData found priv key for cert" ); + rv = SECSuccess; + } else { + if( cert != NULL ) { + CERT_DestroyCertificate( cert ); + cert = NULL; + } + } + } + else { + RA::Debug( LL_PER_PDU, + "ownGetClientAuthData: ", + "ownGetClientAuthData did NOT find cert" ); + } + + if (rv == SECSuccess) { + *pRetCert = cert; + *pRetKey = privKey; + } + + // if( localNickName != NULL ) { + // free( localNickName ); + // localNickName = NULL; + // } + return rv; + } + else { + RA::Debug( LL_PER_PDU, + "ownGetClientAuthData: ", + "ownGetClientAuthData does not have nickname" ); + } + + char* chosenNickName = certName ? (char *)PL_strdup(certName) : NULL; + if (chosenNickName) { + cert = PK11_FindCertFromNickname(chosenNickName, proto_win); + if (cert) { + privKey = PK11_FindKeyByAnyCert(cert, proto_win); + if (privKey) { + rv = SECSuccess; + } else { + if( cert != NULL ) { + CERT_DestroyCertificate( cert ); + cert = NULL; + } + } + } + } else { + /* no nickname given, automatically find the right cert */ + CERTCertNicknames * names; + int i; + + names = CERT_GetCertNicknames( CERT_GetDefaultCertDB(), + SEC_CERT_NICKNAMES_USER, + proto_win); + + if (names != NULL) { + for( i=0; i < names->numnicknames; i++ ) { + cert = PK11_FindCertFromNickname(names->nicknames[i], + proto_win); + if (!cert) { + continue; + } + + /* Only check unexpired certs */ + if (CERT_CheckCertValidTimes(cert, PR_Now(), PR_FALSE) != + secCertTimeValid) { + if( cert != NULL ) { + CERT_DestroyCertificate( cert ); + cert = NULL; + } + continue; + } + + rv = NSS_CmpCertChainWCANames(cert, caNames); + + if (rv == SECSuccess) { + privKey = PK11_FindKeyByAnyCert(cert, proto_win); + if (privKey) { + // got the key + break; + } + + // cert database password was probably wrong + rv = SECFailure; + break; + }; + } /* for loop */ + CERT_FreeNicknames(names); + } // names + } // no nickname chosen + + if (rv == SECSuccess) { + *pRetCert = cert; + *pRetKey = privKey; + } + + if( chosenNickName != NULL ) { + free( chosenNickName ); + chosenNickName = NULL; + } + + return rv; +} +} // extern "C" + +void nodelay(PRFileDesc* fd) { + PRSocketOptionData opt; + PRStatus rv; + + opt.option = PR_SockOpt_NoDelay; + opt.value.no_delay = PR_FALSE; + + rv = PR_GetSocketOption(fd, &opt); + if (rv == PR_FAILURE) { + return; + } + + opt.option = PR_SockOpt_NoDelay; + opt.value.no_delay = PR_TRUE; + rv = PR_SetSocketOption(fd, &opt); + if (rv == PR_FAILURE) { + return; + } + + return; +} + + +void __EXPORT setDefaultAllTLSCiphers() { + int i =0; + char alg[256]; + while (tlsSuites[i]) { + PR_snprintf((char *)alg, 256, "%x", tlsSuites[i]); + RA::Debug( LL_PER_PDU, + "setDefaultAllTLSCiphers", + alg); + SSL_CipherPrefSetDefault(tlsSuites[i++], PR_TRUE); + } +} + +/** + * Returns a file descriptor for I/O if the HTTP connection is successful + * @param addr PRnetAddr structure which points to the server to connect to + * @param SSLOn boo;elan to state if this is an SSL client + */ +PRFileDesc * Engine::_doConnect(PRNetAddr *addr, PRBool SSLOn, + const PRInt32* cipherSuite, + PRInt32 count, const char *nickName, + PRBool handshake, + /*const SecurityProtocols& secprots,*/ + const char *serverName, PRIntervalTime timeout) { +//-- static const char *DEBUG_METHOD_NAME = "doConnect"; +//-- DebugLogger *logger = DebugLogger::GetDebugLogger( "httpclient"); + PRFileDesc *tcpsock = NULL; + PRFileDesc *sock = NULL; + + SSL_CipherPrefSetDefault(0xC005 /* TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA */, PR_TRUE); + setDefaultAllTLSCiphers(); + + tcpsock = PR_OpenTCPSocket(addr->raw.family); + + if (nickName != NULL) + RA::Debug( LL_PER_PDU, + "Engine::_doConnect: ", + "_doConnect has nickname=%s", + nickName ); + else + RA::Debug( LL_PER_PDU, + "Engine::_doConnect: ", + "_doConnect has nickname=NULL" ); + + if (!tcpsock) { +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, +//XXXX log NSPR error code + RA::Debug( LL_PER_PDU, + "Engine::_doConnect: ", + "PR_OpenTCPSocket returned NULL" ); + return NULL; + } + + nodelay(tcpsock); + + if (PR_TRUE == SSLOn) { + RA::Debug( LL_PER_PDU, + "Engine::_doConnect: ", + "SSL is ON" ); + sock=SSL_ImportFD(NULL, tcpsock); + if (!sock) { + //xxx log + if( tcpsock != NULL ) { + PR_Close( tcpsock ); + tcpsock = NULL; + } + return NULL; + } + + int error = 0; + PRBool rv = SSL_OptionSet(sock, SSL_SECURITY, 1); + if ( SECSuccess == rv ) { + rv = SSL_OptionSet(sock, SSL_HANDSHAKE_AS_CLIENT, 1); + } + if ( SECSuccess == rv ) { + rv = SSL_OptionSet(sock, SSL_ENABLE_SSL3, PR_TRUE); + } + if ( SECSuccess == rv ) { + rv = SSL_OptionSet(sock, SSL_ENABLE_TLS, PR_TRUE); + } + if ( SECSuccess != rv ) { + error = PORT_GetError(); + if( sock != NULL ) { + PR_Close( sock ); + sock = NULL; + } +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "Engine::_doConnect: ", + "SSL_OptionSet error: %d", + error ); + return NULL; + } + + rv = SSL_GetClientAuthDataHook( sock, + ownGetClientAuthData, + (void*)nickName); + if ( SECSuccess != rv ) { + error = PORT_GetError(); + if( sock != NULL ) { + PR_Close( sock ); + sock = NULL; + } +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "Engine::_doConnect: ", + "SSL_GetClientAuthDataHook error: %d", + error ); + return NULL; + } + + rv = SSL_AuthCertificateHook(sock, + (SSLAuthCertificate)myAuthCertificate, + (void *)CERT_GetDefaultCertDB()); + + if (rv != SECSuccess ) { + if( sock != NULL ) { + PR_Close( sock ); + sock = NULL; + } + return NULL; + } + + PRErrorCode errCode = 0; + + rv = SSL_BadCertHook( sock, + (SSLBadCertHandler)myBadCertHandler, + &errCode ); + rv = SSL_SetURL( sock, serverName ); + + if (rv != SECSuccess ) { + error = PORT_GetError(); + if( sock != NULL ) { + PR_Close( sock ); + sock = NULL; + } +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "Engine::_doConnect: ", + "SSL_SetURL error: %d", + error ); + return NULL; + } + + RA::Debug( LL_PER_PDU, + "Engine::_doConnect: ", + "end SSL is ON" ); + //EnableAllTLSCiphers( sock); + //EnableAllSSL3Ciphers( sock); + } else { + RA::Debug( LL_PER_PDU, + "Engine::_doConnect: ", + "SSL is OFF" ); + sock = tcpsock; + } + + RA::Debug( LL_PER_PDU, + "Engine::_doConnect: ", + "about to call PR_Connect, timeout =%d", + timeout ); + + if ( PR_Connect(sock, addr, timeout) == PR_FAILURE ) { +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "Engine::_doConnect: ", + "PR_Connect error: %d Msg=%s", + PR_GetError(), + "XXX" ); + if( sock != NULL ) { + PR_Close( sock ); + sock = NULL; + } + return NULL; + } + + return (sock); +} + +/** + * Called from higher level to connect, sends a request + * and gets a response as an HttpResponse object + * + * @param request Contains the entire request url + headers etc + * @param server Has the host, port, protocol info + * @param timeout Time in seconds to wait for a response + * @return The response body and headers + */ +PSHttpResponse * HttpEngine::makeRequest( PSHttpRequest &request, + const PSHttpServer& server, + int timeout, PRBool expectChunked ) { + PRNetAddr addr; + PRFileDesc *sock = NULL; + PSHttpResponse *resp = NULL; + + PRBool response_code = 0; + + server.getAddr(&addr); + + char *nickName = request.getCertNickName(); + + char *serverName = (char *)server.getAddr(); + sock = _doConnect( &addr, request.isSSL(), 0, 0,nickName, 0, serverName ); + + if ( sock != NULL) { + PRBool status = request.send( sock ); + if ( status ) { + resp = new PSHttpResponse( sock, &request, timeout, expectChunked ); + response_code = resp->processResponse(); + + RA::Debug( LL_PER_PDU, + "HttpEngine::makeRequest: ", + "makeRequest response %d", + response_code ); + + if(!response_code) + { + RA::Debug( LL_PER_PDU, + "HttpEngine::makeRequest: ", + "Deleting response because of FALSE return, returning NULL." ); + if( resp != NULL ) { + delete resp; + resp = NULL; + } + if( sock != NULL ) { + PR_Close( sock ); + sock = NULL; + } + + return NULL; + + } + } + if( sock != NULL ) { + PR_Close( sock ); + sock = NULL; + } + } + + return resp; +} diff --git a/base/tps/src/httpClient/http.cpp b/base/tps/src/httpClient/http.cpp new file mode 100644 index 000000000..60ca48bf5 --- /dev/null +++ b/base/tps/src/httpClient/http.cpp @@ -0,0 +1,307 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/** BEGIN COPYRIGHT BLOCK + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * Copyright (C) 2007 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include <string.h> + +#include "httpClient/httpc/http.h" +#include "httpClient/httpc/engine.h" +#include "httpClient/httpc/request.h" +#include "httpClient/httpc/response.h" +//-- #include "httpClient/httpc/DebugLogger.h" +//-- #include "httpClient/httpc/ErrorLogger.h" +#include "httpClient/httpc/PSPRUtil.h" +#include "httpClient/httpc/Defines.h" +#include "engine/RA.h" +#include "main/Memory.h" + + +//-- static const char *DEBUG_MODULE = "httpclient"; +//-- static const char *DEBUG_CLASS_NAME = "PSHttpServer"; + +/** + * Constructor + * @param addr The hostname:port of the server to connect to. The default + * port is 80 + * @param af The protocol family like PR_AF_INET + */ +PSHttpServer::PSHttpServer(const char *addr, PRUint16 af) { + SSLOn = PR_FALSE; + PRUint16 port = 80; +//-- static const char *DEBUG_METHOD_NAME = "Constructor"; +//-- DebugLogger *logger = DebugLogger::GetDebugLogger( DEBUG_MODULE ); + + char *pPort; + + + _addr = NULL; +// if( _addr != NULL ) { +// PL_strfree( _addr ); +// _addr = NULL; +// } + + if (addr) { + _addr = PL_strdup(addr); + } + + pPort = PL_strchr(_addr, ':'); + if (pPort) { + *pPort = '\0'; + port = (PRUint16) atoi(++pPort); + } + + /* kludge for doing IPv6 tests on localhost */ + if (!PL_strcmp(_addr, "ip6-localhost") && (af == PR_AF_INET6)) { + PL_strcpy(_addr, "::1"); + } + +// PR_InitializeNetAddr(PR_IpAddrNull, port, &_netAddr); + + if (PR_StringToNetAddr(_addr, &_netAddr) == PR_FAILURE) { + char buf[2000]; + PRHostEnt ent; + + RA::Debug( LL_PER_PDU, + "PSHttpServer::PSHttpServer ", + " host %s port %d ",_addr,port ); + PR_InitializeNetAddr(PR_IpAddrNull, port, &_netAddr); + if (PR_GetIPNodeByName(_addr, af, PR_AI_DEFAULT, + buf, sizeof(buf), &ent) == PR_SUCCESS) { + PR_EnumerateHostEnt(0, &ent, port, &_netAddr); + } else { +//-- ErrorLogger::GetErrorLogger()->Log( +//-- LOGLEVEL_SEVERE, PR_GetError(), + RA::Debug( LL_PER_PDU, + "PSHttpServer::PSHttpServer: ", + "PR_GetIPNodeByName returned error %d [%s] for " + "address %s", + PR_GetError (), + "XXX", + addr ); +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpServer::PSHttpServer: ", + "PR_GetIPNodeByName returned error %d [%s] for " + "address %s", + PR_GetError(), + "XXX", + addr ); + } + } +} + +/** + * Destructor of the Httpserver class + */ +PSHttpServer::~PSHttpServer() { + if( _addr != NULL ) { + PL_strfree( _addr ); + _addr = NULL; + } +} + +/** + * Turns SSL on or off for the connection + * @param SSLstate PR_TRUE to make an SSL connection + */ +void PSHttpServer::setSSL(PRBool SSLstate) { + SSLOn = SSLstate; +} + +/** + * Returns the current SSL state for this PSHttpServer object + * @return PR_TRUE if SSL is enabled else PR_FALSE + */ +PRBool PSHttpServer::isSSL() const { + return SSLOn; +} + +/** + * Returns the IP address of the HTTP server + * @return IP address of the server as a long + */ + +long PSHttpServer::getIp() const { + return _netAddr.inet.ip; +} + +/** + * Returns the port for the HTTP server + * @return port of the server + */ + +long PSHttpServer::getPort() const { + return (long) PR_ntohs(_netAddr.inet.port); +} + +/** + * Returns the server IP address as a string + * @return server address as string +*/ +const char * PSHttpServer::getAddr() const { + return _addr; +} + +/** + * Gets the server addr as a PR_NetAddr structure + * @param addr PR_netaddr struct in which server address is returned + */ +void PSHttpServer::getAddr(PRNetAddr *addr) const { + memcpy(addr, &_netAddr, sizeof(_netAddr)); +} + +/** + * Fets the protocol as string: "HTTP/1.0" "HTTP/1.1" etc + * @return Protocol string + */ +const char *HttpProtocolToString(HttpProtocol proto) { + switch(proto) { + case HTTP09: + return ""; + case HTTP10: + return "HTTP/1.0"; + case HTTP11: + return "HTTP/1.1"; + case HTTPBOGUS: + return "BOGO-PROTO"; + case HTTPNA: + return NULL; + } + + return NULL; +} + +/** +* Constructor for HttpMessage. This is a base class for PSHttpRequest +*/ +HttpMessage :: HttpMessage(long len, const char* buf) { + firstline = NULL; + cl = 0; + proto = HTTPNA; + + // search for the first line + int counter=0; + PRBool found = PR_FALSE; + while ( ( (counter++<len) && (PR_FALSE == found) ) ) { + if (buf[counter] != '\n') { + continue; + } + found = PR_TRUE; + } + + // extract the first line + if (PR_TRUE == found) { + firstline=new char[counter+1]; + memcpy(firstline, buf, counter); + firstline[counter] = '\0'; + } +} + +HttpMessage :: ~HttpMessage() { + if( firstline != NULL ) { + delete firstline; + firstline = NULL; + } +} + +/*SecurityProtocols :: SecurityProtocols(PRBool s2, PRBool s3, PRBool t) +{ + ssl2 = s2; + ssl3 = s3; + tls = t; +}; + +const SecurityProtocols& SecurityProtocols :: operator = (const RWTPtrSlist<char>& protlist) +{ + ssl2 = PR_FALSE; + ssl3 = PR_FALSE; + tls = PR_FALSE; + PRInt32 i; + for (i = 0;i<protlist.entries();i++) + { + if (0 == strcmp(protlist.at(i), "SSL2")) + { + ssl2 = PR_TRUE; + }; + if (0 == strcmp(protlist.at(i), "SSL3")) + { + ssl3 = PR_TRUE; + }; + if (0 == strcmp(protlist.at(i), "TLS")) + { + tls = PR_TRUE; + }; + }; + return *this; +}; + +const SecurityProtocols& SecurityProtocols :: operator = (const SecurityProtocols& rhs) +{ + ssl2 = rhs.ssl2; + ssl3 = rhs.ssl3; + tls = rhs.tls; + return *this; +}; +*/ + + +PRBool PSHttpServer::putFile(const char* localFile, + const char* remoteUri) const { + PSHttpRequest request(this, remoteUri, HTTP10, Engine::globaltimeout); + request.setMethod("PUT"); + request.useLocalFileAsBody(localFile); + + PRBool rv = _putFile(request); + return rv; +} + +PRBool PSHttpServer::putFile(const char *uri, int size) const { + PSHttpRequest request(this, uri, HTTP10, Engine::globaltimeout); + request.setMethod("PUT"); + request.addRandomBody(size); + + PRBool rv = _putFile(request);; + return rv; +} + +PRBool PSHttpServer::_putFile(PSHttpRequest& request) const { + HttpEngine engine; + PRBool rv = PR_TRUE; + + PSHttpResponse* response = engine.makeRequest(request, *this); + + if (response) { + int status = response->getStatus(); + if (status == 200 || status == 201 || status == 204) { + rv = PR_TRUE; + } else { + rv = PR_FALSE; + } + if( response != NULL ) { + delete response; + response = NULL; + } + } else { + rv = PR_FALSE; + } + return rv; +} + diff --git a/base/tps/src/httpClient/httpClient.cpp b/base/tps/src/httpClient/httpClient.cpp new file mode 100644 index 000000000..7f4e9fff3 --- /dev/null +++ b/base/tps/src/httpClient/httpClient.cpp @@ -0,0 +1,130 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; +// version 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// Copyright (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +#include "nspr.h" +#include <sys/types.h> + +#include <stdio.h> +#ifndef XP_WIN32 +#include <unistd.h> /* sleep */ +#else /* XP_WIN32 */ +#include <windows.h> +#endif /* XP_WIN32 */ + +#include "main/Base.h" +#include "httpClient/httpc/http.h" +#include "httpClient/httpc/request.h" +#include "httpClient/httpc/response.h" +#include "httpClient/httpc/engine.h" + +#include "engine/RA.h" +#include "main/Memory.h" + +/* + * httpSend: sends to an HTTP server + * host_port should be in the for "host:port" + * e.g. ca.fedora.redhat.com:1027 + * uri should contain uri including parameter values + * e.g. https://ca.fedora.redhat.com:1027/ca/profileSubmitSSLClient?profileId=userKey&screenname=user1&publickey=YWJjMTIzCg + * method has to be "GET" or "POST" + * body is the HTTP body. Can have nothing. + */ +PSHttpResponse *httpSend(char *host_port, char *uri, char *method, char *body) +{ + const char* nickname; + nickname = RA::GetConfigStore()->GetConfigAsString("ra.clientNickname", ""); + + char *pPort = NULL; + char *pPortActual = NULL; + + + char hostName[512]; + + /* + * Isolate the host name, account for IPV6 numeric addresses. + * + */ + + if(host_port) + strncpy(hostName,host_port,512); + + pPort = hostName; + while(1) { + pPort = strchr(pPort, ':'); + if (pPort) { + pPortActual = pPort; + pPort++; + } else + break; + } + + if(pPortActual) + *pPortActual = '\0'; + + + /* + * Rifle through the values for the host + */ + + PRAddrInfo *ai; + void *iter; + PRNetAddr addr; + int family = PR_AF_INET; + + ai = PR_GetAddrInfoByName(hostName, PR_AF_UNSPEC, PR_AI_ADDRCONFIG); + if (ai) { + printf("%s\n", PR_GetCanonNameFromAddrInfo(ai)); + iter = NULL; + while ((iter = PR_EnumerateAddrInfo(iter, ai, 0, &addr)) != NULL) { + char buf[512]; + PR_NetAddrToString(&addr, buf, sizeof buf); + RA::Debug( LL_PER_PDU, + "PSHttpResponse::httpSend: ", + "Sending addr -- Msg='%s'\n", + buf ); + family = PR_NetAddrFamily(&addr); + RA::Debug( LL_PER_PDU, + "PSHttpResponse::httpSend: ", + "Sending family -- Msg='%d'\n", + family ); + break; + } + PR_FreeAddrInfo(ai); + + } + + PSHttpServer server(host_port, family); + server.setSSL(PR_TRUE); + // use "HTTP10" if no chunking + PSHttpRequest request( &server, uri, HTTP11, 0 ); + request.setSSL(PR_TRUE); + request.setCertNickName(nickname); + request.setMethod(method); + if (body != NULL) + request.setBody( strlen(body), body); + + // use with "POST" only + request.addHeader( "Content-Type", "text/xml" ); + request.addHeader( "Connection", "keep-alive" ); + HttpEngine engine; + PSHttpResponse *resp = engine.makeRequest( request, server, 120 /*_timeout*/ , PR_TRUE /* expect chunked*/); + + return resp; +} diff --git a/base/tps/src/httpClient/nscperror.cpp b/base/tps/src/httpClient/nscperror.cpp new file mode 100644 index 000000000..38c722de2 --- /dev/null +++ b/base/tps/src/httpClient/nscperror.cpp @@ -0,0 +1,358 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; +// version 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// Copyright (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +/* nscperrors.c + * Very crude error handling for nspr and libsec. + */ + +#include "prerror.h" + +#define NSCP_NSPR_ERROR_BASE (PR_NSPR_ERROR_BASE) +#define NSCP_NSPR_MAX_ERROR ((PR_MAX_ERROR) - 1) +#define NSCP_LIBSEC_ERROR_BASE (-8192) +#define NSCP_LIBSEC_MAX_ERROR (NSCP_LIBSEC_ERROR_BASE + 118) +#define NSCP_LIBSSL_ERROR_BASE (-12288) +#define NSCP_LIBSSL_MAX_ERROR (NSCP_LIBSSL_ERROR_BASE + 89) + +typedef struct nscp_error_t { + int errorNumber; + const char *errorString; +} nscp_error_t; + +nscp_error_t nscp_nspr_errors[] = { + { 0, "Out of memory" }, + { 1, "Bad file descriptor" }, + { 2, "Data temporarily not available" }, + { 3, "Access fault" }, + { 4, "Invalid method" }, + { 5, "Illegal access" }, + { 6, "Unknown error" }, + { 7, "Pending interrupt" }, + { 8, "Not implemented" }, + { 9, "IO error" }, + { 10, "IO timeout error" }, + { 11, "IO already pending error" }, + { 12, "Directory open error" }, + { 13, "Invalid Argument" }, + { 14, "Address not available" }, + { 15, "Address not supported" }, + { 16, "Already connected" }, + { 17, "Bad address" }, + { 18, "Address already in use" }, + { 19, "Connection refused" }, + { 20, "Network unreachable" }, + { 21, "Connection timed out" }, + { 22, "Not connected" }, + { 23, "Load library error" }, + { 24, "Unload library error" }, + { 25, "Find symbol error" }, + { 26, "Insufficient resources" }, + { 27, "Directory lookup error" }, + { 28, "Invalid thread private data key" }, + { 29, "PR_PROC_DESC_TABLE_FULL_ERROR" }, + { 30, "PR_SYS_DESC_TABLE_FULL_ERROR" }, + { 31, "Descriptor is not a socket" }, + { 32, "Descriptor is not a TCP socket" }, + { 33, "Socket address is already bound" }, + { 34, "No access rights" }, + { 35, "Operation not supported" }, + { 36, "Protocol not supported" }, + { 37, "Remote file error" }, + { 38, "Buffer overflow error" }, + { 39, "Connection reset by peer" }, + { 40, "Range error" }, + { 41, "Deadlock error" }, + { 42, "File is locked" }, + { 43, "File is too big" }, + { 44, "No space on device" }, + { 45, "Pipe error" }, + { 46, "No seek on device" }, + { 47, "File is a directory" }, + { 48, "Loop error" }, + { 49, "Name too long" }, + { 50, "File not found" }, + { 51, "File is not a directory" }, + { 52, "Read-only filesystem" }, + { 53, "Directory not empty" }, + { 54, "Filesystem mounted" }, + { 55, "Not same device" }, + { 56, "Directory corrupted" }, + { 57, "File exists" }, + { 58, "Maximum directory entries" }, + { 59, "Invalid device state" }, + { 60, "Device is locked" }, + { 61, "No more files" }, + { 62, "End of file" }, + { 63, "File seek error" }, + { 64, "File is busy" }, + { 65, "NSPR error 65" }, + { 66, "In progress error" }, + { 67, "Already initiated" }, + { 68, "Group empty" }, + { 69, "Invalid state" }, + { 70, "Network down" }, + { 71, "Socket shutdown" }, + { 72, "Connect aborted" }, + { 73, "Host unreachable" } +}; + +#if (PR_MAX_ERROR - PR_NSPR_ERROR_BASE) > 74 +// cfu temporarily get rid of the "#error NSPR error table is too small" error +//#error NSPR error table is too small +#endif + +nscp_error_t nscp_libsec_errors[] = { + { 0, "SEC_ERROR_IO - I/O Error" }, + { 1, "SEC_ERROR_LIBRARY_FAILURE - Library Failure" }, + { 2, "SEC_ERROR_BAD_DATA - Bad data was received" }, + { 3, "SEC_ERROR_OUTPUT_LEN" }, + { 4, "SEC_ERROR_INPUT_LEN" }, + { 5, "SEC_ERROR_INVALID_ARGS" }, + { 6, "SEC_ERROR_INVALID_ALGORITHM - Certificate contains invalid encryption or signature algorithm" }, + { 7, "SEC_ERROR_INVALID_AVA" }, + { 8, "SEC_ERROR_INVALID_TIME - Certificate contains an invalid time value" }, + { 9, "SEC_ERROR_BAD_DER - Certificate is improperly DER encoded" }, + { 10, "SEC_ERROR_BAD_SIGNATURE - Certificate has invalid signature" }, + { 11, "SEC_ERROR_EXPIRED_CERTIFICATE - Certificate has expired" }, + { 12, "SEC_ERROR_REVOKED_CERTIFICATE - Certificate has been revoked" }, + { 13, "SEC_ERROR_UNKNOWN_ISSUER - Certificate is signed by an unknown issuer" }, + { 14, "SEC_ERROR_BAD_KEY - Invalid public key in certificate." }, + { 15, "SEC_ERROR_BAD_PASSWORD" }, + { 16, "SEC_ERROR_UNUSED" }, + { 17, "SEC_ERROR_NO_NODELOCK" }, + { 18, "SEC_ERROR_BAD_DATABASE - Problem using certificate or key database" }, + { 19, "SEC_ERROR_NO_MEMORY - Out of Memory" }, + { 20, "SEC_ERROR_UNTRUSTED_ISSUER - Certificate is signed by an untrusted issuer" }, + { 21, "SEC_ERROR_UNTRUSTED_CERT" }, + { 22, "SEC_ERROR_DUPLICATE_CERT" }, + { 23, "SEC_ERROR_DUPLICATE_CERT_TIME" }, + { 24, "SEC_ERROR_ADDING_CERT" }, + { 25, "SEC_ERROR_FILING_KEY" }, + { 26, "SEC_ERROR_NO_KEY" }, + { 27, "SEC_ERROR_CERT_VALID" }, + { 28, "SEC_ERROR_CERT_NOT_VALID" }, + { 29, "SEC_ERROR_CERT_NO_RESPONSE" }, + { 30, "SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE" }, + { 31, "SEC_ERROR_CRL_EXPIRED" }, + { 32, "SEC_ERROR_CRL_BAD_SIGNATURE" }, + { 33, "SEC_ERROR_CRL_INVALID" }, + { 34, "SEC_ERROR_EXTENSION_VALUE_INVALID" }, + { 35, "SEC_ERROR_EXTENSION_NOT_FOUND" }, + { 36, "SEC_ERROR_CA_CERT_INVALID" }, + { 37, "SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID" }, + { 38, "SEC_ERROR_CERT_USAGES_INVALID" }, + { 39, "SEC_INTERNAL_ONLY" }, + { 40, "SEC_ERROR_INVALID_KEY" }, + { 41, "SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION" }, + { 42, "SEC_ERROR_OLD_CRL" }, + { 43, "SEC_ERROR_NO_EMAIL_CERT" }, + { 44, "SEC_ERROR_NO_RECIPIENT_CERTS_QUERY" }, + { 45, "SEC_ERROR_NOT_A_RECIPIENT" }, + { 46, "SEC_ERROR_PKCS7_KEYALG_MISMATCH" }, + { 47, "SEC_ERROR_PKCS7_BAD_SIGNATURE" }, + { 48, "SEC_ERROR_UNSUPPORTED_KEYALG" }, + { 49, "SEC_ERROR_DECRYPTION_DISALLOWED" }, + { 50, "XP_SEC_FORTEZZA_BAD_CARD" }, + { 51, "XP_SEC_FORTEZZA_NO_CARD" }, + { 52, "XP_SEC_FORTEZZA_NONE_SELECTED" }, + { 53, "XP_SEC_FORTEZZA_MORE_INFO" }, + { 54, "XP_SEC_FORTEZZA_PERSON_NOT_FOUND" }, + { 55, "XP_SEC_FORTEZZA_NO_MORE_INFO" }, + { 56, "XP_SEC_FORTEZZA_BAD_PIN" }, + { 57, "XP_SEC_FORTEZZA_PERSON_ERROR" }, + { 58, "SEC_ERROR_NO_KRL" }, + { 59, "SEC_ERROR_KRL_EXPIRED" }, + { 60, "SEC_ERROR_KRL_BAD_SIGNATURE" }, + { 61, "SEC_ERROR_REVOKED_KEY" }, + { 62, "SEC_ERROR_KRL_INVALID" }, + { 63, "SEC_ERROR_NEED_RANDOM" }, + { 64, "SEC_ERROR_NO_MODULE" }, + { 65, "SEC_ERROR_NO_TOKEN" }, + { 66, "SEC_ERROR_READ_ONLY" }, + { 67, "SEC_ERROR_NO_SLOT_SELECTED" }, + { 68, "SEC_ERROR_CERT_NICKNAME_COLLISION" }, + { 69, "SEC_ERROR_KEY_NICKNAME_COLLISION" }, + { 70, "SEC_ERROR_SAFE_NOT_CREATED" }, + { 71, "SEC_ERROR_BAGGAGE_NOT_CREATED" }, + { 72, "XP_JAVA_REMOVE_PRINCIPAL_ERROR" }, + { 73, "XP_JAVA_DELETE_PRIVILEGE_ERROR" }, + { 74, "XP_JAVA_CERT_NOT_EXISTS_ERROR" }, + { 75, "SEC_ERROR_BAD_EXPORT_ALGORITHM" }, + { 76, "SEC_ERROR_EXPORTING_CERTIFICATES" }, + { 77, "SEC_ERROR_IMPORTING_CERTIFICATES" }, + { 78, "SEC_ERROR_PKCS12_DECODING_PFX" }, + { 79, "SEC_ERROR_PKCS12_INVALID_MAC" }, + { 80, "SEC_ERROR_PKCS12_UNSUPPORTED_MAC_ALGORITHM" }, + { 81, "SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE" }, + { 82, "SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE" }, + { 83, "SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM" }, + { 84, "SEC_ERROR_PKCS12_UNSUPPORTED_VERSION" }, + { 85, "SEC_ERROR_PKCS12_PRIVACY_PASSWORD_INCORRECT" }, + { 86, "SEC_ERROR_PKCS12_CERT_COLLISION" }, + { 87, "SEC_ERROR_USER_CANCELLED" }, + { 88, "SEC_ERROR_PKCS12_DUPLICATE_DATA" }, + { 89, "SEC_ERROR_MESSAGE_SEND_ABORTED" }, + { 90, "SEC_ERROR_INADEQUATE_KEY_USAGE" }, + { 91, "SEC_ERROR_INADEQUATE_CERT_TYPE" }, + { 92, "SEC_ERROR_CERT_ADDR_MISMATCH" }, + { 93, "SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY" }, + { 94, "SEC_ERROR_PKCS12_IMPORTING_CERT_CHAIN" }, + { 95, "SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME" }, + { 96, "SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY" }, + { 97, "SEC_ERROR_PKCS12_UNABLE_TO_WRITE" }, + { 98, "SEC_ERROR_PKCS12_UNABLE_TO_READ" }, + { 99, "SEC_ERROR_PKCS12_KEY_DATABASE_NOT_INITIALIZED" }, + { 100, "SEC_ERROR_KEYGEN_FAIL" }, + { 101, "SEC_ERROR_INVALID_PASSWORD" }, + { 102, "SEC_ERROR_RETRY_OLD_PASSWORD" }, + { 103, "SEC_ERROR_BAD_NICKNAME" }, + { 104, "SEC_ERROR_NOT_FORTEZZA_ISSUER" }, + { 105, "unused error" }, + { 106, "SEC_ERROR_JS_INVALID_MODULE_NAME" }, + { 107, "SEC_ERROR_JS_INVALID_DLL" }, + { 108, "SEC_ERROR_JS_ADD_MOD_FAILURE" }, + { 109, "SEC_ERROR_JS_DEL_MOD_FAILURE" }, + { 110, "SEC_ERROR_OLD_KRL" }, + { 111, "SEC_ERROR_CKL_CONFLICT" }, + { 112, "SEC_ERROR_CERT_NOT_IN_NAME_SPACE" }, + { 113, "SEC_ERROR_KRL_NOT_YET_VALID" }, + { 114, "SEC_ERROR_CRL_NOT_YET_VALID" }, + { 115, "SEC_ERROR_CERT_STATUS_SERVER_ERROR" }, + { 116, "SEC_ERROR_CERT_STATUS_UNKNOWN" }, + { 117, "SEC_ERROR_CERT_REVOKED_SINCE" }, + { 118, "SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE" } +}; + +nscp_error_t nscp_libssl_errors[] = { + { 0, "SSL_ERROR_EXPORT_ONLY_SERVER - client does not support high-grade encryption." }, + { 1, "SSL_ERROR_US_ONLY_SERVER - client requires high-grade encryption which is not supported." }, + { 2, "SSL_ERROR_NO_CYPHER_OVERLAP - no common encryption algorithm(s) with client." }, + { 3, "SSL_ERROR_NO_CERTIFICATE - unable to find the certificate or key necessary for authentication." }, + { 4, "SSL_ERROR_BAD_CERTIFICATE - unable to communicate securely wih peer: peer's certificate was rejected." }, + { 5, "unused SSL error #5" }, + { 6, "SSL_ERROR_BAD_CLIENT - protocol error." }, + { 7, "SSL_ERROR_BAD_SERVER - protocol error." }, + { 8, "SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE - unsupported certificate type." }, + { 9, "SSL_ERROR_UNSUPPORTED_VERSION - client is using unsupported SSL version." }, + { 10, "unused SSL error #10" }, + { 11, "SSL_ERROR_WRONG_CERTIFICATE - the public key in the server's own certificate does not match its private key" }, + { 12, "SSL_ERROR_BAD_CERT_DOMAIN - requested domain name does not match the server's certificate." }, + { 13, "SSL_ERROR_POST_WARNING" }, + { 14, "SSL_ERROR_SSL2_DISABLED - peer only supports SSL version 2, which is locally disabled" }, + { 15, "SSL_ERROR_BAD_MAC_READ - SSL has received a record with an incorrect Message Authentication Code." }, + { 16, "SSL_ERROR_BAD_MAC_ALERT - SSL has received an error indicating an incorrect Message Authentication Code." }, + { 17, "SSL_ERROR_BAD_CERT_ALERT - SSL client cannot verify your certificate." }, + { 18, "SSL_ERROR_REVOKED_CERT_ALERT - the server has rejected your certificate as revoked." }, + { 19, "SSL_ERROR_EXPIRED_CERT_ALERT - the server has rejected your certificate as expired." }, + { 20, "SSL_ERROR_SSL_DISABLED - cannot connect: SSL is disabled." }, + { 21, "SSL_ERROR_FORTEZZA_PQG - cannot connect: SSL peer is in another Fortezza domain" }, + { 22, "SSL_ERROR_UNKNOWN_CIPHER_SUITE - an unknown SSL cipher suite has been requested" }, + { 23, "SSL_ERROR_NO_CIPHERS_SUPPORTED - no cipher suites are present and enabled in this program" }, + { 24, "SSL_ERROR_BAD_BLOCK_PADDING" }, + { 25, "SSL_ERROR_RX_RECORD_TOO_LONG" }, + { 26, "SSL_ERROR_TX_RECORD_TOO_LONG" }, + { 27, "SSL_ERROR_RX_MALFORMED_HELLO_REQUEST" }, + { 28, "SSL_ERROR_RX_MALFORMED_CLIENT_HELLO" }, + { 29, "SSL_ERROR_RX_MALFORMED_SERVER_HELLO" }, + { 30, "SSL_ERROR_RX_MALFORMED_CERTIFICATE" }, + { 31, "SSL_ERROR_RX_MALFORMED_SERVER_KEY_EXCH" }, + { 32, "SSL_ERROR_RX_MALFORMED_CERT_REQUEST" }, + { 33, "SSL_ERROR_RX_MALFORMED_HELLO_DONE" }, + { 34, "SSL_ERROR_RX_MALFORMED_CERT_VERIFY" }, + { 35, "SSL_ERROR_RX_MALFORMED_CLIENT_KEY_EXCH" }, + { 36, "SSL_ERROR_RX_MALFORMED_FINISHED" }, + { 37, "SSL_ERROR_RX_MALFORMED_CHANGE_CIPHER" }, + { 38, "SSL_ERROR_RX_MALFORMED_ALERT" }, + { 39, "SSL_ERROR_RX_MALFORMED_HANDSHAKE" }, + { 40, "SSL_ERROR_RX_MALFORMED_APPLICATION_DATA" }, + { 41, "SSL_ERROR_RX_UNEXPECTED_HELLO_REQUEST" }, + { 42, "SSL_ERROR_RX_UNEXPECTED_CLIENT_HELLO" }, + { 43, "SSL_ERROR_RX_UNEXPECTED_SERVER_HELLO" }, + { 44, "SSL_ERROR_RX_UNEXPECTED_CERTIFICATE" }, + { 45, "SSL_ERROR_RX_UNEXPECTED_SERVER_KEY_EXCH" }, + { 46, "SSL_ERROR_RX_UNEXPECTED_CERT_REQUEST" }, + { 47, "SSL_ERROR_RX_UNEXPECTED_HELLO_DONE" }, + { 48, "SSL_ERROR_RX_UNEXPECTED_CERT_VERIFY" }, + { 49, "SSL_ERROR_RX_UNEXPECTED_CLIENT_KEY_EXCH" }, + { 50, "SSL_ERROR_RX_UNEXPECTED_FINISHED" }, + { 51, "SSL_ERROR_RX_UNEXPECTED_CHANGE_CIPHER" }, + { 52, "SSL_ERROR_RX_UNEXPECTED_ALERT" }, + { 53, "SSL_ERROR_RX_UNEXPECTED_HANDSHAKE" }, + { 54, "SSL_ERROR_RX_UNEXPECTED_APPLICATION_DATA" }, + { 55, "SSL_ERROR_RX_UNKNOWN_RECORD_TYPE" }, + { 56, "SSL_ERROR_RX_UNKNOWN_HANDSHAKE" }, + { 57, "SSL_ERROR_RX_UNKNOWN_ALERT" }, + { 58, "SSL_ERROR_CLOSE_NOTIFY_ALERT - SSL peer has closed the connection" }, + { 59, "SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT" }, + { 60, "SSL_ERROR_DECOMPRESSION_FAILURE_ALERT" }, + { 61, "SSL_ERROR_HANDSHAKE_FAILURE_ALERT" }, + { 62, "SSL_ERROR_ILLEGAL_PARAMETER_ALERT" }, + { 63, "SSL_ERROR_UNSUPPORTED_CERT_ALERT" }, + { 64, "SSL_ERROR_CERTIFICATE_UNKNOWN_ALERT" }, + { 65, "SSL_ERROR_GENERATE_RANDOM_FAILURE" }, + { 66, "SSL_ERROR_SIGN_HASHES_FAILURE" }, + { 67, "SSL_ERROR_EXTRACT_PUBLIC_KEY_FAILURE" }, + { 68, "SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE" }, + { 69, "SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE" }, + { 70, "SSL_ERROR_ENCRYPTION_FAILURE" }, + { 71, "SSL_ERROR_DECRYPTION_FAILURE" }, + { 72, "SSL_ERROR_SOCKET_WRITE_FAILURE" }, + { 73, "SSL_ERROR_MD5_DIGEST_FAILURE" }, + { 74, "SSL_ERROR_SHA_DIGEST_FAILURE" }, + { 75, "SSL_ERROR_MAC_COMPUTATION_FAILURE" }, + { 76, "SSL_ERROR_SYM_KEY_CONTEXT_FAILURE" }, + { 77, "SSL_ERROR_SYM_KEY_UNWRAP_FAILURE" }, + { 78, "SSL_ERROR_PUB_KEY_SIZE_LIMIT_EXCEEDED" }, + { 79, "SSL_ERROR_IV_PARAM_FAILURE" }, + { 80, "SSL_ERROR_INIT_CIPHER_SUITE_FAILURE" }, + { 81, "SSL_ERROR_SESSION_KEY_GEN_FAILURE" }, + { 82, "SSL_ERROR_NO_SERVER_KEY_FOR_ALG" }, + { 83, "SSL_ERROR_TOKEN_INSERTION_REMOVAL" }, + { 84, "SSL_ERROR_TOKEN_SLOT_NOT_FOUND" }, + { 85, "SSL_ERROR_NO_COMPRESSION_OVERLAP" }, + { 86, "SSL_ERROR_HANDSHAKE_NOT_COMPLETED" }, + { 87, "SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE" }, + { 88, "SSL_ERROR_CERT_KEA_MISMATCH" }, + { 89, "SSL_ERROR_NO_TRUSTED_SSL_CLIENT_CA - the CA that signed the client certificate is not trusted locally" } +}; + +#ifdef WIN32 +#define __EXPORT __declspec(dllexport) +#else +#define __EXPORT +#endif + +__EXPORT const char* nscperror_lookup(int error) +{ + const char *errmsg; + + if ((error >= NSCP_NSPR_ERROR_BASE) && (error <= NSCP_NSPR_MAX_ERROR)) { + errmsg = nscp_nspr_errors[error-NSCP_NSPR_ERROR_BASE].errorString; + return errmsg; + } else if ((error >= NSCP_LIBSEC_ERROR_BASE) && + (error <= NSCP_LIBSEC_MAX_ERROR)) { + return nscp_libsec_errors[error-NSCP_LIBSEC_ERROR_BASE].errorString; + } else if ((error >= NSCP_LIBSSL_ERROR_BASE) && + (error <= NSCP_LIBSSL_MAX_ERROR)) { + return nscp_libssl_errors[error-NSCP_LIBSSL_ERROR_BASE].errorString; + } else { + return (const char *)NULL; + } +} diff --git a/base/tps/src/httpClient/request.cpp b/base/tps/src/httpClient/request.cpp new file mode 100644 index 000000000..629f74821 --- /dev/null +++ b/base/tps/src/httpClient/request.cpp @@ -0,0 +1,431 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + */ +/** BEGIN COPYRIGHT BLOCK + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * Copyright (C) 2007 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include <string.h> +#include "httpClient/httpc/request.h" +#include "httpClient/httpc/engine.h" +#include "httpClient/httpc/PSPRUtil.h" +#include "engine/RA.h" +#include "main/Memory.h" + +//-- static const char *DEBUG_MODULE = "httpclient"; +//-- static const char *DEBUG_CLASS_NAME = "PSHttpRequest"; + +/** + * Constructor + * @param server The server to send request to + * @param uri The uri representing the request e.g /presence/start + * @param prot HTTP10 or HTTP11 . + * @param to Timeout ... ignore for now + */ + +PSHttpRequest::PSHttpRequest(const PSHttpServer* server, + const char *uri, + HttpProtocol prot, + PRIntervalTime to) : NetRequest(server) { + //timeout = to; + timeout = PR_INTERVAL_NO_TIMEOUT; + _method = PL_strdup("GET"); + _uri = PL_strdup(uri); + _proto = prot; + _body = NULL; + _bodyLength = -1; + _expectedResponseLength = -1; + _expectStandardBody = PR_FALSE; + _expectDynamicBody = PR_FALSE; + _hangupOk = PR_FALSE; + _fileFd = NULL; + nickName = NULL; + _headers = new StringKeyCache("request",10*60); +} + +/** + * Destructor + * + */ + +PSHttpRequest::~PSHttpRequest() { + if( _method != NULL ) { + PL_strfree( _method ); + _method = NULL; + } + if( _uri != NULL ) { + PL_strfree( _uri ); + _uri = NULL; + } + if( nickName != NULL ) { + PL_strfree( nickName ); + nickName = NULL; + } + if( _fileFd != NULL ) { + PR_Close( _fileFd ); + _fileFd = NULL; + } + if( _headers != NULL ) { + delete _headers; + _headers = NULL; + } +} + +/** + * sets the request method for Http protocol + * @param method GET /POST etc + * + */ + +PRBool PSHttpRequest::setMethod(const char *method) { + if( _method != NULL ) { + free( _method ); + _method = NULL; + } + _method = PL_strdup(method); + return PR_TRUE; +} + +void PSHttpRequest::setExpectedResponseLength(int size) { + _expectedResponseLength = size; +} + +void PSHttpRequest::setExpectStandardBody() { + _expectStandardBody = PR_TRUE; +} + +void PSHttpRequest::setExpectDynamicBody() { + _expectDynamicBody = PR_TRUE; +} + +PRBool PSHttpRequest::getExpectStandardBody() { + return _expectStandardBody; +} + +PRBool PSHttpRequest::getExpectDynamicBody() { + return _expectDynamicBody; +} + +int PSHttpRequest::getExpectedResponseLength() { + return _expectedResponseLength; +} + +/** + * Returns the method to use + * + * @return GET /POST etc + */ + +char * PSHttpRequest::getMethod() { + return _method; +} + +/** + * Returns HTTP0 or HTTP11 + */ +HttpProtocol HttpMessage::getProtocol() const { + return proto; +} + +/** + * Adds an HTTP header to the request + * + * @param name header name + * @param value header value + */ +PRBool PSHttpRequest::addHeader(const char *name, const char *value) { + char *dvalue = PL_strdup(value); + CacheEntry *entry = _headers->Put(name,dvalue); + if (entry == NULL ) { + if( dvalue != NULL ) { + PL_strfree( dvalue ); + dvalue = NULL; + } + return PR_FALSE; + } else { + return PR_TRUE; + } +} + +/** + * Gets the value for a header for this HTTP request object + * + * @param name Name of the header + * @return The value of the header in the request object + */ + +const char * PSHttpRequest::getHeader(const char *name) { + CacheEntry *entry = _headers->Get(name); + return entry ? (char *)entry->GetData() : NULL; +} + +/** + * Sets the body of a POST message + * + * @param size Content length + * @param body Content of the message; it is not copied + * @return PR_TRUE if the Content-length header can be set + */ +PRBool PSHttpRequest::setBody(int size, const char* body) { + char byteStr[12]; + + sprintf(byteStr, "%d", size); + if (!addHeader("Content-length", byteStr)) { + return PR_FALSE; + } + + _bodyLength = size; + _body = (char *)body; + + return PR_TRUE; +} + +PRBool PSHttpRequest::addRandomBody(int size) { + char byteStr[12]; + + sprintf(byteStr, "%d", size); + if (!addHeader("Content-length", byteStr)) { + return PR_FALSE; + } + + _bodyLength = size; + + return PR_TRUE; +} + +PRBool PSHttpRequest::useLocalFileAsBody(const char* fileName) { + PRBool res = PR_FALSE; + PRFileInfo finfo; + if (PR_GetFileInfo(fileName, &finfo) == PR_SUCCESS) { + res = PR_TRUE; + char byteStr[25]; + sprintf(byteStr, "%d", finfo.size); + if (!addHeader("Content-length", byteStr)) { + return PR_FALSE; + } + _bodyLength = finfo.size; + _fileFd = PR_Open(fileName, PR_RDONLY, 0); + if (!_fileFd) { + return PR_FALSE; + } + } + + return PR_TRUE; +} + +/** + * This function sends the HTTP request to the server. + * @param sock - the connection onto which the request is to be sent + */ + +PRBool PSHttpRequest::send( PRFileDesc *sock ) { + const char *hostname; +//-- static const char *DEBUG_METHOD_NAME = "send"; +//-- DebugLogger *logger = DebugLogger::GetDebugLogger( DEBUG_MODULE ); + + PRBool rv = PR_FALSE; + if (!sock) { + return rv; + } + + char *data = NULL; + + if (_proto == HTTP11) { + hostname = getHeader("Host"); + + if (hostname == NULL) { + // long port = _server->getPort(); + + char address[100]; + PR_snprintf(address, 100, "%s:%d", _server->getAddr(), + _server->getPort()); + addHeader("Host", address); + } + } + + // create the HTTP string "GET /presence/stop HTTP/1.0" + char *path = strstr( _uri, "//" ); + if ( path ) { + path = strchr( path + 2, '/' ); + } + if ( !path ) { + path = _uri; + } + data = PR_smprintf( "%s %s %s\r\n", _method, path, + HttpProtocolToString(_proto) ); + + // Send HTTP headers + char **keys; + char *headerValue = NULL; + int nKeys = _headers->GetKeys( &keys ); + for ( int i = 0 ; i < nKeys; i++ ) { + CacheEntry *entry = _headers->Get( keys[i] ); + if (entry) { + headerValue = (char *)entry->GetData(); + //adds the headers name: value + data = PR_sprintf_append(data,"%s: %s\r\n",keys[i],headerValue); + if( headerValue != NULL ) { + PL_strfree( headerValue ); + headerValue = NULL; + } + } + entry = _headers->Remove(keys[i]); + if( entry != NULL ) { + delete entry; + entry = NULL; + } + if( keys[i] != NULL ) { + delete [] ( keys[i] ); + keys[i] = NULL; + } + } + if( keys != NULL ) { + delete [] keys; + keys = NULL; + } + + // Send terminator + data = PR_sprintf_append(data,"\r\n"); + + int len = PL_strlen(data); + //send the data .. + int bytes = PR_Send(sock, data, len, 0, timeout); + if( data != NULL ) { + PR_smprintf_free( data ); + data = NULL; + } + if ( bytes != len ) { +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpRequest::send: ", + "Error sending request -- PR_Send returned(%d) Msg=%s\n", + PR_GetError(), + "XXX" ); + return PR_FALSE; + } + + if ( _fileFd ) { + // Send body from file + PRInt32 bytesSent = PR_TransmitFile(sock, _fileFd, 0, 0, + PR_TRANSMITFILE_KEEP_OPEN, + timeout); + if ( bytesSent < 0 ) { +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpRequest::send: ", + "Error sending request\n" ); + return PR_FALSE; + } + } else if (_bodyLength > 0) { + // Send internally stored body + char *allocated = NULL; + if ( !_body ) { + // Send a generated pattern + _body = allocated = new char[_bodyLength]; + for ( int index = 0; index < _bodyLength; index++ ) { + _body[index] = (unsigned char)(index %256); + } + } + int sentBytes = 0; + char *toSend = _body; + for ( int i = _bodyLength; i > 0; i -= sentBytes ) { + sentBytes = PR_Send( sock, toSend, i, 0, timeout ); + if ( sentBytes < 0 ) { +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpRequest::send: ", + "Error sending request in PR_Send\n" ); + return PR_FALSE; + } + toSend += sentBytes; + } + if ( allocated ) { + if( _body != NULL ) { + delete [] _body; + _body = NULL; + } + } + } + + return PR_TRUE; +} + +/** + * Sets the nickname for the client cert to be send to the server + * @param certName Nickname of the cert in the cert db + */ +void PSHttpRequest::setCertNickName(const char *certName) { + nickName = PL_strdup(certName); +} + +/** + * Gets the nickname for the client cert + * @return certName Nickname of the cert in the cert db + */ +char * PSHttpRequest::getCertNickName() { + return nickName; +} + +void PSHttpRequest::setHangupOk() { + _hangupOk = PR_TRUE; +} + +PRBool PSHttpRequest::isHangupOk() { + return(_hangupOk); +} + + +/** + * returns PR_TRUE if ssl is enabled for this request + */ +PRBool NetRequest::isSSL() const { + return SSLOn; +} + +/** + * enable/disable SSL for the request + */ +void NetRequest::setSSL(PRBool SSLstate) { + SSLOn=SSLstate; +} + +/** +* Constructor for NetRequest class. This is a superclass of httprequest class +* @param server The server to which the request is to be send +*/ +NetRequest :: NetRequest(const PSHttpServer* server) { + _server = server; + timeout = Engine::globaltimeout; + SSLOn=PR_FALSE; + if (server) + SSLOn=server->isSSL(); + handshake = PR_FALSE; + cipherCount = 0; + cipherSet = NULL; + +} + +/** +* Returns the current configured timeout +*/ +PRIntervalTime NetRequest :: getTimeout() const { + return timeout; +} diff --git a/base/tps/src/httpClient/response.cpp b/base/tps/src/httpClient/response.cpp new file mode 100644 index 000000000..89b900492 --- /dev/null +++ b/base/tps/src/httpClient/response.cpp @@ -0,0 +1,1115 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/** BEGIN COPYRIGHT BLOCK + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * Copyright (C) 2007 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/** + * HTTP response handler + */ + +#include <ctype.h> +#include <string.h> +#include <math.h> + +#include "nspr.h" +#include "engine/RA.h" +#include "main/Util.h" +#include "httpClient/httpc/response.h" +#include "httpClient/httpc/engine.h" +//-- #include "httpClient/httpc/DebugLogger.h" +#include "httpClient/httpc/PSPRUtil.h" +#include "main/Memory.h" + +//-- static const char *DEBUG_MODULE = "httpclient"; +//-- static const char *DEBUG_CLASS_NAME = "PSHttpResponse"; +void printBuf(int , char* ); + +/** + * Constructor. This class is used by the HttpResponse class for reading and + * processing data from the socket + * @param socket The NSPR socket from which the response is expected + * @param size The size of the internal buffer to hold data + * @param timeout Timeout in seconds on receiving a response + */ + +RecvBuf::RecvBuf( const PRFileDesc *socket, int size, int timeout ) { + _socket = socket; + _allocSize = size; + _buf = (char *)PR_Malloc(size); + _curPos = 0; + _curSize = 0; + _chunkedMode = PR_FALSE; + _currentChunkSize = _currentChunkBytesRead = 0; + _timeout = PR_TicksPerSecond() * timeout; + _content = NULL; +} + +/** + * Destructor + */ +RecvBuf::~RecvBuf() { + if( _buf != NULL ) { + PR_Free( _buf ); + _buf = NULL; + } +} + +/** + * Reads the specified number of bytes from the socket and place it into the buffer + * + * @param socket The NSPR socket from which the response is expected + * @param size The size of the buffer + * @return PR_TRUE on success, otherwise PR_FALSE + */ +PRBool RecvBuf::_getBytes(int size) { +//-- DebugLogger *logger = DebugLogger::GetDebugLogger( DEBUG_MODULE ); + PRErrorCode pec; + _curPos = 0; + + int num =1; + int i =0; + PRBool endChunk= PR_FALSE; + RA::Debug( LL_PER_PDU, + "RecvBuf::_getBytes: ", + "Start RecvBuf::_getBytes" ); + // actual reading from the socket happens here + do { + num = PR_Recv( (PRFileDesc*)_socket, + &_buf[_curSize], + _allocSize-_curSize, + 0, + _timeout ); + RA::Debug( LL_PER_PDU, + "RecvBuf::_getBytes: ", + "num of bytes read from the socket=%d", + num ); + /* + * in chunked mode, ending chunk contains a 0 to begin + * loop through to see if it contains just 0 (skip carriage returns + * endChunk indicates possible end chunk. + */ + if ((_chunkedMode == PR_TRUE) && (num < 10)) { + endChunk = PR_FALSE; + + for (i=0; i< num; i++) { + if (endChunk == PR_TRUE) { + if ((_buf[_curSize+i] == 13) || (_buf[_curSize+i] == 10)) + continue; + else { + endChunk = PR_FALSE; + break; // not an endChunk + } + } else { // endChunk==PR_FALSE + if (_buf[_curSize+i] == '0') { + RA::Debug( LL_PER_PDU, + "RecvBuf::_getBytes: ", + "may be chunked mode end chunk" ); + endChunk = PR_TRUE; + } else if ((_buf[_curSize+i] == 13) || (_buf[_curSize+i] == 10)) + continue; + else { + endChunk = PR_FALSE; + break; // not an endChunk + } + } + } // for + } + + if (num >0) + _curSize = _curSize+num; + + if (_chunkedMode == PR_FALSE) { + if (getAllContent()) { + RA::Debug( LL_PER_PDU, + "RecvBuf::_getBytes: ", + "Already got all the content, no need to call PR_Recv again." ); + break; + } + } + + if (endChunk == PR_TRUE) + break; + } while (num > 0); + + if (num <0) { + pec = PR_GetError(); + RA::Debug( LL_PER_PDU, + "RecvBuf::_getBytes: ", + "error in pr_recv, err=%d", + pec ); + } + + if ( _curSize <= 0 ) { + return PR_FALSE; + } + + _buf[_curSize] = '\0'; +//-- logger->Log( LOGLEVEL_FINEST, DEBUG_CLASS_NAME, +//-- "getBytes", + + _content = (char *) PR_Malloc(_curSize+1); + if (_content == NULL) { + return PR_FALSE; + } + memcpy((char*) _content, (const char *)_buf, _curSize+1); + _contentSize = _curSize +1; + + RA::Debug(LL_PER_PDU, "RecvBuf::_getBytes", + "buffer received with size %d follows:", _contentSize); + printBuf(_contentSize, _content); + + return PR_TRUE; +} + +int RecvBuf::getAllContent() { + //int result[10]; + //int j=0; + //int k=0; + int number = 0; + for (int i=0; i<_curSize; i++) { + if (_buf[i] == '\r') { + if (i < (_curSize-3)) { + if (_buf[i+1] == '\n' && _buf[i+2] == '\r' + && _buf[i+3] == '\n') { + // find content length +// strcasestr may not be supported by Solaris +// char *clen = strcasestr(_buf, "Content-length:"); + char *clen = strstr(_buf, "Content-Length:"); + if (clen != NULL) { + clen = &clen[16]; + number = atoi(clen); +/* + while (1) { + if ((number=Util::ascii2numeric(clen[j++])) >= 0) { + result[k++] = number; + } else { + break; + } + } + + number = 0; + for (int l=0; l<k; l++) + number = (int)(number + result[l]*(float)pow((float)10, (float)k-l-1)); +*/ + RA::Debug( LL_PER_PDU, + "RecvBuf::getAllContent: ", + "content length number=%d", + number ); + } + int remainingBytes = _curSize - (i+4); + RA::Debug( LL_PER_PDU, + "RecvBuf::getAllContent: ", + "remainingbytes=%d", + remainingBytes ); + if (remainingBytes == number) + return 1; + } + } + } + } + + return 0; +} + +void printBuf(int len, char* buf) { + RA::Debug(LL_PER_PDU, "response:printBuf", + "Buffer print begins"); + RA::Debug(LL_PER_PDU, "response::printBuf", + "%s", buf); + RA::Debug(LL_PER_PDU, "response:printBuf", + "Buffer print end"); + /* + int times = len/256; + if (len%256) + times++; + RA::Debug("response:printBuf", + "%d times", times); + RA::Debug("response:printBuf", + "attempting to print the whole buffer:"); + + int i; + + for (i = 0; i< times; i++) { + char *temp; + temp = PL_strdup((char *)buf+i*256); + RA::Debug("response:printBuf", + "%s", temp); + } + */ +} + +/** + * gets the next char from the buffer. If all the data in the buffer is read, + * read a chunk to the buffer + * @returns - the next char from the data + */ +char RecvBuf::_getChar() { + if (_curPos >= _curSize) { + if (!_getBytes(_allocSize)) { + /* bugscape #55624: Solaris RA exited + with a signal ABRT if we raised exception + without handling it */ + return -1; + /* throw RecvBuf::EndOfFile(); */ + } + } + + return _buf[_curPos++]; +} + + +/** + * gets the next char from the buffer. If all the data in the buffer is read , + * read a chunk to the buffer + * @returns - the next char from the data + */ +char RecvBuf::getChar() { + if (!_chunkedMode) + return _getChar(); + + else + { + if (_currentChunkSize == 0) + { + // read the chunk header + char ch, chunkStr[20]; + int index = 0; + + while (!isspace(ch = _getChar()) ) + chunkStr[index++] = ch; + chunkStr[index] = '\0'; + + sscanf((char *)chunkStr, "%x", (unsigned int *)(&_currentChunkSize)); + + if (ch != '\n') + { + char ch2 = _getChar(); + if (ch != '\r' || ch2 != '\n') + { + printf( "did not find CRLF after chunk"); + } + } + + if (_currentChunkSize == 0) + return -1; + + _currentChunkBytesRead = 1; + return _buf[_curPos++]; + } + else + if (_currentChunkBytesRead < _currentChunkSize) + { + // read a byte from the chunk + _currentChunkBytesRead++; + return _getChar(); + } + else + { + // read the chunk trailer + char ch1 = _getChar(); + char ch2 = _getChar(); + if (ch1 != '\r' || ch2 != '\n') + { + printf( "did not find CRLF after chunk"); + }; + _currentChunkSize = _currentChunkBytesRead = 0; + return getChar(); + }; + }; + +} + +char *RecvBuf::get_content() { + return _content; +} + +int RecvBuf::get_contentSize() { + return _contentSize; +} + +/** + * Decrements the pointer to the internal buffer so that the next read would + * retrieve the last data again + */ +void RecvBuf::putBack() { + if (_curPos > 0) { + _curPos--; + if (_chunkedMode) { + _currentChunkBytesRead--; + } + } +} + +/** + * Sets the chunked mode for reading data + * Not used now.. + */ +void RecvBuf::setChunkedMode() { + _chunkedMode = PR_TRUE; + _currentChunkSize = _currentChunkBytesRead = 0; +} + +/** + * Gets the timeout in seconds for reading + * + * @return The timeout in seconds for reading + */ +int RecvBuf::getTimeout() { + return _timeout / PR_TicksPerSecond(); +} + + +Response::Response(const PRFileDesc *sock, NetRequest *request) { + _socket = sock; + _request = request; +} + +/** + * Constructor + */ + +PSHttpResponse::PSHttpResponse( const PRFileDesc *sock, + PSHttpRequest *request, + int timeout , PRBool expectChunked): + Response(sock, request) { + _request = request; + _proto = HTTPNA; + _protocol = NULL; + retcode =0 ; + _statusNum = NULL; + _statusString = NULL; + _keepAlive = -1; + _connectionClosed = 0; + _bodyLength = -1; + _content = NULL; + + _headers = new StringKeyCache("response",10*60); + _expectChunked = expectChunked; + _chunkedResponse = PR_FALSE; + _timeout = timeout; +} + +PSHttpResponse::~PSHttpResponse() { + if( _protocol != NULL ) { + PL_strfree( _protocol ); + _protocol = NULL; + } + if( _statusString != NULL ) { + PL_strfree( _statusString ); + _statusString = NULL; + } + if( _statusNum != NULL ) { + PL_strfree( _statusNum ); + _statusNum = NULL; + } + if (_headers) { + Iterator* iterator = _headers->GetKeyIterator(); + while ( iterator->HasMore() ) { + const char* name = (const char*)iterator->Next(); + CacheEntry* entry = _headers->Remove( name ); + if ( entry ) { + char* value = (char*)entry->GetData(); + if( value != NULL ) { + PL_strfree( value ); + value = NULL; + } + if( entry != NULL ) { + delete entry; + entry = NULL; + } + } + } + if( iterator != NULL ) { + delete iterator; + iterator = NULL; + } + if( _headers != NULL ) { + delete _headers; + _headers = NULL; + } + } + _socket = 0; +} + +long PSHttpResponse::getStatus() { + return _statusNum ? atoi(_statusNum) : 0; +} + +int PSHttpResponse::getReturnCode() { + return retcode; +} + +char * PSHttpResponse::getStatusString() { + return _statusString?_statusString:(char*)""; +} + +HttpProtocol PSHttpResponse::getProtocol() { + // first check the response protocol + if (_proto == HTTPNA) { + if (_protocol) { + int major, minor; + + sscanf(_protocol, "HTTP/%d.%d", &major, &minor); + + switch(major) { + case 1: + switch(minor) { + case 0: + _proto = HTTP10; + break; + case 1: + _proto = HTTP11; + break; + } + break; + } + } else { + _proto = HTTP09; + } + } + + if (_proto == HTTP11) { + // A 1.1 compliant server response shows the protocol as HTTP/1.1 even + // for a HTTP/1.0 request, but it promises to only use HTTP/1.0 syntax. + if (_request->getProtocol() == HTTP10) { + _proto = HTTP10; + } + } + + return _proto; +}; + +char * PSHttpResponse::getHeader(const char *name) { + CacheEntry *entry = _headers->Get(name); + return entry ? (char *)entry->GetData() : NULL; +} + +int PSHttpResponse::getHeaders(char ***keys) { + + return _headers->GetKeys( keys ); + +} + +long PSHttpResponse::getBodyLength() { + return _bodyLength; +} + +char * PSHttpResponse::getContent() { + return _content; +} + +void PSHttpResponse::freeContent() { + if( _content != NULL ) { + PR_Free( _content ); + _content = NULL; + } +} + +int PSHttpResponse::getContentSize() { + + return _contentSize; +} + +char *PSHttpResponse::toString() { + char *resp = (char *)""; + char **keys; + char *headerBuf = NULL; + int nHeaders = getHeaders( &keys ); + if ( nHeaders > 0 ) { + char **values = new char*[nHeaders]; + int len = 0; + int *keyLengths = new int[nHeaders]; + int *valueLengths = new int[nHeaders]; + int i; + for( i = 0; i < nHeaders; i++ ) { + keyLengths[i] = strlen( keys[i] ); + len += keyLengths[i] + 1; + values[i] = getHeader(keys[i]); + valueLengths[i] = strlen( values[i] ); + len += valueLengths[i] + 1; + } + headerBuf = new char[len + nHeaders * 2]; + char *p = headerBuf; + for( i = 0; i < nHeaders; i++ ) { + strcpy( p, keys[i] ); + p += keyLengths[i]; + *p++ = ':'; + strcpy( p, values[i] ); + p += valueLengths[i]; + *p++ = ','; + } + *p = 0; + for( i = 0; i < nHeaders; i++ ) { + if( keys[i] != NULL ) { + delete [] keys[i]; + keys[i] = NULL; + } + } + if( keys != NULL ) { + delete [] keys; + keys = NULL; + } + if( values != NULL ) { + delete [] values; + values = NULL; + } + if( keyLengths != NULL ) { + delete [] keyLengths; + keyLengths = NULL; + } + if( valueLengths != NULL ) { + delete [] valueLengths; + valueLengths = NULL; + } + } + + char *s = NULL; + if ( headerBuf ) { + s = PR_smprintf( "PSHttpResponse [%s\nbody bytes:%d]", + headerBuf, _bodyLength ); + } else { + s = PR_smprintf( "PSHttpResponse [body bytes:%d]", _bodyLength ); + } + resp = new char[strlen(s) + 1]; + strcpy( resp, s ); + if( s != NULL ) { + PR_smprintf_free( s ); + s = NULL; + } + return resp; +} + +PRBool PSHttpResponse::checkKeepAlive() { + HttpProtocol proto; + const char *connectionHeader; +//-- static const char *DEBUG_METHOD_NAME = "checkKeepAlive"; +//-- DebugLogger *logger = DebugLogger::GetDebugLogger( DEBUG_MODULE ); + + if (_keepAlive < 0) { + proto = getProtocol(); + if (proto == HTTP11) { + // default is connection: keep-alive + _keepAlive = 1; + } else { + // default is connection: close + // _keepAlive = 0; + //CMS needs keepalive with HTTP10 (so no chunked encoding) + _keepAlive=1; + } + + connectionHeader = _request->getHeader("connection"); + if (connectionHeader) { + if (!PL_strcasecmp(connectionHeader, "keep-alive")) { + _keepAlive = 1; + } else if (!PL_strcasecmp(connectionHeader, "close")) { + _keepAlive = 0; + } else { +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpResponse::checkKeepAlive: ", + "Unknown connection header" ); + } + } + } + + return (_keepAlive == 0?PR_FALSE:PR_TRUE); +} + +PRBool PSHttpResponse::checkConnection() { + // return true if the connection is OPEN + return (_connectionClosed == 0?PR_TRUE:PR_FALSE); +} + + +int PSHttpResponse::_verifyStandardBody(RecvBuf &buf, + int expectedBytes, + PRBool check) { + int bytesRead = 0; + int curPos = 0; + char ch; +//-- static const char *DEBUG_METHOD_NAME = "_verifyStandardBody"; +//-- DebugLogger *logger = DebugLogger::GetDebugLogger( DEBUG_MODULE ); + + while(expectedBytes > 0 ) { + ch = buf.getChar(); + if (ch < 0 ) { + break; + } + // if check is true, we think we know what the content looks like + if ( check ) { + if (ch != (char) curPos%256) { +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpResponse::_verifyStandardBody: ", + "Response data corrupt at byte %d (%d, %d)", + curPos, + ch, + ( curPos % 256 ) ); + check = PR_FALSE; + break; + } + curPos++; + } + + bytesRead++; + + if (expectedBytes > 0) { + expectedBytes--; + } + } + + return bytesRead; +} + + +PRBool PSHttpResponse::_handleBody( RecvBuf &buf ) { + char *clHeader; // content length header + char *teHeader; // transfer-encoding header + int expected_cl=-1; // expected content length +//-- static const char *DEBUG_METHOD_NAME = "_handleBody"; +//-- DebugLogger *logger = DebugLogger::GetDebugLogger( DEBUG_MODULE ); + + teHeader = getHeader("transfer-encoding"); + if (teHeader && !PL_strcasecmp(teHeader, "chunked")) { + _chunkedResponse = PR_TRUE; + buf.setChunkedMode(); + } else { + _chunkedResponse = PR_FALSE; + clHeader = getHeader("Content-length"); + if (clHeader) { + expected_cl = atoi(clHeader); + } + } + + if (_request->getExpectStandardBody()) { + _bodyLength = _verifyStandardBody(buf, expected_cl, PR_TRUE); + + } else { + _bodyLength = _verifyStandardBody(buf, expected_cl, PR_FALSE); + } + + if (expected_cl >= 0) { + if (_bodyLength != expected_cl) { +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpResponse::_handleBody: ", + "Content length was incorrect (%d/%d bytes)", + _bodyLength, + expected_cl ); + } + } + + return PR_TRUE; +} + +/** + * Reads until the first space character + * + * @param buf Receive buffer to read from + * @param headerBuf Array to read header into + * @param len Size of headerBuf + * @return Number of characters read, or -1 if too many + */ +static int readHeader( RecvBuf& buf, char* headerBuf, int len ) { + int index = 0; + + do { + char ch = buf.getChar(); + + if ( ch != -1 && !isspace(ch) ) { + headerBuf[index++] = ch; + if ( index >= (len-1) ) { + return -1; + } + } else { + headerBuf[index] = '\0'; + break; + } + } while( true ); + // RA::Debug( LL_PER_PDU, + // "readHeader: ", + // "headerBuf = %s", + // headerBuf ); + + return index; +} + + +PRBool PSHttpResponse::processResponse() { + RecvBuf buf( _socket, 8192, _timeout ); + + if (_expectChunked) { + buf.setChunkedMode(); + } + +//-- static const char *DEBUG_METHOD_NAME = "processResponse"; +//-- DebugLogger *logger = DebugLogger::GetDebugLogger( DEBUG_MODULE ); + RA::Debug( LL_PER_PDU, + "PSHttpResponse::processResponse: ", + "Entered processResponse()" ); + + try { + char tmp[2048]; + int tmpLen = sizeof(tmp); + + // Get protocol string + int nRead = readHeader( buf, tmp, tmpLen ); + + if ( nRead < 0 ) { +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpResponse::processResponse: ", + "Returned more than expected bytes %d " + "in protocol header", + sizeof( tmp ) ); + return PR_FALSE; + } + + _protocol = PL_strdup(tmp); +//-- logger->Log( LOGLEVEL_FINER, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpResponse::processResponse: ", + "Protocol header: %s", + _protocol ); + + // Get status num + nRead = readHeader( buf, tmp, tmpLen ); + if ( nRead < 0 ) { +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpResponse::processResponse: ", + "Returned more than expected bytes %d " + "in status header", + tmpLen ); + return PR_FALSE; + } + + _statusNum = PL_strdup( tmp ); + +//-- logger->Log( LOGLEVEL_FINER, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpResponse::processResponse: ", + "Status header: %s", + _statusNum ); + retcode = atoi( tmp ); + + // Get status string + int index = 0; + do { + char ch = buf.getChar(); + if ( ch != -1 && ch != '\r' ) { + tmp[index++] = ch; + if ( index >= (tmpLen-2) ) { + tmp[index] = 0; +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpResponse::processResponse: ", + "Returned more than expected bytes %d " + "in protocol header:\n%s", + tmpLen, + tmp ); + return PR_FALSE; + } + } else { + break; + } + } while (true); + tmp[index] = '\0'; + _statusString = PL_strdup( tmp ); + + // Skip CRLF + (void)buf.getChar(); + + // loop over response headers + index = 0; +#ifdef CHECK + PRBool doneParsing = PR_FALSE; + PRBool atEOL = PR_FALSE; + PRBool inName = PR_TRUE; + char name[2048]; + int nameLen = sizeof(name); + + while ( !doneParsing ) { + char value[2048]; + int valueLen = sizeof(value); + char ch = buf.getChar(); + + switch( ch ) { + case ':': + if ( inName ) { + name[index] = '\0'; + index = 0; + inName = PR_FALSE; + + nRead = readHeader( buf, value, valueLen ); + if ( nRead < 0 ) { +//-- logger->Log( LOGLEVEL_SEVERE, +//-- DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpResponse::processResponse: ", + "Name %s in header does not " + "have a value", + name ); + // return PR_FALSE; + } else { + value[index++] = ch; + if ( index >= (int)(sizeof(value) - 1 ) ) { +//-- logger->Log( LOGLEVEL_SEVERE, +//-- DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpResponse::processResponse: ", + "Name %s in header does not " + "have a value", + name ); + // return PR_FALSE; + } + } + break; + case '\r': + if ( inName && !atEOL ) { + name[index] = '\0'; +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpResponse::processResponse: ", + "Name %s in header does not " + "have a value", + name ); + // return PR_FALSE; + } + break; + case '\n': + if ( atEOL ) { + doneParsing = PR_TRUE; + break; + } + if ( inName ) { + name[index] = '\0'; +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpResponse::processResponse: ", + "Name %s in header does not " + "have a value", + name ); + // return PR_FALSE; + } + value[index] = '\0'; + index = 0; + inName = PR_TRUE; + _headers->Put(name, PL_strdup(value)); + atEOL = PR_TRUE; + break; + default: + atEOL = PR_FALSE; + if (inName) { + name[index++] = ch; + } else { + value[index++] = ch; + } + if ( inName && (index >= (nameLen-2)) ) { + name[index] = '\0'; +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpResponse::processResponse: ", + "Name %s in header exceeds the expected " + "%d max characters", + name, + nameLen ); + // return PR_FALSE; + } else if ( !inName && (index >= (valueLen-1)) ) { +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpResponse::processResponse: ", + "Name %s in header does not " + "have a value", + name ); + // return PR_FALSE; + } + break; + } + } + + } //while +#endif //CHECK + } catch ( RecvBuf::EndOfFile & ) { + if ( !_request->isHangupOk() ) { + + int errCode = PR_GetError(); + if ( PR_IO_TIMEOUT_ERROR == errCode ) { +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpResponse::processResponse: ", + "Timed out reading response (%d seconds)", + buf.getTimeout() ); + } else { +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpResponse::processResponse: ", + "Received unexpected end of file from server\n%s", + "XXX" ); + } + } + return PR_FALSE; + } + + // Read the body (HEAD requests don't have bodies) + // jpierre 1xx, 204 and 304 don't have bodies either + if ( PL_strcmp(_request->getMethod(), "HEAD") && + (!((retcode>=100) && (retcode<200))) && + (retcode!=204) && + (retcode!=304) ) { + if ( _handleBody(buf) == PR_FALSE ) { + return PR_FALSE; + } + } + + if ( checkConnection() && !checkKeepAlive() ) { + // if connection is still open, and we didn't expect a keepalive, + // read another byte to see if the connection has closed. + try { + char ch; + ch = buf.getChar(); + buf.putBack(); + // conflict! +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpResponse::processResponse: ", + "Connection kept alive when it shouldn't" ); + } catch (RecvBuf::EndOfFile &) { + _connectionClosed = 1; + } + } + + _checkResponseSanity(); + _content = (char *)buf.get_content(); + _contentSize = buf.get_contentSize(); + RA::Debug( LL_PER_PDU, + "PSHttpResponse::processResponse: ", + "processed Buffer contentSize=%d", + getContentSize() ); + if (_content != NULL) { + RA::Debug( LL_PER_PDU, + "PSHttpResponse::processResponse: ", + "processed Buffer content=%s", + _content ); + } + // char * yo = getContent(); + + return PR_TRUE; +} + +void PSHttpResponse::_checkResponseSanity() { + char *clHeader = getHeader("Content-length"); + char *teHeader = getHeader("Transfer-encoding"); +//-- static const char *DEBUG_METHOD_NAME = "checkResponseSanity"; +//-- DebugLogger *logger = DebugLogger::GetDebugLogger( DEBUG_MODULE ); + RA::Debug( LL_PER_PDU, + "PSHttpResponse::_checkResponseSanity: ", + "in _checkResponseSanity" ); + + /////////////////////////////////////////////////// + // Check items relevant to HTTP/1.0 and HTTP/1.1 // + /////////////////////////////////////////////////// + + // check for both content-length and chunked + if ( clHeader && teHeader ) { +//-- logger->Log( LOGLEVEL_FINER, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpResponse::_checkResponseSanity: ", + "Response contains both content-length and " + "transfer-encoding" ); + } + + // check for basic headers + if ( !getHeader("Date") ) { +//-- logger->Log( LOGLEVEL_WARNING, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpResponse::_checkResponseSanity: ", + "Response does not contain a date header" ); + } + if ( !getHeader("Server") ) { +//-- logger->Log( LOGLEVEL_WARNING, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpResponse::_checkResponseSanity: ", + "Response does not contain a server header" ); + } + + int expectedLength; + if ((expectedLength = _request->getExpectedResponseLength()) > 0) { + if (expectedLength != _bodyLength) { +//-- logger->Log( LOGLEVEL_INFO, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpResponse::_checkResponseSanity: ", + "Response body length does not match expected " + "response length (%d/%d)", + _bodyLength, + expectedLength ); + } + } + + /////////////////////////////////////// + // Check items relevant to HTTP/1.0 // + /////////////////////////////////////// + if ( getProtocol() == HTTP10 ) { + if ( _chunkedResponse ) { +//-- logger->Log( LOGLEVEL_INFO, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpResponse::_checkResponseSanity: ", + "Server sent a chunked HTTP/1.0 response" ); + } + } + + /////////////////////////////////////// + // Check items relevant to HTTP/1.1 // + /////////////////////////////////////// + if ( getProtocol() == HTTP11 ) { + if ( (!clHeader && !_chunkedResponse) && + (!((retcode>=100) && (retcode<200))) && + (retcode!=204) && + (retcode!=304) ) { +//-- logger->Log( LOGLEVEL_INFO, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "PSHttpResponse::_checkResponseSanity: ", + "Server responded with a HTTP/1.1 response without " + "content-length or chunked encoding" ); + } + } +} |