Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

panda/src/downloader/httpClient.cxx

Go to the documentation of this file.
00001 // Filename: httpClient.cxx
00002 // Created by:  drose (24Sep02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "httpClient.h"
00020 #include "httpChannel.h"
00021 #include "config_downloader.h"
00022 #include "filename.h"
00023 #include "config_express.h"
00024 #include "virtualFileSystem.h"
00025 #include "executionEnvironment.h"
00026 #include "httpBasicAuthorization.h"
00027 #include "httpDigestAuthorization.h"
00028 
00029 #ifdef HAVE_SSL
00030 
00031 #include <openssl/rand.h>
00032 #ifdef REPORT_OPENSSL_ERRORS
00033 #include <openssl/err.h>
00034 #endif
00035 
00036 // Windows may define this macro inappropriately.
00037 #ifdef X509_NAME
00038 #undef X509_NAME
00039 #endif
00040 
00041 bool HTTPClient::_ssl_initialized = false;
00042 
00043 // This is created once and never freed.
00044 X509_STORE *HTTPClient::_x509_store = NULL;
00045 
00046 
00047 ////////////////////////////////////////////////////////////////////
00048 //     Function: HTTPClient::Constructor
00049 //       Access: Published
00050 //  Description:
00051 ////////////////////////////////////////////////////////////////////
00052 HTTPClient::
00053 HTTPClient() {
00054   _http_version = HTTPEnum::HV_11;
00055   _verify_ssl = verify_ssl ? VS_normal : VS_no_verify;
00056   _ssl_ctx = (SSL_CTX *)NULL;
00057 
00058   _proxy = URLSpec(http_proxy, 1);
00059   if (!http_proxy_username.empty()) {
00060     set_username("*proxy", "", http_proxy_username);
00061   }
00062 
00063   {
00064     // Also load in the general usernames.
00065     Config::ConfigTable::Symbol http_usernames;
00066     config_downloader.GetAll("http-username", http_usernames);
00067     
00068     // When we use GetAll(), we might inadvertently read duplicate
00069     // lines.  Filter them out with a set.
00070     pset<string> already_read;
00071     
00072     Config::ConfigTable::Symbol::iterator si;
00073     for (si = http_usernames.begin(); si != http_usernames.end(); ++si) {
00074       string http_username = (*si).Val();
00075       if (already_read.insert(http_username).second) {
00076         add_http_username(http_username);
00077       }
00078     }
00079   }
00080 
00081   // The first time we create an HTTPClient, we must initialize the
00082   // OpenSSL library.
00083   if (!_ssl_initialized) {
00084     initialize_ssl();
00085   }
00086 }
00087 
00088 ////////////////////////////////////////////////////////////////////
00089 //     Function: HTTPClient::Copy Constructor
00090 //       Access: Published
00091 //  Description:
00092 ////////////////////////////////////////////////////////////////////
00093 HTTPClient::
00094 HTTPClient(const HTTPClient &copy) {
00095   _ssl_ctx = (SSL_CTX *)NULL;
00096 
00097   (*this) = copy;
00098 }
00099 
00100 ////////////////////////////////////////////////////////////////////
00101 //     Function: HTTPClient::Copy Assignment Operator
00102 //       Access: Published
00103 //  Description:
00104 ////////////////////////////////////////////////////////////////////
00105 void HTTPClient::
00106 operator = (const HTTPClient &copy) {
00107   _proxy = copy._proxy;
00108   _http_version = copy._http_version;
00109   _verify_ssl = copy._verify_ssl;
00110   _usernames = copy._usernames;
00111   clear_expected_servers();
00112 
00113   ExpectedServers::const_iterator ei;
00114   for (ei = copy._expected_servers.begin();
00115        ei != copy._expected_servers.end();
00116        ++ei) {
00117     X509_NAME *orig_name = (*ei);
00118     X509_NAME *new_name = X509_NAME_dup(orig_name);
00119     _expected_servers.push_back(new_name);
00120   }
00121 }
00122 
00123 ////////////////////////////////////////////////////////////////////
00124 //     Function: HTTPClient::Destructor
00125 //       Access: Published
00126 //  Description:
00127 ////////////////////////////////////////////////////////////////////
00128 HTTPClient::
00129 ~HTTPClient() {
00130   // Before we can free the context, we must remove the X509_STORE
00131   // pointer from it, so it won't be destroyed along with it (this
00132   // object is shared among all contexts).
00133   if (_ssl_ctx != (SSL_CTX *)NULL) {
00134     nassertv(_ssl_ctx->cert_store == _x509_store);
00135     _ssl_ctx->cert_store = NULL;
00136     SSL_CTX_free(_ssl_ctx);
00137   }
00138 
00139   // Free all of the expected server definitions.
00140   clear_expected_servers();
00141 }
00142 
00143 ////////////////////////////////////////////////////////////////////
00144 //     Function: HTTPClient::set_username
00145 //       Access: Published
00146 //  Description: Specifies the username:password string corresponding
00147 //               to a particular server and/or realm, when demanded by
00148 //               the server.  Either or both of the server or realm
00149 //               may be empty; if so, they match anything.  Also, the
00150 //               server may be set to the special string "*proxy",
00151 //               which will match any proxy server.
00152 //
00153 //               If the username is set to the empty string, this
00154 //               clears the password for the particular server/realm
00155 //               pair.
00156 ////////////////////////////////////////////////////////////////////
00157 void HTTPClient::
00158 set_username(const string &server, const string &realm, const string &username) {
00159   string key = server + ":" + realm;
00160   if (username.empty()) {
00161     _usernames.erase(key);
00162   } else {
00163     _usernames[key] = username;
00164   }
00165 }
00166 
00167 ////////////////////////////////////////////////////////////////////
00168 //     Function: HTTPClient::get_username
00169 //       Access: Published
00170 //  Description: Returns the username:password string set for this
00171 //               server/realm pair, or empty string if nothing has
00172 //               been set.  See set_username().
00173 ////////////////////////////////////////////////////////////////////
00174 string HTTPClient::
00175 get_username(const string &server, const string &realm) const {
00176   string key = server + ":" + realm;
00177   Usernames::const_iterator ui;
00178   ui = _usernames.find(key);
00179   if (ui != _usernames.end()) {
00180     return (*ui).second;
00181   }
00182   return string();
00183 }
00184 
00185 ////////////////////////////////////////////////////////////////////
00186 //     Function: HTTPClient::get_http_version_string
00187 //       Access: Published
00188 //  Description: Returns the current HTTP version setting as a string,
00189 //               e.g. "HTTP/1.0" or "HTTP/1.1".
00190 ////////////////////////////////////////////////////////////////////
00191 string HTTPClient::
00192 get_http_version_string() const {
00193   switch (_http_version) {
00194   case HTTPEnum::HV_09:
00195     return "HTTP/0.9";
00196 
00197   case HTTPEnum::HV_10:
00198     return "HTTP/1.0";
00199 
00200   case HTTPEnum::HV_11:
00201     return "HTTP/1.1";
00202 
00203   case HTTPEnum::HV_other:
00204     // Report the best we can do.
00205     return "HTTP/1.1";
00206   }
00207 
00208   return "unknown";
00209 }
00210 
00211 ////////////////////////////////////////////////////////////////////
00212 //     Function: HTTPClient::parse_http_version_string
00213 //       Access: Published
00214 //  Description: Matches the string representing a particular HTTP
00215 //               version against any of the known versions and returns
00216 //               the appropriate enumerated value, or HV_other if the
00217 //               version is unknown.
00218 ////////////////////////////////////////////////////////////////////
00219 HTTPEnum::HTTPVersion HTTPClient::
00220 parse_http_version_string(const string &version) {
00221   if (version == "HTTP/1.0") {
00222     return HTTPEnum::HV_10;
00223   } else if (version == "HTTP/1.1") {
00224     return HTTPEnum::HV_11;
00225   } else if (version.substr(0, 6) == "HTTP/0") {
00226     return HTTPEnum::HV_09;
00227   } else {
00228     return HTTPEnum::HV_other;
00229   }
00230 }
00231 
00232 ////////////////////////////////////////////////////////////////////
00233 //     Function: HTTPClient::load_certificates
00234 //       Access: Published
00235 //  Description: Reads the certificate(s) (delimited by -----BEGIN
00236 //               CERTIFICATE----- and -----END CERTIFICATE-----) from
00237 //               the indicated file and makes them known as trusted
00238 //               public keys for validating future connections.
00239 //               Returns true on success, false otherwise.
00240 ////////////////////////////////////////////////////////////////////
00241 bool HTTPClient::
00242 load_certificates(const Filename &filename) {
00243   int result;
00244   
00245   if (use_vfs) {
00246     result = load_verify_locations(_ssl_ctx, filename);
00247 
00248   } else {
00249     string os_specific = filename.to_os_specific();
00250     result =
00251       SSL_CTX_load_verify_locations(_ssl_ctx, os_specific.c_str(), NULL);
00252   }
00253 
00254   if (result <= 0) {
00255     downloader_cat.info()
00256       << "Could not load certificates from " << filename << ".\n";
00257 #ifdef REPORT_OPENSSL_ERRORS
00258     ERR_print_errors_fp(stderr);
00259 #endif
00260     return false;
00261   }
00262 
00263   downloader_cat.info()
00264     << "Appending " << result << " SSL certificates from "
00265     << filename << "\n";
00266 
00267   return true;
00268 }
00269 
00270 ////////////////////////////////////////////////////////////////////
00271 //     Function: HTTPClient::add_expected_server
00272 //       Access: Published
00273 //  Description: Adds the indicated string as a definition of a valid
00274 //               server to contact via https.  If no servers have been
00275 //               been added, an https connection will be allowed to
00276 //               any server.  If at least one server has been added,
00277 //               an https connection will be allowed to any of the
00278 //               named servers, but none others.
00279 //
00280 //               The string passed in defines a subset of the server
00281 //               properties that are to be insisted on, using the X509
00282 //               naming convention, e.g. O=WDI/OU=VRStudio/CN=ttown.
00283 //
00284 //               It makes sense to use this in conjunction with
00285 //               set_verify_ssl(), which insists that the https
00286 //               connection uses a verifiable certificate.
00287 ////////////////////////////////////////////////////////////////////
00288 bool HTTPClient::
00289 add_expected_server(const string &server_attributes) {
00290   X509_NAME *name = parse_x509_name(server_attributes);
00291   if (name == (X509_NAME *)NULL) {
00292     return false;
00293   }
00294 
00295   _expected_servers.push_back(name);
00296   return true;
00297 }
00298 
00299 ////////////////////////////////////////////////////////////////////
00300 //     Function: HTTPClient::clear_expected_servers
00301 //       Access: Published
00302 //  Description: Clears the set of expected servers; the HTTPClient
00303 //               will allow an https connection to any server.
00304 ////////////////////////////////////////////////////////////////////
00305 void HTTPClient::
00306 clear_expected_servers() {
00307   for (ExpectedServers::iterator ei = _expected_servers.begin();
00308        ei != _expected_servers.end();
00309        ++ei) {
00310     X509_NAME *name = (*ei);
00311     X509_NAME_free(name);
00312   }
00313   _expected_servers.clear();
00314 }
00315 
00316 ////////////////////////////////////////////////////////////////////
00317 //     Function: HTTPClient::make_channel
00318 //       Access: Published
00319 //  Description: Returns a new HTTPChannel object that may be used
00320 //               for reading multiple documents using the same
00321 //               connection, for greater network efficiency than
00322 //               calling HTTPClient::get_document() repeatedly (which
00323 //               would force a new connection for each document).
00324 //
00325 //               Also, HTTPChannel has some additional, less common
00326 //               interface methods than the basic interface methods
00327 //               that exist on HTTPClient; if you wish to call any of
00328 //               these methods you must first obtain an HTTPChannel.
00329 //
00330 //               Pass true for persistent_connection to gain this
00331 //               network efficiency.  If, on the other hand, your
00332 //               intention is to use the channel to retrieve only one
00333 //               document, then pass false to inform the server that
00334 //               we will be dropping the connection after the first
00335 //               document.
00336 ////////////////////////////////////////////////////////////////////
00337 PT(HTTPChannel) HTTPClient::
00338 make_channel(bool persistent_connection) {
00339   PT(HTTPChannel) doc = new HTTPChannel(this);
00340   doc->set_persistent_connection(persistent_connection);
00341   return doc;
00342 }
00343 
00344 ////////////////////////////////////////////////////////////////////
00345 //     Function: HTTPClient::post_form
00346 //       Access: Published
00347 //  Description: Posts form data to a particular URL and retrieves the
00348 //               response.  Returns a new HTTPChannel object whether
00349 //               the document is successfully read or not; you can
00350 //               test is_valid() and get_return_code() to determine
00351 //               whether the document was retrieved.
00352 ////////////////////////////////////////////////////////////////////
00353 PT(HTTPChannel) HTTPClient::
00354 post_form(const URLSpec &url, const string &body) {
00355   PT(HTTPChannel) doc = new HTTPChannel(this);
00356   doc->post_form(url, body);
00357   return doc;
00358 }
00359 
00360 ////////////////////////////////////////////////////////////////////
00361 //     Function: HTTPClient::get_document
00362 //       Access: Published
00363 //  Description: Opens the named document for reading.  Returns a new
00364 //               HTTPChannel object whether the document is
00365 //               successfully read or not; you can test is_valid() and
00366 //               get_return_code() to determine whether the document
00367 //               was retrieved.
00368 ////////////////////////////////////////////////////////////////////
00369 PT(HTTPChannel) HTTPClient::
00370 get_document(const URLSpec &url) {
00371   PT(HTTPChannel) doc = new HTTPChannel(this);
00372   doc->get_document(url);
00373   return doc;
00374 }
00375 
00376 ////////////////////////////////////////////////////////////////////
00377 //     Function: HTTPClient::get_header
00378 //       Access: Published
00379 //  Description: Like get_document(), except only the header
00380 //               associated with the document is retrieved.  This may
00381 //               be used to test for existence of the document; it
00382 //               might also return the size of the document (if the
00383 //               server gives us this information).
00384 ////////////////////////////////////////////////////////////////////
00385 PT(HTTPChannel) HTTPClient::
00386 get_header(const URLSpec &url) {
00387   PT(HTTPChannel) doc = new HTTPChannel(this);
00388   doc->get_header(url);
00389   return doc;
00390 }
00391 
00392 
00393 ////////////////////////////////////////////////////////////////////
00394 //     Function: HTTPClient::get_ssl_ctx
00395 //       Access: Public
00396 //  Description: Returns the OpenSSL context object, creating it first
00397 //               if needed.
00398 ////////////////////////////////////////////////////////////////////
00399 SSL_CTX *HTTPClient::
00400 get_ssl_ctx() {
00401   if (_ssl_ctx != (SSL_CTX *)NULL) {
00402     return _ssl_ctx;
00403   }
00404 
00405   _ssl_ctx = SSL_CTX_new(SSLv23_client_method());
00406 
00407 #if defined(SSL_097) && !defined(NDEBUG)
00408   // If we have debugging enabled, set a callback that allows us to
00409   // report the SSL messages as they are sent and received.
00410   if (downloader_cat.is_debug()) {
00411     SSL_CTX_set_msg_callback(_ssl_ctx, ssl_msg_callback);
00412   }
00413 #endif
00414 
00415   // Get the configured set of expected servers.
00416   {
00417     // Load in any default certificates listed in the Configrc file.
00418     Config::ConfigTable::Symbol expected_servers;
00419     config_downloader.GetAll("expected-ssl-server", expected_servers);
00420     
00421     // When we use GetAll(), we might inadvertently read duplicate
00422     // lines.  Filter them out with a set.
00423     pset<string> already_read;
00424     
00425     Config::ConfigTable::Symbol::iterator si;
00426     for (si = expected_servers.begin(); si != expected_servers.end(); ++si) {
00427       string expected_server = (*si).Val();
00428       if (already_read.insert(expected_server).second) {
00429         add_expected_server(expected_server);
00430       }
00431     }
00432   }
00433 
00434   if (_x509_store != (X509_STORE *)NULL) {
00435     // If we've already created an x509 store object, share it with
00436     // this context.  It would be better to make a copy of the store
00437     // object for each context, so we could locally add certificates,
00438     // but (a) there doesn't seem to be an interface for this, and (b)
00439     // something funny about loading certificates that seems to save
00440     // some persistent global state anyway.
00441     SSL_CTX_set_cert_store(_ssl_ctx, _x509_store);
00442 
00443   } else {
00444     // Create the first x509 store object, and fill it up with our
00445     // certificates.
00446     _x509_store = X509_STORE_new();
00447     SSL_CTX_set_cert_store(_ssl_ctx, _x509_store);
00448 
00449     // Load in any default certificates listed in the Configrc file.
00450     Config::ConfigTable::Symbol cert_files;
00451     config_downloader.GetAll("ssl-certificates", cert_files);
00452     
00453     // When we use GetAll(), we might inadvertently read duplicate
00454     // lines.  Filter them out with a set.
00455     pset<string> already_read;
00456     
00457     Config::ConfigTable::Symbol::iterator si;
00458     for (si = cert_files.begin(); si != cert_files.end(); ++si) {
00459       string cert_file = (*si).Val();
00460       if (already_read.insert(cert_file).second) {
00461         Filename filename = Filename::from_os_specific(ExecutionEnvironment::expand_string(cert_file));
00462         load_certificates(filename);
00463       }
00464     }
00465   }
00466 
00467   return _ssl_ctx;
00468 }
00469 
00470 ////////////////////////////////////////////////////////////////////
00471 //     Function: HTTPClient::add_http_username
00472 //       Access: Private
00473 //  Description: Handles a Configrc definition for http-username as
00474 //               server:realm:username:password, where either or both
00475 //               of server and realm may be empty, or just
00476 //               server:username:password or username:password.
00477 ////////////////////////////////////////////////////////////////////
00478 void HTTPClient::
00479 add_http_username(const string &http_username) {
00480   size_t c1 = http_username.find(':');
00481   if (c1 != string::npos) {
00482     size_t c2 = http_username.find(':', c1 + 1);
00483     if (c2 != string::npos) {
00484       size_t c3 = http_username.find(':', c2 + 1);
00485       if (c3 != string::npos) {
00486         size_t c4 = http_username.find(':', c3 + 1);
00487         if (c4 != string::npos) {
00488           // Oops, we have five?  Problem.
00489           downloader_cat.error()
00490             << "Invalid http-username " << http_username << "\n";
00491 
00492         } else {
00493           // Ok, we have four.
00494           set_username(http_username.substr(0, c1),
00495                        http_username.substr(c1 + 1, c2 - (c1 + 1)),
00496                        http_username.substr(c2 + 1));
00497         }
00498 
00499       } else {
00500         // We have only three.
00501         set_username(string(),
00502                      http_username.substr(0, c1),
00503                      http_username.substr(c1 + 1));
00504       }
00505     } else {
00506       // We have only two.
00507       set_username(string(), string(), http_username);
00508     }
00509   } else {
00510     // We have only one?  Problem.
00511     downloader_cat.error()
00512       << "Invalid http-username " << http_username << "\n";
00513   }
00514 }
00515 
00516 ////////////////////////////////////////////////////////////////////
00517 //     Function: HTTPClient::select_username
00518 //       Access: Private
00519 //  Description: Chooses a suitable username:password string for the
00520 //               given URL and realm.
00521 ////////////////////////////////////////////////////////////////////
00522 string HTTPClient::
00523 select_username(const URLSpec &url, bool is_proxy, const string &realm) const {
00524   string username;
00525 
00526   // Look in several places in order to find the matching username.
00527 
00528   // Fist, if there's a username on the URL, that always wins (except
00529   // when we are looking for a proxy username).
00530   if (url.has_username() && !is_proxy) {
00531     username = url.get_username();
00532   }
00533 
00534   // Otherwise, start looking on the HTTPClient.  
00535   if (is_proxy) {
00536     if (username.empty()) {
00537       // Try the *proxy/realm.
00538       username = get_username("*proxy", realm);
00539     }
00540     if (username.empty()) {
00541       // Then, try *proxy/any realm.
00542       username = get_username("*proxy", string());
00543     }
00544   }
00545   if (username.empty()) {
00546     // Try the specific server/realm.
00547     username = get_username(url.get_server(), realm);
00548   }
00549   if (username.empty()) {
00550     // Then, try the specific server/any realm.
00551     username = get_username(url.get_server(), string());
00552   }
00553   if (username.empty()) {
00554     // Then, try any server with this realm.
00555     username = get_username(string(), realm);
00556   }
00557   if (username.empty()) {
00558     // Then, take the general password.
00559     username = get_username(string(), string());
00560   }
00561 
00562   return username;
00563 }
00564 
00565 ////////////////////////////////////////////////////////////////////
00566 //     Function: HTTPClient::select_auth
00567 //       Access: Private
00568 //  Description: Chooses a suitable pre-computed authorization for the
00569 //               indicated URL.  Returns NULL if no authorization
00570 //               matches.
00571 ////////////////////////////////////////////////////////////////////
00572 HTTPAuthorization *HTTPClient::
00573 select_auth(const URLSpec &url, bool is_proxy, const string &last_realm) {
00574   Domains &domains = is_proxy ? _proxy_domains : _www_domains;
00575   string canon = HTTPAuthorization::get_canonical_url(url).get_url();
00576 
00577   // Look for the longest domain string that is a prefix of our
00578   // canonical URL.  We have to make a linear scan through the list.
00579   Domains::const_iterator best_di = domains.end();
00580   size_t longest_length = 0;
00581   Domains::const_iterator di;
00582   for (di = domains.begin(); di != domains.end(); ++di) {
00583     const string &domain = (*di).first;
00584     size_t length = domain.length();
00585     if (domain == canon.substr(0, length)) {
00586       // This domain string matches.  Is it the longest?
00587       if (length > longest_length) {
00588         best_di = di;
00589         longest_length = length;
00590       }
00591     }
00592   }
00593 
00594   if (best_di != domains.end()) {
00595     // Ok, we found a matching domain.  Use it.
00596     if (downloader_cat.is_spam()) {
00597       downloader_cat.spam()
00598         << "Choosing domain " << (*best_di).first << " for " << url << "\n";
00599     }
00600     const Realms &realms = (*best_di).second._realms;
00601     // First, try our last realm.
00602     Realms::const_iterator ri;
00603     ri = realms.find(last_realm);
00604     if (ri != realms.end()) {
00605       return (*ri).second;
00606     }
00607 
00608     if (!realms.empty()) {
00609       // Oh well, just return the first realm.
00610       return (*realms.begin()).second;
00611     }
00612   }
00613 
00614   // No matching domains.
00615   return NULL;
00616 }
00617 
00618 ////////////////////////////////////////////////////////////////////
00619 //     Function: HTTPClient::generate_auth
00620 //       Access: Private
00621 //  Description: Generates a new authorization entry in response to a
00622 //               401 or 407 challenge from the server or proxy.  The
00623 //               new authorization entry is stored for future
00624 //               connections to the same server (or, more precisely,
00625 //               the same domain, which may be a subset of the server,
00626 //               or it may include multiple servers).
00627 ////////////////////////////////////////////////////////////////////
00628 PT(HTTPAuthorization) HTTPClient::
00629 generate_auth(const URLSpec &url, bool is_proxy, const string &challenge) {
00630   HTTPAuthorization::AuthenticationSchemes schemes;
00631   HTTPAuthorization::parse_authentication_schemes(schemes, challenge);
00632 
00633   PT(HTTPAuthorization) auth;
00634   HTTPAuthorization::AuthenticationSchemes::iterator si;
00635 
00636   si = schemes.find("digest");
00637   if (si != schemes.end()) {
00638     auth = new HTTPDigestAuthorization((*si).second, url, is_proxy);
00639   }
00640 
00641   if (auth == (HTTPAuthorization *)NULL || !auth->is_valid()) {
00642     si = schemes.find("basic");
00643     if (si != schemes.end()) {
00644       auth = new HTTPBasicAuthorization((*si).second, url, is_proxy);
00645     }
00646   }
00647 
00648   if (auth == (HTTPAuthorization *)NULL || !auth->is_valid()) {
00649     downloader_cat.warning() 
00650       << "Don't know how to use any of the server's available authorization schemes:\n";
00651     for (si = schemes.begin(); si != schemes.end(); ++si) {
00652       downloader_cat.warning() << (*si).first << "\n";
00653     }
00654 
00655   } else {
00656     // Now that we've got an authorization, store it under under each
00657     // of its suggested domains for future use.
00658     Domains &domains = is_proxy ? _proxy_domains : _www_domains;
00659     const vector_string &domain = auth->get_domain();
00660     vector_string::const_iterator si;
00661     for (si = domain.begin(); si != domain.end(); ++si) {
00662       domains[(*si)]._realms[auth->get_realm()] = auth;
00663     }
00664   }
00665 
00666   return auth;
00667 }
00668 
00669 ////////////////////////////////////////////////////////////////////
00670 //     Function: HTTPClient::initialize_ssl
00671 //       Access: Private, Static
00672 //  Description: Called once the first time this class is used to
00673 //               initialize the OpenSSL library.
00674 ////////////////////////////////////////////////////////////////////
00675 void HTTPClient::
00676 initialize_ssl() {
00677 #ifdef REPORT_OPENSSL_ERRORS
00678   ERR_load_crypto_strings();
00679   ERR_load_SSL_strings();
00680 #endif
00681   OpenSSL_add_all_algorithms();
00682 
00683   // Call RAND_status() here to force the random number generator to
00684   // initialize early.
00685   if (early_random_seed) {
00686     RAND_status();
00687   }
00688 
00689   _ssl_initialized = true;
00690 }
00691 
00692 ////////////////////////////////////////////////////////////////////
00693 //     Function: HTTPClient::load_verify_locations
00694 //       Access: Private, Static
00695 //  Description: An implementation of the OpenSSL-provided
00696 //               SSL_CTX_load_verify_locations() that takes a Filename
00697 //               (and supports Panda vfs).
00698 //
00699 //               This reads the certificates from the named ca_file
00700 //               and makes them available to the given SSL context.
00701 //               It returns a positive number on success, or <= 0 on
00702 //               failure.
00703 ////////////////////////////////////////////////////////////////////
00704 int HTTPClient::
00705 load_verify_locations(SSL_CTX *ctx, const Filename &ca_file) {
00706   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
00707 
00708   // First, read the complete file into memory.
00709   string data;
00710   if (!vfs->read_file(ca_file, data)) {
00711     // Could not find or read file.
00712     downloader_cat.info()
00713       << "Could not read " << ca_file << ".\n";
00714     return 0;
00715   }
00716 
00717   STACK_OF(X509_INFO) *inf;
00718 
00719   // Now create an in-memory BIO to read the "file" from the buffer we
00720   // just read, and call the low-level routines to read the
00721   // certificates from the BIO.
00722   BIO *mbio = BIO_new_mem_buf((void *)data.data(), data.length());
00723 
00724   // We have to be sure and clear the OpenSSL error state before we
00725   // call this function, or it will get confused.
00726   ERR_clear_error();
00727   inf = PEM_X509_INFO_read_bio(mbio, NULL, NULL, NULL);
00728   BIO_free(mbio);
00729 
00730   if (!inf) {
00731     // Could not scan certificates.
00732     downloader_cat.info()
00733       << "PEM_X509_INFO_read_bio() returned NULL.\n";
00734 #ifdef REPORT_OPENSSL_ERRORS
00735     ERR_print_errors_fp(stderr);
00736 #endif
00737     return 0;
00738   }
00739   
00740   if (downloader_cat.is_spam()) {
00741     downloader_cat.spam()
00742       << "PEM_X509_INFO_read_bio() found " << sk_X509_INFO_num(inf)
00743       << " entries.\n";
00744   }
00745 
00746   // Now add the certificates to the context.
00747   X509_STORE *store = ctx->cert_store;
00748 
00749   int count = 0;
00750   int num_entries = sk_X509_INFO_num(inf);
00751   for (int i = 0; i < num_entries; i++) {
00752     X509_INFO *itmp = sk_X509_INFO_value(inf, i);
00753 
00754     if (itmp->x509) {
00755       X509_STORE_add_cert(store, itmp->x509);
00756       count++;
00757       if (downloader_cat.is_spam()) {
00758         downloader_cat.spam()
00759           << "Entry " << i << " is x509\n";
00760       }
00761 
00762     } else if (itmp->crl) {
00763       X509_STORE_add_crl(store, itmp->crl);
00764       count++;
00765       if (downloader_cat.is_spam()) {
00766         downloader_cat.spam()
00767           << "Entry " << i << " is crl\n";
00768       }
00769 
00770     } else {
00771       if (downloader_cat.is_spam()) {
00772         downloader_cat.spam()
00773           << "Entry " << i << " is unknown type\n";
00774       }
00775     }
00776   }
00777   sk_X509_INFO_pop_free(inf, X509_INFO_free);
00778 
00779   return count;
00780 }
00781 
00782 ////////////////////////////////////////////////////////////////////
00783 //     Function: HTTPClient::parse_x509_name
00784 //       Access: Private, Static
00785 //  Description: Parses a string of the form
00786 //               /type0=value0/type1=value1/type2=... into a newly
00787 //               allocated X509_NAME object.  Returns NULL if the
00788 //               string is invalid.
00789 ////////////////////////////////////////////////////////////////////
00790 X509_NAME *HTTPClient::
00791 parse_x509_name(const string &source) {
00792   X509_NAME *result = NULL;
00793 
00794   result = X509_NAME_new();
00795   bool added_any = false;
00796 
00797   string::const_iterator si;
00798   si = source.begin();
00799   while (si != source.end()) {
00800     if ((*si) == '/') {
00801       // Skip a slash delimiter.
00802       ++si;
00803     } else {
00804       string type;
00805       while (si != source.end() && (*si) != '=' && (*si) != '/') {
00806         if ((*si) == '\\') {
00807           ++si;
00808           if (si != source.end()) {
00809             type += (*si);
00810             ++si;
00811           }
00812         } else {
00813           type += (*si);
00814           ++si;
00815         }
00816       }
00817 
00818       int nid = OBJ_txt2nid((char *)type.c_str());
00819       if (nid == NID_undef) {
00820         downloader_cat.info()
00821           << "Unknown type " << type << " in X509 name: " << source
00822           << "\n";
00823         X509_NAME_free(result);
00824         return NULL;
00825       }
00826 
00827       string value;
00828       
00829       if (si != source.end() && (*si) == '=') {
00830         ++si;
00831         while (si != source.end() && (*si) != '/') {
00832           if ((*si) == '\\') {
00833             ++si;
00834             if (si != source.end()) {
00835               value += (*si);
00836               ++si;
00837             }
00838           } else {
00839             value += (*si);
00840             ++si;
00841           }
00842         }
00843       }
00844 
00845       if (!value.empty()) {
00846         int add_result =
00847           X509_NAME_add_entry_by_NID(result, nid, V_ASN1_APP_CHOOSE, 
00848                                      (unsigned char *)value.c_str(), -1, -1, 0);
00849         if (!add_result) {
00850           downloader_cat.info()
00851             << "Unable to add " << type << "=" << value << " in X509 name: "
00852             << source << "\n";
00853           X509_NAME_free(result);
00854           return NULL;
00855         }
00856         added_any = true;
00857       }
00858     }
00859   }
00860 
00861   if (!added_any) {
00862     downloader_cat.info()
00863       << "Invalid empty X509 name: " << source << "\n";
00864     X509_NAME_free(result);
00865     return NULL;
00866   }
00867 
00868   return result;
00869 }
00870 
00871 #if defined(SSL_097) && !defined(NDEBUG)
00872 ////////////////////////////////////////////////////////////////////
00873 //     Function: HTTPClient::ssl_msg_callback
00874 //       Access: Private, Static
00875 //  Description: This method is attached as a callback for SSL
00876 //               messages only when debug output is enabled.
00877 ////////////////////////////////////////////////////////////////////
00878 void HTTPClient::
00879 ssl_msg_callback(int write_p, int version, int content_type,
00880                  const void *, size_t len, SSL *, void *) {
00881   ostringstream describe;
00882   if (write_p) {
00883     describe << "sent ";
00884   } else {
00885     describe << "received ";
00886   }
00887   switch (version) {
00888   case SSL2_VERSION:
00889     describe << "SSL 2.0 ";
00890     break;
00891 
00892   case SSL3_VERSION:
00893     describe << "SSL 3.0 ";
00894     break;
00895 
00896   case TLS1_VERSION: 
00897     describe << "TLS 1.0 ";
00898     break;
00899 
00900   default:
00901     describe << "unknown protocol ";
00902   }
00903 
00904   describe << "message: ";
00905 
00906   if (version != SSL2_VERSION) {
00907     switch (content_type) {
00908     case 20:
00909       describe << "change cipher spec, ";
00910       break;
00911       
00912     case 21:
00913       describe << "alert, ";
00914       break;
00915       
00916     case 22:
00917       describe << "handshake, ";
00918       break;
00919       
00920     case 23:
00921       describe << "application data, ";
00922       break;
00923       
00924     default:
00925       describe << "unknown content type, ";
00926     }
00927   }
00928 
00929   describe << len << " bytes.\n";
00930 
00931   downloader_cat.debug() << describe.str();
00932 }
00933 #endif  // defined(SSL_097) && !defined(NDEBUG)
00934 
00935 #endif  // HAVE_SSL

Generated on Fri May 2 00:36:49 2003 for Panda by doxygen1.3