diff --git a/panda/src/downloader/Sources.pp b/panda/src/downloader/Sources.pp index 2ceae3055f..ea41b10480 100644 --- a/panda/src/downloader/Sources.pp +++ b/panda/src/downloader/Sources.pp @@ -6,8 +6,7 @@ #begin lib_target #define TARGET downloader - #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx \ - $[if $[HAVE_ZLIB], $[TARGET]_composite4.cxx] \ + #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx #define SOURCES \ config_downloader.h \ @@ -16,8 +15,10 @@ bioStreamPtr.I bioStreamPtr.h \ bioStream.I bioStream.h bioStreamBuf.h \ chunkedStream.I chunkedStream.h chunkedStreamBuf.h \ + decompressor.h \ documentSpec.I documentSpec.h \ downloadDb.I downloadDb.h \ + download_utils.h \ extractor.h \ httpAuthorization.I httpAuthorization.h \ httpBasicAuthorization.I httpBasicAuthorization.h \ @@ -30,10 +31,9 @@ identityStream.I identityStream.h identityStreamBuf.h \ multiplexStream.I multiplexStream.h \ multiplexStreamBuf.I multiplexStreamBuf.h \ + patcher.h patcher.I \ socketStream.h socketStream.I \ - urlSpec.I urlSpec.h \ - $[if $[HAVE_ZLIB], decompressor.h download_utils.h] \ - $[if $[HAVE_CRYPTO], patcher.cxx patcher.h patcher.I] + urlSpec.I urlSpec.h #define INCLUDED_SOURCES \ config_downloader.cxx \ @@ -42,8 +42,10 @@ bioStreamPtr.cxx \ bioStream.cxx bioStreamBuf.cxx \ chunkedStream.cxx chunkedStreamBuf.cxx \ + decompressor.cxx \ documentSpec.cxx \ downloadDb.cxx \ + download_utils.cxx \ extractor.cxx \ httpAuthorization.cxx \ httpBasicAuthorization.cxx \ @@ -55,9 +57,9 @@ httpEnum.cxx \ identityStream.cxx identityStreamBuf.cxx \ multiplexStream.cxx multiplexStreamBuf.cxx \ + patcher.cxx \ socketStream.cxx \ - urlSpec.cxx \ - $[if $[HAVE_ZLIB], decompressor.cxx download_utils.cxx] + urlSpec.cxx #define INSTALL_HEADERS \ asyncUtility.h asyncUtility.I \ diff --git a/panda/src/downloader/bioPtr.cxx b/panda/src/downloader/bioPtr.cxx index f6e08156dd..aba0bda6b9 100644 --- a/panda/src/downloader/bioPtr.cxx +++ b/panda/src/downloader/bioPtr.cxx @@ -17,6 +17,7 @@ //////////////////////////////////////////////////////////////////// #include "bioPtr.h" +#include "urlSpec.h" #ifdef HAVE_SSL diff --git a/panda/src/downloader/bioStreamPtr.cxx b/panda/src/downloader/bioStreamPtr.cxx index 19e31f47e7..9b7a8f5e28 100644 --- a/panda/src/downloader/bioStreamPtr.cxx +++ b/panda/src/downloader/bioStreamPtr.cxx @@ -16,7 +16,7 @@ // //////////////////////////////////////////////////////////////////// -#include "bioPtr.h" +#include "bioStreamPtr.h" #ifdef HAVE_SSL diff --git a/panda/src/downloader/config_downloader.cxx b/panda/src/downloader/config_downloader.cxx index 91ffb7fddc..bf5375bf8e 100644 --- a/panda/src/downloader/config_downloader.cxx +++ b/panda/src/downloader/config_downloader.cxx @@ -115,6 +115,17 @@ config_downloader.GetDouble("http-timeout", 20.0); const int http_max_connect_count = config_downloader.GetInt("http-max-connect-count", 10); +// These provide a default client certificate to offer up should an +// SSL server demand one. The files references a PEM-formatted file +// that includes a public and private key specification. A +// connection-specific certificate may also be specified at runtime on +// the HTTPClient object, but this will require having a different +// HTTPClient object for each differently-certificated connection. +const string http_client_certificate_filename = +config_downloader.GetString("http-client-certificate-filename", ""); +const string http_client_certificate_passphrase = +config_downloader.GetString("http-client-certificate-passphrase", ""); + ConfigureFn(config_downloader) { #ifdef HAVE_SSL HTTPChannel::init_type(); diff --git a/panda/src/downloader/config_downloader.h b/panda/src/downloader/config_downloader.h index 3591074730..18c0dcb791 100644 --- a/panda/src/downloader/config_downloader.h +++ b/panda/src/downloader/config_downloader.h @@ -51,5 +51,7 @@ extern const bool http_proxy_tunnel; extern const double connect_timeout; extern const double http_timeout; extern const int http_max_connect_count; +extern const string http_client_certificate_filename; +extern const string http_client_certificate_passphrase; #endif diff --git a/panda/src/downloader/decompressor.cxx b/panda/src/downloader/decompressor.cxx index d928744720..5f5617b845 100644 --- a/panda/src/downloader/decompressor.cxx +++ b/panda/src/downloader/decompressor.cxx @@ -16,13 +16,15 @@ // //////////////////////////////////////////////////////////////////// -// This file is compiled only if we have zlib installed. +#include "pandabase.h" + +#ifdef HAVE_ZLIB #include "config_downloader.h" #include "error_utils.h" #include "filename.h" -#include "buffer.h" +#include "ramfile.h" #include "zStream.h" #include "config_express.h" @@ -273,3 +275,5 @@ cleanup() { _decompress = NULL; } } + +#endif // HAVE_ZLIB diff --git a/panda/src/downloader/decompressor.h b/panda/src/downloader/decompressor.h index 0dd4744bca..8304e13f87 100644 --- a/panda/src/downloader/decompressor.h +++ b/panda/src/downloader/decompressor.h @@ -20,6 +20,9 @@ #define DECOMPRESSOR_H #include "pandabase.h" + +#ifdef HAVE_ZLIB + #include "filename.h" class Ramfile; @@ -58,4 +61,6 @@ private: #include "decompressor.I" +#endif // HAVE_ZLIB + #endif diff --git a/panda/src/downloader/downloadDb.cxx b/panda/src/downloader/downloadDb.cxx index fa0399ed76..683e5d9d5c 100644 --- a/panda/src/downloader/downloadDb.cxx +++ b/panda/src/downloader/downloadDb.cxx @@ -20,6 +20,7 @@ #include "downloadDb.h" #include "streamReader.h" #include "streamWriter.h" +#include "ramfile.h" #include @@ -481,11 +482,7 @@ write(ostream &out) const { << " phase: " << _phase << endl << " size: " << _size << endl << " status: " << _status << endl - << " hash: [" << _hash.get_value(0) - << " " << _hash.get_value(1) - << " " << _hash.get_value(2) - << " " << _hash.get_value(3) - << "]" << endl; + << " hash: " << _hash << endl; out << "--------------------------------------------------" << endl; pvector< PT(FileRecord) >::const_iterator i = _file_records.begin(); for(; i != _file_records.end(); ++i) { @@ -753,12 +750,7 @@ parse_mfr(const string &data) { mfr->_name = back_to_front_slash(mfr->_name); // Read the hash value - HashVal hash; - hash.set_value(0, di.get_uint32()); - hash.set_value(1, di.get_uint32()); - hash.set_value(2, di.get_uint32()); - hash.set_value(3, di.get_uint32()); - mfr->_hash = hash; + mfr->_hash.read_datagram(di); downloader_cat.debug() << "Parsed multifile record: " << mfr->_name << " phase: " << mfr->_phase @@ -812,7 +804,7 @@ read(StreamReader &sr, bool want_server_info) { // Read the header string header; header = sr.extract_bytes(_header_length); - if (header.size() != _header_length) { + if (header.size() != (size_t)_header_length) { downloader_cat.error() << "truncated db file" << endl; return false; } @@ -832,7 +824,7 @@ read(StreamReader &sr, bool want_server_info) { int mfr_header_length = sizeof(PN_int32); string mfr_header = sr.extract_bytes(mfr_header_length); - if (mfr_header.size() != mfr_header_length) { + if (mfr_header.size() != (size_t)mfr_header_length) { downloader_cat.error() << "invalid mfr header" << endl; return false; } @@ -845,7 +837,7 @@ read(StreamReader &sr, bool want_server_info) { // do not count the header length twice int read_length = (mfr_length - mfr_header_length); string mfr_record = sr.extract_bytes(read_length); - if (mfr_record.size() != read_length) { + if (mfr_record.size() != (size_t)read_length) { downloader_cat.error() << "invalid mfr record" << endl; return false; } @@ -864,7 +856,7 @@ read(StreamReader &sr, bool want_server_info) { // Read the header string fr_header = sr.extract_bytes(fr_header_length); - if (fr_header.size() != fr_header_length) { + if (fr_header.size() != (size_t)fr_header_length) { downloader_cat.error() << "invalid fr header" << endl; return false; } @@ -877,7 +869,7 @@ read(StreamReader &sr, bool want_server_info) { int read_length = (fr_length - fr_header_length); string fr_record = sr.extract_bytes(read_length); - if (fr_record.size() != read_length) { + if (fr_record.size() != (size_t)read_length) { downloader_cat.error() << "invalid fr record" << endl; return false; } @@ -914,7 +906,6 @@ write(StreamWriter &sw, bool want_server_info) { PN_int32 num_files; PN_int32 name_length; PN_int32 header_length; - HashVal hash; // Iterate over the multifiles writing them to the stream pvector< PT(MultifileRecord) >::const_iterator i = _mfile_records.begin(); @@ -949,11 +940,7 @@ write(StreamWriter &sw, bool want_server_info) { sw.add_int32(status); sw.add_int32(num_files); - hash = (*i)->_hash; - sw.add_uint32(hash.get_value(0)); - sw.add_uint32(hash.get_value(1)); - sw.add_uint32(hash.get_value(2)); - sw.add_uint32(hash.get_value(3)); + (*i)->_hash.write_stream(sw); // Only write out the file information if you are the server if (want_server_info) { @@ -1217,7 +1204,6 @@ write_version_map(StreamWriter &sw) { VersionMap::iterator vmi; VectorHash::iterator i; string name; - HashVal hash; sw.add_int32(_versions.size()); for (vmi = _versions.begin(); vmi != _versions.end(); ++vmi) { @@ -1230,12 +1216,7 @@ write_version_map(StreamWriter &sw) { sw.add_int32((*vmi).second.size()); for (i = (*vmi).second.begin(); i != (*vmi).second.end(); ++i) { // *i will point to a HashVal - hash = *i; - // Write out each uint separately - sw.add_uint32(hash.get_value(0)); - sw.add_uint32(hash.get_value(1)); - sw.add_uint32(hash.get_value(2)); - sw.add_uint32(hash.get_value(3)); + (*i).write_stream(sw); } } } @@ -1279,10 +1260,7 @@ read_version_map(StreamReader &sr) { for (int j = 0; j < length; j++) { HashVal hash; - hash.set_value(0, sr.get_uint32()); - hash.set_value(1, sr.get_uint32()); - hash.set_value(2, sr.get_uint32()); - hash.set_value(3, sr.get_uint32()); + hash.read_stream(sr); if (sr.get_istream()->fail()) { return false; } @@ -1306,7 +1284,7 @@ write_version_map(ostream &out) const { out << " " << (*vmi).first << endl; for (i = (*vmi).second.begin(); i != (*vmi).second.end(); ++i) { HashVal hash = *i; - out << " " << hash << endl; + out << " " << hash.as_dec() << endl; } } out << endl; diff --git a/panda/src/downloader/downloadDb.h b/panda/src/downloader/downloadDb.h index 270d3a4129..73bec77f8e 100644 --- a/panda/src/downloader/downloadDb.h +++ b/panda/src/downloader/downloadDb.h @@ -30,11 +30,11 @@ #include "pmap.h" #include "hashVal.h" -#include "buffer.h" class StreamReader; class StreamWriter; typedef float Phase; +class Ramfile; /* ////////////////////////////////////////////////// diff --git a/panda/src/downloader/download_utils.cxx b/panda/src/downloader/download_utils.cxx index 7a528a13d6..ec5a29e703 100644 --- a/panda/src/downloader/download_utils.cxx +++ b/panda/src/downloader/download_utils.cxx @@ -16,13 +16,15 @@ // //////////////////////////////////////////////////////////////////// -// This file is compiled only if we have zlib installed. +#include "pandabase.h" + +#ifdef HAVE_ZLIB #include "download_utils.h" #include "config_downloader.h" #include -ulong +unsigned long check_crc(Filename name) { ifstream read_stream; name.set_binary(); @@ -40,7 +42,7 @@ check_crc(Filename name) { read_stream.read(buffer, buffer_length); // Compute the crc - ulong crc = crc32(0L, Z_NULL, 0); + unsigned long crc = crc32(0L, Z_NULL, 0); crc = crc32(crc, (uchar *)buffer, buffer_length); delete buffer; @@ -48,7 +50,7 @@ check_crc(Filename name) { return crc; } -ulong +unsigned long check_adler(Filename name) { ifstream read_stream; name.set_binary(); @@ -66,10 +68,12 @@ check_adler(Filename name) { read_stream.read(buffer, buffer_length); // Compute the adler checksum - ulong adler = adler32(0L, Z_NULL, 0); + unsigned long adler = adler32(0L, Z_NULL, 0); adler = adler32(adler, (uchar *)buffer, buffer_length); delete buffer; return adler; } + +#endif // HAVE_ZLIB diff --git a/panda/src/downloader/download_utils.h b/panda/src/downloader/download_utils.h index 37ed03503e..938f254d7b 100644 --- a/panda/src/downloader/download_utils.h +++ b/panda/src/downloader/download_utils.h @@ -19,16 +19,20 @@ #ifndef DOWNLOAD_UTILS_H #define DOWNLOAD_UTILS_H -#include -#include -#include +#include "pandabase.h" + +#ifdef HAVE_ZLIB + +#include "filename.h" BEGIN_PUBLISH -EXPCL_PANDAEXPRESS ulong check_crc(Filename name); -EXPCL_PANDAEXPRESS ulong check_adler(Filename name); +EXPCL_PANDAEXPRESS unsigned long check_crc(Filename name); +EXPCL_PANDAEXPRESS unsigned long check_adler(Filename name); END_PUBLISH +#endif // HAVE_ZLIB + #endif diff --git a/panda/src/downloader/downloader_composite1.cxx b/panda/src/downloader/downloader_composite1.cxx index b77a6adf60..56ad61d79d 100644 --- a/panda/src/downloader/downloader_composite1.cxx +++ b/panda/src/downloader/downloader_composite1.cxx @@ -1,14 +1,13 @@ -#include "config_downloader.cxx" #include "asyncUtility.cxx" #include "bioPtr.cxx" -#include "bioStreamPtr.cxx" #include "bioStream.cxx" #include "bioStreamBuf.cxx" +#include "bioStreamPtr.cxx" #include "chunkedStream.cxx" #include "chunkedStreamBuf.cxx" -#include "downloadDb.cxx" -#include "extractor.cxx" -#include "httpDate.cxx" -#include "httpEntityTag.cxx" +#include "config_downloader.cxx" +#include "decompressor.cxx" #include "documentSpec.cxx" - +#include "downloadDb.cxx" +#include "download_utils.cxx" +#include "extractor.cxx" diff --git a/panda/src/downloader/downloader_composite2.cxx b/panda/src/downloader/downloader_composite2.cxx index 5a92b86729..74084f4a46 100644 --- a/panda/src/downloader/downloader_composite2.cxx +++ b/panda/src/downloader/downloader_composite2.cxx @@ -1,12 +1,15 @@ #include "httpAuthorization.cxx" #include "httpBasicAuthorization.cxx" -#include "httpClient.cxx" #include "httpChannel.cxx" +#include "httpClient.cxx" +#include "httpDate.cxx" #include "httpDigestAuthorization.cxx" +#include "httpEntityTag.cxx" #include "httpEnum.cxx" #include "identityStream.cxx" #include "identityStreamBuf.cxx" #include "multiplexStream.cxx" #include "multiplexStreamBuf.cxx" +#include "patcher.cxx" #include "socketStream.cxx" #include "urlSpec.cxx" diff --git a/panda/src/downloader/downloader_composite4.cxx b/panda/src/downloader/downloader_composite4.cxx deleted file mode 100644 index 82a2191e19..0000000000 --- a/panda/src/downloader/downloader_composite4.cxx +++ /dev/null @@ -1,3 +0,0 @@ -#include "download_utils.cxx" -#include "decompressor.cxx" - diff --git a/panda/src/downloader/httpChannel.cxx b/panda/src/downloader/httpChannel.cxx index 4adcc07990..5b829d5b2d 100644 --- a/panda/src/downloader/httpChannel.cxx +++ b/panda/src/downloader/httpChannel.cxx @@ -23,7 +23,7 @@ #include "identityStream.h" #include "config_downloader.h" #include "clockObject.h" -#include "buffer.h" // for Ramfile +#include "ramfile.h" #ifdef HAVE_SSL #ifdef REPORT_OPENSSL_ERRORS @@ -54,6 +54,7 @@ HTTPChannel(HTTPClient *client) : _max_updates_per_second = 1.0f / _seconds_per_update; _bytes_per_update = int(_max_bytes_per_second * _seconds_per_update); _nonblocking = false; + _want_ssl = false; _proxy_serves_document = false; _proxy_tunnel_now = false; @@ -646,6 +647,7 @@ downcase(const string &s) { return result; } + //////////////////////////////////////////////////////////////////// // Function: HTTPChannel::reached_done_state // Access: Private @@ -1191,6 +1193,24 @@ run_setup_ssl() { return false; } + // It would be nice to use something like SSL_set_client_cert_cb() + // here to set a callback to provide the certificate should it be + // requested, or even to potentially provide any of a number of + // certificates according to the server's CA presented, but that + // interface as provided by OpenSSL is broken since there's no way + // to pass additional data to the callback function (and hence no + // way to tie it back to the HTTPChannel object, other than by + // building a messy mapping of SSL pointers back to HTTPChannel + // pointers). + if (_client->load_client_certificate()) { + SSL_use_certificate(ssl, _client->_client_certificate_pub); + SSL_use_PrivateKey(ssl, _client->_client_certificate_priv); + if (!SSL_check_private_key(ssl)) { + downloader_cat.warning() + << "Client private key does not match public key!\n"; + } + } + if (downloader_cat.is_spam()) { downloader_cat.spam() << "SSL Ciphers available:\n"; diff --git a/panda/src/downloader/httpClient.I b/panda/src/downloader/httpClient.I index 7a57addd53..e8801a1a69 100644 --- a/panda/src/downloader/httpClient.I +++ b/panda/src/downloader/httpClient.I @@ -44,6 +44,52 @@ get_try_all_direct() const { return _try_all_direct; } +//////////////////////////////////////////////////////////////////// +// Function: HTTPClient::set_client_certificate_filename +// Access: Published +// Description: Sets the filename of the pem-formatted file that will +// be read for the client public and private keys if an +// SSL server requests a certificate. Either this or +// set_client_certificate_pem() may be used to specify a +// client certificate. +//////////////////////////////////////////////////////////////////// +INLINE void HTTPClient:: +set_client_certificate_filename(const Filename &filename) { + _client_certificate_filename = filename; + _client_certificate_pem = string(); + unload_client_certificate(); +} + +//////////////////////////////////////////////////////////////////// +// Function: HTTPClient::set_client_certificate_pem +// Access: Published +// Description: Sets the pem-formatted contents of the certificate +// that will be parsed for the client public and private +// keys if an SSL server requests a certificate. Either +// this or set_client_certificate_filename() may be used +// to specify a client certificate. +//////////////////////////////////////////////////////////////////// +INLINE void HTTPClient:: +set_client_certificate_pem(const string &pem) { + _client_certificate_pem = pem; + _client_certificate_filename = Filename(); + unload_client_certificate(); +} + +//////////////////////////////////////////////////////////////////// +// Function: HTTPClient::set_client_certificate_passphrase +// Access: Published +// Description: Sets the passphrase used to decrypt the private key +// in the certificate named by +// set_client_certificate_filename() or +// set_client_certificate_pem(). +//////////////////////////////////////////////////////////////////// +INLINE void HTTPClient:: +set_client_certificate_passphrase(const string &passphrase) { + _client_certificate_passphrase = passphrase; + unload_client_certificate(); +} + //////////////////////////////////////////////////////////////////// // Function: HTTPClient::set_http_version // Access: Published diff --git a/panda/src/downloader/httpClient.cxx b/panda/src/downloader/httpClient.cxx index 971039d602..c9839ac318 100644 --- a/panda/src/downloader/httpClient.cxx +++ b/panda/src/downloader/httpClient.cxx @@ -128,6 +128,13 @@ HTTPClient() { } } + _client_certificate_filename = http_client_certificate_filename; + _client_certificate_passphrase = http_client_certificate_passphrase; + + _client_certificate_loaded = false; + _client_certificate_pub = NULL; + _client_certificate_priv = NULL; + // The first time we create an HTTPClient, we must initialize the // OpenSSL library. if (!_ssl_initialized) { @@ -190,6 +197,8 @@ HTTPClient:: // Free all of the expected server definitions. clear_expected_servers(); + + unload_client_certificate(); } //////////////////////////////////////////////////////////////////// @@ -599,6 +608,93 @@ get_username(const string &server, const string &realm) const { return string(); } +//////////////////////////////////////////////////////////////////// +// Function: HTTPClient::load_client_certificate +// Access: Published +// Description: Attempts to load the certificate named by +// set_client_certificate_filename() immediately, and +// returns true if successful, false otherwise. +// +// Normally this need not be explicitly called, since it +// will be called automatically if the server requests a +// certificate, but it may be useful to determine ahead +// of time if the certificate can be loaded correctly. +//////////////////////////////////////////////////////////////////// +bool HTTPClient:: +load_client_certificate() { + if (!_client_certificate_loaded) { + _client_certificate_loaded = true; + + if (!_client_certificate_filename.empty()) { + _client_certificate_filename.set_text(); + + // First, read the complete file into memory. + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); + + if (!vfs->read_file(_client_certificate_filename, + _client_certificate_pem)) { + // Could not find or read file. + downloader_cat.warning() + << "Could not read " << _client_certificate_filename << ".\n"; + return false; + } + } + + // Create an in-memory BIO to read the "file" from the memory + // buffer, and call the low-level routines to read the + // keys from the BIO. + BIO *mbio = BIO_new_mem_buf((void *)_client_certificate_pem.data(), + _client_certificate_pem.length()); + + ERR_clear_error(); + _client_certificate_priv = + PEM_read_bio_PrivateKey(mbio, NULL, NULL, + (char *)_client_certificate_passphrase.c_str()); + + // Rewind the "file" to the beginning in order to read the public + // key (which might appear first in the file). + BIO_reset(mbio); + + ERR_clear_error(); + _client_certificate_pub = + PEM_read_bio_X509(mbio, NULL, NULL, NULL); + + BIO_free(mbio); + + + NotifySeverity sev = NS_debug; + string source = "memory"; + if (!_client_certificate_filename.empty()) { + // Only report status to "info" severity if we have read the + // certificate from a file. If it came from an in-memory image, + // a failure will presumably be handled by whoever set the + // image. + sev = NS_info; + source = _client_certificate_filename; + } + + if (_client_certificate_priv != (EVP_PKEY *)NULL && + _client_certificate_pub != (X509 *)NULL) { + downloader_cat.out(sev) + << "Read client certificate from " << source << "\n"; + + } else { + if (_client_certificate_priv == (EVP_PKEY *)NULL) { + downloader_cat.out(sev) + << "Could not read private key from " << source << "\n"; + } + + if (_client_certificate_pub == (X509 *)NULL) { + downloader_cat.out(sev) + << "Could not read public key from " << source << "\n"; + } + } + } + + return (_client_certificate_priv != (EVP_PKEY *)NULL && + _client_certificate_pub != (X509 *)NULL); +} + //////////////////////////////////////////////////////////////////// // Function: HTTPClient::get_http_version_string // Access: Published @@ -1108,6 +1204,28 @@ generate_auth(const URLSpec &url, bool is_proxy, const string &challenge) { return auth; } +//////////////////////////////////////////////////////////////////// +// Function: HTTPClient::unload_client_certificate +// Access: Private +// Description: Frees the resources allocated by a previous call to +// load_client_certificate(), and marks the certificate +// unloaded. +//////////////////////////////////////////////////////////////////// +void HTTPClient:: +unload_client_certificate() { + if (_client_certificate_priv != (EVP_PKEY *)NULL) { + EVP_PKEY_free(_client_certificate_priv); + _client_certificate_priv = NULL; + } + + if (_client_certificate_pub != (X509 *)NULL) { + X509_free(_client_certificate_pub); + _client_certificate_pub = NULL; + } + + _client_certificate_loaded = false; +} + //////////////////////////////////////////////////////////////////// // Function: HTTPClient::initialize_ssl // Access: Private, Static @@ -1209,6 +1327,14 @@ load_verify_locations(SSL_CTX *ctx, const Filename &ca_file) { << "Entry " << i << " is crl\n"; } + } else if (itmp->x_pkey) { + // X509_STORE_add_crl(store, itmp->x_pkey); + // count++; + if (downloader_cat.is_spam()) { + downloader_cat.spam() + << "Entry " << i << " is pkey\n"; + } + } else { if (downloader_cat.is_spam()) { downloader_cat.spam() diff --git a/panda/src/downloader/httpClient.h b/panda/src/downloader/httpClient.h index c1321079a4..da6b7746b6 100644 --- a/panda/src/downloader/httpClient.h +++ b/panda/src/downloader/httpClient.h @@ -82,6 +82,11 @@ PUBLISHED: void set_username(const string &server, const string &realm, const string &username); string get_username(const string &server, const string &realm) const; + INLINE void set_client_certificate_filename(const Filename &filename); + INLINE void set_client_certificate_pem(const string &pem); + INLINE void set_client_certificate_passphrase(const string &passphrase); + bool load_client_certificate(); + INLINE void set_http_version(HTTPEnum::HTTPVersion version); INLINE HTTPEnum::HTTPVersion get_http_version() const; string get_http_version_string() const; @@ -125,6 +130,8 @@ private: PT(HTTPAuthorization) generate_auth(const URLSpec &url, bool is_proxy, const string &challenge); + void unload_client_certificate(); + static void initialize_ssl(); static int load_verify_locations(SSL_CTX *ctx, const Filename &ca_file); @@ -158,12 +165,19 @@ private: typedef pmap Domains; Domains _proxy_domains, _www_domains; + Filename _client_certificate_filename; + string _client_certificate_pem; + string _client_certificate_passphrase; + // List of allowable SSL servers to connect to. If the list is // empty, any server is acceptable. typedef pvector ExpectedServers; ExpectedServers _expected_servers; SSL_CTX *_ssl_ctx; + bool _client_certificate_loaded; + X509 *_client_certificate_pub; + EVP_PKEY *_client_certificate_priv; static bool _ssl_initialized; static X509_STORE *_x509_store; diff --git a/panda/src/downloader/patcher.cxx b/panda/src/downloader/patcher.cxx index 5acd356e33..ae2d0e6b43 100644 --- a/panda/src/downloader/patcher.cxx +++ b/panda/src/downloader/patcher.cxx @@ -16,13 +16,13 @@ // //////////////////////////////////////////////////////////////////// -#include "config_downloader.h" -#include -#include "patcher.h" +#include "pandabase.h" -//////////////////////////////////////////////////////////////////// -// Defines -//////////////////////////////////////////////////////////////////// +#ifdef HAVE_SSL + +#include "config_downloader.h" +#include "patcher.h" +#include "filename.h" //////////////////////////////////////////////////////////////////// // Function: Patcher::Constructor @@ -88,3 +88,5 @@ int Patcher:: run(void) { return _patchfile->run(); } + +#endif // HAVE_SSL diff --git a/panda/src/downloader/patcher.h b/panda/src/downloader/patcher.h index c3058b9ebd..8f9cd0a2be 100644 --- a/panda/src/downloader/patcher.h +++ b/panda/src/downloader/patcher.h @@ -15,16 +15,17 @@ // panda3d@yahoogroups.com . // //////////////////////////////////////////////////////////////////// + #ifndef PATCHER_H #define PATCHER_H -// -//////////////////////////////////////////////////////////////////// -// Includes -//////////////////////////////////////////////////////////////////// -#include -#include -#include -#include + +#include "pandabase.h" + +#ifdef HAVE_SSL + +#include "filename.h" +#include "buffer.h" +#include "patchfile.h" //////////////////////////////////////////////////////////////////// // Class : Patcher @@ -49,4 +50,6 @@ private: #include "patcher.I" +#endif // HAVE_SSL + #endif diff --git a/panda/src/downloadertools/Sources.pp b/panda/src/downloadertools/Sources.pp index aec83d2644..8c847398b5 100644 --- a/panda/src/downloadertools/Sources.pp +++ b/panda/src/downloadertools/Sources.pp @@ -4,7 +4,7 @@ #begin bin_target #define TARGET apply_patch - #define BUILD_TARGET $[HAVE_CRYPTO] + #define BUILD_TARGET $[HAVE_SSL] #define SOURCES \ apply_patch.cxx @@ -15,7 +15,7 @@ #begin bin_target #define TARGET build_patch - #define BUILD_TARGET $[HAVE_CRYPTO] + #define BUILD_TARGET $[HAVE_SSL] #define SOURCES \ build_patch.cxx @@ -24,7 +24,7 @@ #begin bin_target #define TARGET show_ddb - #define BUILD_TARGET $[HAVE_CRYPTO] + #define BUILD_TARGET $[HAVE_SSL] #define SOURCES \ show_ddb.cxx @@ -53,7 +53,7 @@ #begin bin_target #define TARGET check_md5 - #define BUILD_TARGET $[HAVE_CRYPTO] + #define BUILD_TARGET $[HAVE_SSL] #define USE_PACKAGES $[USE_PACKAGES] crypto #define SOURCES \ diff --git a/panda/src/downloadertools/check_md5.cxx b/panda/src/downloadertools/check_md5.cxx index 7b4722b8cb..068d615c67 100644 --- a/panda/src/downloadertools/check_md5.cxx +++ b/panda/src/downloadertools/check_md5.cxx @@ -16,34 +16,121 @@ // //////////////////////////////////////////////////////////////////// -#include "crypto_utils.h" +#include "pandabase.h" + #include "hashVal.h" #include "filename.h" +#ifndef HAVE_GETOPT +#include +#else +#include +#endif + +void +usage() { + cerr << + "\n" + "Usage:\n\n" + "check_md5 [-d] [-i \"input string\"] [file1 file2 ...]\n" + "check_md5 -h\n\n"; +} + +void +help() { + usage(); + cerr << + "This program outputs the MD5 hash of one or more files (or of a string\n" + "passed on the command line with -i).\n\n" + + "An MD5 hash is a 128-bit value. The output is presented as a 32-digit\n" + "hexadecimal string by default, but with -d, it is presented as four\n" + "big-endian unsigned 32-bit decimal integers.\n\n"; +} + +void +output_hash(const string &filename, const HashVal &hash, bool output_decimal) { + if (!filename.empty()) { + cout << filename << " "; + } + if (output_decimal) { + hash.output_dec(cout); + } else { + hash.output_hex(cout); + } + cout << "\n"; +} + + int main(int argc, char *argv[]) { - const char *usagestr = "Usage: check_md5 "; - if (argc < 2) { - cerr << usagestr << endl; - return 1; + extern char *optarg; + extern int optind; + const char *optstr = "i:dh"; + + bool got_input_string = false; + string input_string; + bool output_decimal = false; + + int flag = getopt(argc, argv, optstr); + + while (flag != EOF) { + switch (flag) { + case 'i': + got_input_string = true; + input_string = optarg; + break; + + case 'd': + output_decimal = true; + break; + + case 'h': + help(); + exit(1); + + default: + exit(1); + } + flag = getopt(argc, argv, optstr); } - Filename source_file; - source_file = Filename::from_os_specific(argv[1]); + argc -= (optind-1); + argv += (optind-1); - if(!source_file.exists()) { - cerr << usagestr << endl; - cerr << source_file << " not found!\n"; - return 2; + if (argc < 2 && !got_input_string) { + usage(); + exit(1); } - HashVal hash; - md5_a_file(source_file, hash); + if (got_input_string) { + HashVal hash; + hash.hash_string(input_string); + output_hash("", hash, output_decimal); + } + + bool okflag = true; + + for (int i = 1; i < argc; i++) { + Filename source_file = Filename::from_os_specific(argv[i]); + + if (!source_file.exists()) { + cerr << source_file << " not found!\n"; + okflag = false; + } else { + HashVal hash; + if (!hash.hash_file(source_file)) { + cerr << "Unable to read " << source_file << "\n"; + okflag = false; + } else { + output_hash(source_file.get_basename(), hash, output_decimal); + } + } + } + + if (!okflag) { + exit(1); + } - // output base of Filename along w/md5 - cout << source_file.get_basename() << " "; - hash.output(cout); - cout << endl; - return 0; } diff --git a/panda/src/express/Sources.pp b/panda/src/express/Sources.pp index 229c6621c7..f447c8c3b7 100644 --- a/panda/src/express/Sources.pp +++ b/panda/src/express/Sources.pp @@ -4,7 +4,7 @@ #begin lib_target #define TARGET express - #define USE_PACKAGES nspr crypto net zlib + #define USE_PACKAGES nspr crypto net zlib ssl #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx @@ -41,10 +41,13 @@ namable.h nativeNumericData.I nativeNumericData.h \ numeric_types.h \ ordered_vector.h ordered_vector.I ordered_vector.T \ + patchfile.I patchfile.h \ pointerTo.I pointerTo.h \ pointerToArray.I pointerToArray.h \ profileTimer.I profileTimer.h \ - pta_uchar.h referenceCount.I referenceCount.h \ + pta_uchar.h \ + ramfile.I ramfile.h \ + referenceCount.I referenceCount.h \ register_type.I register_type.h \ reversedNumericData.I reversedNumericData.h \ selectThreadImpl.h \ @@ -69,10 +72,7 @@ virtualFileMountSystem.I virtualFileSimple.h virtualFileSimple.I \ virtualFileSystem.h virtualFileSystem.I \ windowsRegistry.h \ - zStream.I zStream.h zStreamBuf.h \ - $[if $[HAVE_CRYPTO], \ - crypto_utils.cxx crypto_utils.h patchfile.I \ - patchfile.cxx patchfile.h ] + zStream.I zStream.h zStreamBuf.h #define INCLUDED_SOURCES \ atomicAdjust.cxx atomicAdjustDummyImpl.cxx atomicAdjustNsprImpl.cxx \ @@ -90,8 +90,11 @@ namable.cxx \ nativeNumericData.cxx \ ordered_vector.cxx \ + patchfile.cxx \ profileTimer.cxx \ - pta_uchar.cxx referenceCount.cxx register_type.cxx \ + pta_uchar.cxx \ + ramfile.cxx \ + referenceCount.cxx register_type.cxx \ reversedNumericData.cxx \ streamReader.cxx streamWriter.cxx \ stringDecoder.cxx \ @@ -141,7 +144,9 @@ ordered_vector.h ordered_vector.I ordered_vector.T \ patchfile.I patchfile.h pointerTo.I pointerTo.h \ pointerToArray.I pointerToArray.h profileTimer.I \ - profileTimer.h pta_uchar.h referenceCount.I referenceCount.h \ + profileTimer.h pta_uchar.h \ + ramfile.I ramfile.h \ + referenceCount.I referenceCount.h \ register_type.I register_type.h \ reversedNumericData.I reversedNumericData.h \ selectThreadImpl.h \ diff --git a/panda/src/express/buffer.I b/panda/src/express/buffer.I index 444832b2ba..ca93c4518e 100644 --- a/panda/src/express/buffer.I +++ b/panda/src/express/buffer.I @@ -25,13 +25,3 @@ INLINE int Buffer:: get_length(void) const { return _length; } - -//////////////////////////////////////////////////////////////////// -// Function: Ramfile::constructor -// Access: Published -// Description: -//////////////////////////////////////////////////////////////////// -INLINE Ramfile:: -Ramfile(void) { - _pos = 0; -} diff --git a/panda/src/express/buffer.cxx b/panda/src/express/buffer.cxx index 7973362702..691de879eb 100644 --- a/panda/src/express/buffer.cxx +++ b/panda/src/express/buffer.cxx @@ -15,11 +15,8 @@ // panda3d@yahoogroups.com . // //////////////////////////////////////////////////////////////////// -#include "buffer.h" -//////////////////////////////////////////////////////////////////// -// Defines -//////////////////////////////////////////////////////////////////// +#include "buffer.h" //////////////////////////////////////////////////////////////////// // Function: Buffer::Constructor @@ -42,31 +39,3 @@ Buffer:: delete _buffer; } - -//////////////////////////////////////////////////////////////////// -// Function: Ramfile::readline -// Access: Published -// Description: Assumes the stream represents a text file, and -// extracts one line up to and including the trailing -// newline character. Returns empty string when the end -// of file is reached. -// -// The interface here is intentionally designed to be -// similar to that for Python's File.readline() -// function. -//////////////////////////////////////////////////////////////////// -string Ramfile:: -readline() { - size_t start = _pos; - while (_pos < _data.length() && _data[_pos] != '\n') { - ++_pos; - } - - if (_pos < _data.length() && _data[_pos] == '\n') { - // Include the newline character also. - ++_pos; - } - - return _data.substr(start, _pos - start); -} - diff --git a/panda/src/express/buffer.h b/panda/src/express/buffer.h index e743d8ca4e..c80733a9a4 100644 --- a/panda/src/express/buffer.h +++ b/panda/src/express/buffer.h @@ -15,13 +15,11 @@ // panda3d@yahoogroups.com . // //////////////////////////////////////////////////////////////////// + #ifndef BUFFER_H #define BUFFER_H -// -//////////////////////////////////////////////////////////////////// -// Includes -//////////////////////////////////////////////////////////////////// -#include + +#include "pandabase.h" #include "typedef.h" #include "referenceCount.h" @@ -46,21 +44,6 @@ private: int _length; }; -//////////////////////////////////////////////////////////////////// -// Class : Ramfile -// Description : -//////////////////////////////////////////////////////////////////// -class EXPCL_PANDAEXPRESS Ramfile { -PUBLISHED: - INLINE Ramfile(); - - string readline(); - -public: - size_t _pos; - string _data; -}; - #include "buffer.I" #endif diff --git a/panda/src/express/crypto_utils.cxx b/panda/src/express/crypto_utils.cxx deleted file mode 100644 index 0c4e931cd5..0000000000 --- a/panda/src/express/crypto_utils.cxx +++ /dev/null @@ -1,134 +0,0 @@ -// Filename: crypto_utils.cxx -// Created by: drose (07Nov00) -// -//////////////////////////////////////////////////////////////////// -// -// PANDA 3D SOFTWARE -// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved -// -// All use of this software is subject to the terms of the Panda 3d -// Software license. You should have received a copy of this license -// along with this source code; you will also find a current copy of -// the license at http://www.panda3d.org/license.txt . -// -// To contact the maintainers of this program write to -// panda3d@yahoogroups.com . -// -//////////////////////////////////////////////////////////////////// - -// This file is compiled only if we have crypto++ installed. - - -#include "hashVal.h" -#include "config_express.h" -#include "crypto_utils.h" - -#include -#include -#include - -// md5 defines this, but we'd rather do without it, because it -// conflicts with a similar macro defined in NSPR. -#undef IS_LITTLE_ENDIAN - -USING_NAMESPACE(CryptoPP); -USING_NAMESPACE(std); - -static uint -read32(istream& is) { - unsigned int ret = 0x0; - for (int i=0; i<8; ++i) { - char b; - int n = 0; - is >> b; - switch (b) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - n = b - '0'; - break; - case 'A': - case 'B': - case 'C': - case 'D': - case 'E': - case 'F': - n = b - 'A' + 10; - break; - case 'a': - case 'b': - case 'c': - case 'd': - case 'e': - case 'f': - n = b - 'a' + 10; - break; - default: - cerr << "illegal hex nibble '" << b << "'" << endl; - } - ret = (ret << 4) | (n & 0x0f); - } - return ret; -} - -void -md5_a_file(const Filename &name, HashVal &ret) { - ostringstream os; - MD5 md5; - - string fs = name.to_os_specific(); - - try { - FileSource f(fs.c_str(), true, - new HashFilter(md5, new HexEncoder(new FileSink(os)))); - } catch (...) { - express_cat.warning() - << "Unable to read " << name << " to compute md5 hash.\n"; - if (!name.exists()) { - express_cat.warning() - << "(file does not exist.)\n"; - } else { - express_cat.warning() - << "(file exists but cannot be read.)\n"; - } - ret.hv[0] = 0; - ret.hv[1] = 0; - ret.hv[2] = 0; - ret.hv[3] = 0; - return; - } - - istringstream is(os.str()); - - ret.hv[0] = read32(is); - ret.hv[1] = read32(is); - ret.hv[2] = read32(is); - ret.hv[3] = read32(is); -} - -void -md5_a_buffer(const unsigned char* buf, unsigned long len, HashVal& ret) { - MD5 md5; - - HashFilter hash(md5); - hash.Put((byte*)buf, len); - hash.Close(); - - unsigned char* outb; - unsigned long outl = hash.MaxRetrieveable(); - outb = new uchar[outl]; - hash.Get((byte*)outb, outl); - ret.hv[0] = (outb[0] << 24) | (outb[1] << 16) | (outb[2] << 8) | outb[3]; - ret.hv[1] = (outb[4] << 24) | (outb[5] << 16) | (outb[6] << 8) | outb[7]; - ret.hv[2] = (outb[8] << 24) | (outb[9] << 16) | (outb[10] << 8) | outb[11]; - ret.hv[3] = (outb[12] << 24) | (outb[13] << 16) | (outb[14] << 8) | outb[15]; - delete[] outb; -} - diff --git a/panda/src/express/express_composite1.cxx b/panda/src/express/express_composite1.cxx index 967a0a0e19..0a78a523f8 100644 --- a/panda/src/express/express_composite1.cxx +++ b/panda/src/express/express_composite1.cxx @@ -24,13 +24,9 @@ #include "memoryUsagePointerCounts.cxx" #include "memoryUsagePointers.cxx" #include "multifile.cxx" -#include "pmutex.cxx" #include "mutexHolder.cxx" #include "mutexDummyImpl.cxx" #include "mutexNsprImpl.cxx" #include "namable.cxx" #include "nativeNumericData.cxx" #include "ordered_vector.cxx" -#include "profileTimer.cxx" -#include "pta_uchar.cxx" -#include "referenceCount.cxx" diff --git a/panda/src/express/express_composite2.cxx b/panda/src/express/express_composite2.cxx index 150c337ada..eb66eba9fe 100644 --- a/panda/src/express/express_composite2.cxx +++ b/panda/src/express/express_composite2.cxx @@ -1,3 +1,9 @@ +#include "patchfile.cxx" +#include "pmutex.cxx" +#include "profileTimer.cxx" +#include "pta_uchar.cxx" +#include "ramfile.cxx" +#include "referenceCount.cxx" #include "register_type.cxx" #include "reversedNumericData.cxx" #include "streamReader.cxx" diff --git a/panda/src/express/hashVal.I b/panda/src/express/hashVal.I index aa87a5daa7..bfd3e94f5a 100644 --- a/panda/src/express/hashVal.I +++ b/panda/src/express/hashVal.I @@ -24,7 +24,7 @@ //////////////////////////////////////////////////////////////////// INLINE HashVal:: HashVal() { - hv[0] = hv[1] = hv[2] = hv[3] = 0; + _hv[0] = _hv[1] = _hv[2] = _hv[3] = 0; } //////////////////////////////////////////////////////////////////// @@ -34,10 +34,23 @@ HashVal() { //////////////////////////////////////////////////////////////////// INLINE HashVal:: HashVal(const HashVal ©) { - hv[0] = copy.hv[0]; - hv[1] = copy.hv[1]; - hv[2] = copy.hv[2]; - hv[3] = copy.hv[3]; + _hv[0] = copy._hv[0]; + _hv[1] = copy._hv[1]; + _hv[2] = copy._hv[2]; + _hv[3] = copy._hv[3]; +} + +//////////////////////////////////////////////////////////////////// +// Function: HashVal::Copy Assignment Operator +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void HashVal:: +operator = (const HashVal ©) { + _hv[0] = copy._hv[0]; + _hv[1] = copy._hv[1]; + _hv[2] = copy._hv[2]; + _hv[3] = copy._hv[3]; } //////////////////////////////////////////////////////////////////// @@ -47,10 +60,10 @@ HashVal(const HashVal ©) { //////////////////////////////////////////////////////////////////// INLINE bool HashVal:: operator == (const HashVal &other) const { - return (hv[0] == other.hv[0] && - hv[1] == other.hv[1] && - hv[2] == other.hv[2] && - hv[3] == other.hv[3]); + return (_hv[0] == other._hv[0] && + _hv[1] == other._hv[1] && + _hv[2] == other._hv[2] && + _hv[3] == other._hv[3]); } //////////////////////////////////////////////////////////////////// @@ -64,36 +77,189 @@ operator != (const HashVal &other) const { } //////////////////////////////////////////////////////////////////// -// Function: HashVal::get_value +// Function: HashVal::operator < // Access: Published -// Description: Returns the integer value of the indicated component. +// Description: //////////////////////////////////////////////////////////////////// -INLINE uint HashVal:: -get_value(int val) const { - nassertr(val >= 0 && val < 4, 0); - return hv[val]; +INLINE bool HashVal:: +operator < (const HashVal &other) const { + return compare_to(other) < 0; } +//////////////////////////////////////////////////////////////////// +// Function: HashVal::compare_to +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int HashVal:: +compare_to(const HashVal &other) const { + if (_hv[0] != other._hv[0]) { + return (int)_hv[0] - (int)other._hv[0]; + } + if (_hv[1] != other._hv[1]) { + return (int)_hv[1] - (int)other._hv[1]; + } + if (_hv[2] != other._hv[2]) { + return (int)_hv[2] - (int)other._hv[2]; + } + return (int)_hv[3] - (int)other._hv[3]; +} //////////////////////////////////////////////////////////////////// -// Function: HashVal::set_value +// Function: HashVal::merge_with // Access: Published -// Description: Sets the hash value at index val +// Description: Generates a new HashVal representing the xor of this +// one and the other one. //////////////////////////////////////////////////////////////////// INLINE void HashVal:: -set_value(int val, uint hashval) { - nassertv(val >= 0 && val < 4); - hv[val] = hashval; +merge_with(const HashVal &other) { + _hv[0] ^= other._hv[0]; + _hv[1] ^= other._hv[1]; + _hv[2] ^= other._hv[2]; + _hv[3] ^= other._hv[3]; } +//////////////////////////////////////////////////////////////////// +// Function: HashVal::output_dec +// Access: Published +// Description: Outputs the HashVal as four unsigned decimal +// integers. +//////////////////////////////////////////////////////////////////// +INLINE void HashVal:: +output_dec(ostream &out) const { + out << _hv[0] << " " << _hv[1] << " " << _hv[2] << " " << _hv[3]; +} + +//////////////////////////////////////////////////////////////////// +// Function: HashVal::input +// Access: Published +// Description: Inputs the HashVal as four unsigned decimal integers. +//////////////////////////////////////////////////////////////////// +INLINE void HashVal:: +input_dec(istream &in) { + in >> _hv[0] >> _hv[1] >> _hv[2] >> _hv[3]; +} //////////////////////////////////////////////////////////////////// // Function: HashVal::output // Access: Published -// Description: The output method does not itself output enclosing -// brackets, but the ostream operator << does. +// Description: //////////////////////////////////////////////////////////////////// INLINE void HashVal:: output(ostream &out) const { - out << hv[0] << " " << hv[1] << " " << hv[2] << " " << hv[3]; + output_hex(out); +} + +//////////////////////////////////////////////////////////////////// +// Function: HashVal::write_datagram +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void HashVal:: +write_datagram(Datagram &destination) const { + destination.add_uint32(_hv[0]); + destination.add_uint32(_hv[1]); + destination.add_uint32(_hv[2]); + destination.add_uint32(_hv[3]); +} + +//////////////////////////////////////////////////////////////////// +// Function: HashVal::read_datagram +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void HashVal:: +read_datagram(DatagramIterator &source) { + _hv[0] = source.get_uint32(); + _hv[1] = source.get_uint32(); + _hv[2] = source.get_uint32(); + _hv[3] = source.get_uint32(); +} + +//////////////////////////////////////////////////////////////////// +// Function: HashVal::write_stream +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void HashVal:: +write_stream(StreamWriter &destination) const { + destination.add_uint32(_hv[0]); + destination.add_uint32(_hv[1]); + destination.add_uint32(_hv[2]); + destination.add_uint32(_hv[3]); +} + +//////////////////////////////////////////////////////////////////// +// Function: HashVal::read_stream +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void HashVal:: +read_stream(StreamReader &source) { + _hv[0] = source.get_uint32(); + _hv[1] = source.get_uint32(); + _hv[2] = source.get_uint32(); + _hv[3] = source.get_uint32(); +} + +#ifdef HAVE_SSL +//////////////////////////////////////////////////////////////////// +// Function: HashVal::hash_ramfile +// Access: Published +// Description: Generates the hash value by hashing the indicated +// data. This method is only defined if we have the +// OpenSSL library (which provides md5 functionality) +// available. +//////////////////////////////////////////////////////////////////// +INLINE void HashVal:: +hash_ramfile(const Ramfile &ramfile) { + return hash_buffer(ramfile._data.data(), ramfile._data.length()); +} + +//////////////////////////////////////////////////////////////////// +// Function: HashVal::hash_string +// Access: Published +// Description: Generates the hash value by hashing the indicated +// data. This method is only defined if we have the +// OpenSSL library (which provides md5 functionality) +// available. +//////////////////////////////////////////////////////////////////// +INLINE void HashVal:: +hash_string(const string &data) { + return hash_buffer(data.data(), data.length()); +} +#endif // HAVE_SSL + +//////////////////////////////////////////////////////////////////// +// Function: HashVal::tohex +// Access: Private, Static +// Description: Converts a single nibble to a hex digit. +//////////////////////////////////////////////////////////////////// +INLINE char HashVal:: +tohex(unsigned int nibble) { + nibble &= 0xf; + if (nibble < 10) { + return nibble + '0'; + } + return nibble - 10 + 'a'; +} + +//////////////////////////////////////////////////////////////////// +// Function: HashVal::fromhex +// Access: Private, Static +// Description: Converts a single hex digit to a numerical value. +//////////////////////////////////////////////////////////////////// +INLINE unsigned int HashVal:: +fromhex(char digit) { + if (isdigit(digit)) { + return digit - '0'; + } else { + return tolower(digit) - 'a' + 10; + } +} + + +INLINE ostream &operator << (ostream &out, const HashVal &hv) { + hv.output(out); + return out; } diff --git a/panda/src/express/hashVal.cxx b/panda/src/express/hashVal.cxx index bc1f9e5f4b..e8a018f026 100644 --- a/panda/src/express/hashVal.cxx +++ b/panda/src/express/hashVal.cxx @@ -16,17 +16,233 @@ // //////////////////////////////////////////////////////////////////// - #include "hashVal.h" +#include "ctype.h" +#include "virtualFileSystem.h" + +#ifdef HAVE_SSL +#include +#endif // HAVE_SSL + //////////////////////////////////////////////////////////////////// -// Function: HashVal::as_string +// Function: HashVal::output_hex // Access: Published -// Description: Returns the HashVal as a string with four numbers. +// Description: Outputs the HashVal as a 32-digit hexadecimal number. +//////////////////////////////////////////////////////////////////// +void HashVal:: +output_hex(ostream &out) const { + char buffer[32]; + encode_hex(_hv[0], buffer); + encode_hex(_hv[1], buffer + 8); + encode_hex(_hv[2], buffer + 16); + encode_hex(_hv[3], buffer + 24); + out.write(buffer, 32); +} + +//////////////////////////////////////////////////////////////////// +// Function: HashVal::input +// Access: Published +// Description: Inputs the HashVal as a 32-digit hexadecimal number. +//////////////////////////////////////////////////////////////////// +void HashVal:: +input_hex(istream &in) { + in >> ws; + char buffer[32]; + size_t i = 0; + int ch = in.get(); + + while (!in.eof() && !in.fail() && isxdigit(ch)) { + if (i < 32) { + buffer[i] = ch; + } + i++; + ch = in.get(); + } + + if (i != 32) { + in.setstate(ios::failbit); + return; + } + + if (!in.eof()) { + in.unget(); + } else { + in.clear(); + } + + decode_hex(buffer, _hv[0]); + decode_hex(buffer + 8, _hv[1]); + decode_hex(buffer + 16, _hv[2]); + decode_hex(buffer + 24, _hv[3]); +} + +//////////////////////////////////////////////////////////////////// +// Function: HashVal::as_dec +// Access: Published +// Description: Returns the HashVal as a string with four decimal +// numbers. //////////////////////////////////////////////////////////////////// string HashVal:: -as_string() const { +as_dec() const { ostringstream strm; - output(strm); + output_dec(strm); return strm.str(); } + +//////////////////////////////////////////////////////////////////// +// Function: HashVal::set_from_dec +// Access: Published +// Description: Sets the HashVal from a string with four decimal +// numbers. Returns true if valid, false otherwise. +//////////////////////////////////////////////////////////////////// +bool HashVal:: +set_from_dec(const string &text) { + istringstream strm(text); + input_dec(strm); + return !strm.fail(); +} + +//////////////////////////////////////////////////////////////////// +// Function: HashVal::as_hex +// Access: Published +// Description: Returns the HashVal as a 32-byte hexadecimal string. +//////////////////////////////////////////////////////////////////// +string HashVal:: +as_hex() const { + char buffer[32]; + encode_hex(_hv[0], buffer); + encode_hex(_hv[1], buffer + 8); + encode_hex(_hv[2], buffer + 16); + encode_hex(_hv[3], buffer + 24); + return string(buffer, 32); +} + +//////////////////////////////////////////////////////////////////// +// Function: HashVal::set_from_hex +// Access: Published +// Description: Sets the HashVal from a 32-byte hexademical string. +// Returns true if successful, false otherwise. +//////////////////////////////////////////////////////////////////// +bool HashVal:: +set_from_hex(const string &text) { + istringstream strm(text); + input_hex(strm); + return !strm.fail(); +} + +#ifdef HAVE_SSL +//////////////////////////////////////////////////////////////////// +// Function: HashVal::hash_file +// Access: Published +// Description: Generates the hash value from the indicated file. +// Returns true on success, false if the file cannot be +// read. This method is only defined if we have the +// OpenSSL library (which provides md5 functionality) +// available. +//////////////////////////////////////////////////////////////////// +bool HashVal:: +hash_file(const Filename &filename) { + Filename bin_filename = Filename::binary_filename(filename); + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); + istream *istr = vfs->open_read_file(bin_filename); + if (istr == (istream *)NULL) { + (*this) = HashVal(); + return false; + } + + unsigned char md[16]; + + MD5_CTX ctx; + MD5_Init(&ctx); + + static const int buffer_size = 1024; + char buffer[buffer_size]; + + istr->read(buffer, buffer_size); + size_t count = istr->gcount(); + while (count != 0) { + MD5_Update(&ctx, buffer, count); + istr->read(buffer, buffer_size); + count = istr->gcount(); + } + + delete istr; + MD5_Final(md, &ctx); + + // Store the individual bytes as big-endian ints, from historical + // convention. + _hv[0] = (md[0] << 24) | (md[1] << 16) | (md[2] << 8) | (md[3]); + _hv[1] = (md[4] << 24) | (md[5] << 16) | (md[6] << 8) | (md[7]); + _hv[2] = (md[8] << 24) | (md[9] << 16) | (md[10] << 8) | (md[11]); + _hv[3] = (md[12] << 24) | (md[13] << 16) | (md[14] << 8) | (md[15]); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: HashVal::hash_buffer +// Access: Published +// Description: Generates the hash value by hashing the indicated +// data. This method is only defined if we have the +// OpenSSL library (which provides md5 functionality) +// available. +//////////////////////////////////////////////////////////////////// +void HashVal:: +hash_buffer(const char *buffer, int length) { + unsigned char md[16]; + MD5((const unsigned char *)buffer, length, md); + + // Store the individual bytes as big-endian ints, from historical + // convention. + _hv[0] = (md[0] << 24) | (md[1] << 16) | (md[2] << 8) | (md[3]); + _hv[1] = (md[4] << 24) | (md[5] << 16) | (md[6] << 8) | (md[7]); + _hv[2] = (md[8] << 24) | (md[9] << 16) | (md[10] << 8) | (md[11]); + _hv[3] = (md[12] << 24) | (md[13] << 16) | (md[14] << 8) | (md[15]); +} + +#endif // HAVE_SSL + + +//////////////////////////////////////////////////////////////////// +// Function: HashVal::encode_hex +// Access: Private, Static +// Description: Encodes the indicated unsigned int into an +// eight-digit hex string, stored at the indicated +// buffer and the following 8 positions. +//////////////////////////////////////////////////////////////////// +void HashVal:: +encode_hex(unsigned int val, char *buffer) { + buffer[0] = tohex(val >> 28); + buffer[1] = tohex(val >> 24); + buffer[2] = tohex(val >> 20); + buffer[3] = tohex(val >> 16); + buffer[4] = tohex(val >> 12); + buffer[5] = tohex(val >> 8); + buffer[6] = tohex(val >> 4); + buffer[7] = tohex(val); +} + +//////////////////////////////////////////////////////////////////// +// Function: HashVal::decode_hex +// Access: Private, Static +// Description: Decodes the indicated eight-digit hex string into an +// unsigned integer. +//////////////////////////////////////////////////////////////////// +void HashVal:: +decode_hex(const char *buffer, unsigned int &val) { + unsigned int bytes[8]; + for (int i = 0; i < 8; i++) { + bytes[i] = fromhex(buffer[i]); + } + + val = ((bytes[0] << 28) | + (bytes[1] << 24) | + (bytes[2] << 20) | + (bytes[3] << 16) | + (bytes[4] << 12) | + (bytes[5] << 8) | + (bytes[6] << 4) | + (bytes[7])); +} + diff --git a/panda/src/express/hashVal.h b/panda/src/express/hashVal.h index 7f9a2a5e43..3f78664727 100644 --- a/panda/src/express/hashVal.h +++ b/panda/src/express/hashVal.h @@ -19,36 +19,68 @@ #ifndef HASHVAL_H #define HASHVAL_H -#include +#include "pandabase.h" #include "typedef.h" -#include +#include "notify.h" +#include "ramfile.h" +#include "datagram.h" +#include "datagramIterator.h" +#include "streamWriter.h" +#include "streamReader.h" //////////////////////////////////////////////////////////////////// // Class : HashVal -// Description : A sixteen-byte hash value sent to the crypt library. +// Description : Stores a 128-bit value that represents the hashed +// contents (typically MD5) of a file or buffer. //////////////////////////////////////////////////////////////////// class EXPCL_PANDAEXPRESS HashVal { PUBLISHED: INLINE HashVal(); INLINE HashVal(const HashVal ©); + INLINE void operator = (const HashVal ©); INLINE bool operator == (const HashVal &other) const; INLINE bool operator != (const HashVal &other) const; - INLINE uint get_value(int val) const; - INLINE void set_value(int val, uint hash); - INLINE void output(ostream &out) const; - string as_string() const; + INLINE bool operator < (const HashVal &other) const; + INLINE int compare_to(const HashVal &other) const; -public: - uint hv[4]; + INLINE void merge_with(const HashVal &other); + + INLINE void output_dec(ostream &out) const; + INLINE void input_dec(istream &in); + void output_hex(ostream &out) const; + void input_hex(istream &in); + + INLINE void output(ostream &out) const; + + string as_dec() const; + bool set_from_dec(const string &text); + + string as_hex() const; + bool set_from_hex(const string &text); + + INLINE void write_datagram(Datagram &destination) const; + INLINE void read_datagram(DatagramIterator &source); + INLINE void write_stream(StreamWriter &destination) const; + INLINE void read_stream(StreamReader &source); + +#ifdef HAVE_SSL + bool hash_file(const Filename &filename); + INLINE void hash_ramfile(const Ramfile &ramfile); + INLINE void hash_string(const string &data); + void hash_buffer(const char *buffer, int length); +#endif // HAVE_SSL + +private: + static void encode_hex(unsigned int val, char *buffer); + static void decode_hex(const char *buffer, unsigned int &val); + INLINE static char tohex(unsigned int nibble); + INLINE static unsigned int fromhex(char digit); + + PN_uint32 _hv[4]; }; -INLINE ostream &operator << (ostream &out, const HashVal &hv) { - out << "["; - hv.output(out); - out << "]"; - return out; -} +INLINE ostream &operator << (ostream &out, const HashVal &hv); #include "hashVal.I" diff --git a/panda/src/express/patchfile.cxx b/panda/src/express/patchfile.cxx index b4339b92b4..b048a92a3a 100644 --- a/panda/src/express/patchfile.cxx +++ b/panda/src/express/patchfile.cxx @@ -16,15 +16,22 @@ // //////////////////////////////////////////////////////////////////// +#include "pandabase.h" + +#ifdef HAVE_SSL + #include "config_express.h" #include "error_utils.h" #include "patchfile.h" -#include "crypto_utils.h" // MD5 stuff #include "streamReader.h" #include "streamWriter.h" #include // for tempnam +#ifdef WIN32_VC +#define tempnam _tempnam +#endif + // PROFILING /////////////////////////////////////////////////////// //#define PROFILE_PATCH_BUILD #ifdef PROFILE_PATCH_BUILD @@ -200,7 +207,7 @@ initiate(const Filename &patch_file, const Filename &file) { // Open the temp file for write { - char *tempfilename = _tempnam(".", "pf"); + char *tempfilename = tempnam(".", "pf"); if (NULL == tempfilename) { express_cat.error() << "Patchfile::initiate() - Failed to create temp file name, using default" << endl; @@ -356,7 +363,7 @@ run(void) { // check the MD5 from the patch file against the newly patched file { HashVal MD5_actual; - md5_a_file(_temp_file, MD5_actual); + MD5_actual.hash_file(_temp_file); if (_MD5_ofResult != MD5_actual) { // Whoops, patching screwed up somehow. _origfile_stream.close(); @@ -376,7 +383,7 @@ run(void) { << "No source hash in patch file to verify.\n"; } else { HashVal MD5_orig; - md5_a_file(_orig_file, MD5_orig); + MD5_orig.hash_file(_orig_file); if (MD5_orig != get_source_hash()) { express_cat.info() << "Started from incorrect source file. Got:\n" @@ -496,20 +503,14 @@ internal_read_header(const Filename &patch_file) { _source_file_length = patch_reader.get_uint32(); // get the MD5 of the source file. - _MD5_ofSource.set_value(0, patch_reader.get_uint32()); - _MD5_ofSource.set_value(1, patch_reader.get_uint32()); - _MD5_ofSource.set_value(2, patch_reader.get_uint32()); - _MD5_ofSource.set_value(3, patch_reader.get_uint32()); + _MD5_ofSource.read_stream(patch_reader); } // get the length of the patched result file _result_file_length = patch_reader.get_uint32(); // get the MD5 of the resultant patched file - _MD5_ofResult.set_value(0, patch_reader.get_uint32()); - _MD5_ofResult.set_value(1, patch_reader.get_uint32()); - _MD5_ofResult.set_value(2, patch_reader.get_uint32()); - _MD5_ofResult.set_value(3, patch_reader.get_uint32()); + _MD5_ofResult.read_stream(patch_reader); express_cat.debug() << "Patchfile::initiate() - valid patchfile" << endl; @@ -530,8 +531,7 @@ PN_uint16 Patchfile:: calc_hash(const char *buffer) { #ifdef USE_MD5_FOR_HASHTABLE_INDEX_VALUES HashVal hash; - - md5_a_buffer((const unsigned char*)buffer, (int)_footprint_length, hash); + hash.hash_buffer(buffer, _footprint_length); //cout << PN_uint16(hash.get_value(0)) << " "; @@ -869,22 +869,16 @@ build(Filename file_orig, Filename file_new, Filename patch_name) { patch_writer.add_uint32(_source_file_length); { // calc MD5 of original file - md5_a_buffer((const unsigned char*)buffer_orig, (int)_source_file_length, _MD5_ofSource); + _MD5_ofSource.hash_buffer(buffer_orig, _source_file_length); // add it to the header - patch_writer.add_uint32(_MD5_ofSource.get_value(0)); - patch_writer.add_uint32(_MD5_ofSource.get_value(1)); - patch_writer.add_uint32(_MD5_ofSource.get_value(2)); - patch_writer.add_uint32(_MD5_ofSource.get_value(3)); + _MD5_ofSource.write_stream(patch_writer); } patch_writer.add_uint32(_result_file_length); { // calc MD5 of resultant patched file - md5_a_buffer((const unsigned char*)buffer_new, (int)_result_file_length, _MD5_ofResult); + _MD5_ofResult.hash_buffer(buffer_new, _result_file_length); // add it to the header - patch_writer.add_uint32(_MD5_ofResult.get_value(0)); - patch_writer.add_uint32(_MD5_ofResult.get_value(1)); - patch_writer.add_uint32(_MD5_ofResult.get_value(2)); - patch_writer.add_uint32(_MD5_ofResult.get_value(3)); + _MD5_ofResult.write_stream(patch_writer); } END_PROFILE(writeHeader, "writing patch file header"); @@ -941,7 +935,7 @@ build(Filename file_orig, Filename file_new, Filename patch_name) { } // are there still more bytes left in the new file? - if ((int)ADD_pos != _result_file_length) { + if (ADD_pos != _result_file_length) { // emit ADD for all remaining bytes emit_ADD(write_stream, _result_file_length - ADD_pos, &buffer_new[ADD_pos], ADD_pos); @@ -961,3 +955,4 @@ build(Filename file_orig, Filename file_new, Filename patch_name) { return true; } +#endif // HAVE_SSL diff --git a/panda/src/express/patchfile.h b/panda/src/express/patchfile.h index 757a86daf1..e71595aadb 100644 --- a/panda/src/express/patchfile.h +++ b/panda/src/express/patchfile.h @@ -17,15 +17,14 @@ //////////////////////////////////////////////////////////////////// #ifndef PATCHFILE_H #define PATCHFILE_H -// -//////////////////////////////////////////////////////////////////// -// Includes -//////////////////////////////////////////////////////////////////// -#include + +#include "pandabase.h" + +#ifdef HAVE_SSL #include "typedef.h" -#include -#include +#include "notify.h" +#include "filename.h" #include "plist.h" #include "datagram.h" #include "datagramIterator.h" @@ -133,4 +132,6 @@ protected: #include "patchfile.I" +#endif // HAVE_SSL + #endif diff --git a/panda/src/express/crypto_utils.h b/panda/src/express/ramfile.I similarity index 60% rename from panda/src/express/crypto_utils.h rename to panda/src/express/ramfile.I index 9fa6f2b16b..29d9807ca4 100644 --- a/panda/src/express/crypto_utils.h +++ b/panda/src/express/ramfile.I @@ -1,5 +1,5 @@ -// Filename: crypto_utils.h -// Created by: drose (07Nov00) +// Filename: ramfile.I +// Created by: mike (09Jan97) // //////////////////////////////////////////////////////////////////// // @@ -16,21 +16,12 @@ // //////////////////////////////////////////////////////////////////// -#ifndef CRYPTO_UTILS_H -#define CRYPTO_UTILS_H - -#include -#include -#include "typedef.h" - -class HashVal; - -BEGIN_PUBLISH - -EXPCL_PANDAEXPRESS void md5_a_file(const Filename &fname, HashVal &ret); -EXPCL_PANDAEXPRESS void md5_a_buffer(const uchar *buf, ulong len, HashVal &ret); - -END_PUBLISH - -#endif - +//////////////////////////////////////////////////////////////////// +// Function: Ramfile::constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Ramfile:: +Ramfile() { + _pos = 0; +} diff --git a/panda/src/express/ramfile.cxx b/panda/src/express/ramfile.cxx new file mode 100644 index 0000000000..4a09893ee4 --- /dev/null +++ b/panda/src/express/ramfile.cxx @@ -0,0 +1,47 @@ +// Filename: ramfile.cxx +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "ramfile.h" + +//////////////////////////////////////////////////////////////////// +// Function: Ramfile::readline +// Access: Published +// Description: Assumes the stream represents a text file, and +// extracts one line up to and including the trailing +// newline character. Returns empty string when the end +// of file is reached. +// +// The interface here is intentionally designed to be +// similar to that for Python's File.readline() +// function. +//////////////////////////////////////////////////////////////////// +string Ramfile:: +readline() { + size_t start = _pos; + while (_pos < _data.length() && _data[_pos] != '\n') { + ++_pos; + } + + if (_pos < _data.length() && _data[_pos] == '\n') { + // Include the newline character also. + ++_pos; + } + + return _data.substr(start, _pos - start); +} + diff --git a/panda/src/express/ramfile.h b/panda/src/express/ramfile.h new file mode 100644 index 0000000000..7ee56c9c28 --- /dev/null +++ b/panda/src/express/ramfile.h @@ -0,0 +1,44 @@ +// Filename: ramfile.h +// Created by: mike (09Jan97) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef RAMFILE_H +#define RAMFILE_H + +#include "pandabase.h" +#include "typedef.h" +#include "referenceCount.h" + +//////////////////////////////////////////////////////////////////// +// Class : Ramfile +// Description : An in-memory buffer specifically designed for +// downloading files to memory. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS Ramfile { +PUBLISHED: + INLINE Ramfile(); + + string readline(); + +public: + size_t _pos; + string _data; +}; + +#include "ramfile.I" + +#endif