Panda3D
Loading...
Searching...
No Matches
httpClient.cxx
Go to the documentation of this file.
1/**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file httpClient.cxx
10 * @author drose
11 * @date 2002-09-24
12 */
13
14#include "httpClient.h"
15#include "httpChannel.h"
16#include "config_downloader.h"
17#include "filename.h"
18#include "config_express.h"
19#include "virtualFileSystem.h"
23#include "globPattern.h"
24
25#ifdef HAVE_OPENSSL
26
27#include "openSSLWrapper.h"
28
29using std::string;
30
31PT(HTTPClient) HTTPClient::_global_ptr;
32
33/**
34 *
35 */
36static string
37trim_blanks(const string &str) {
38 size_t start = 0;
39 while (start < str.length() && isspace(str[start])) {
40 start++;
41 }
42
43 size_t end = str.length();
44 while (end > start && isspace(str[end - 1])) {
45 end--;
46 }
47
48 return str.substr(start, end - start);
49}
50
51/**
52 * Chops the source string up into pieces delimited by any of the characters
53 * specified in delimiters. Repeated delimiter characters represent zero-
54 * length tokens.
55 *
56 * It is the user's responsibility to ensure the output vector is cleared
57 * before calling this function; the results will simply be appended to the
58 * end of the vector.
59 */
60static void
61tokenize(const string &str, vector_string &words, const string &delimiters) {
62 size_t p = 0;
63 while (p < str.length()) {
64 size_t q = str.find_first_of(delimiters, p);
65 if (q == string::npos) {
66 words.push_back(str.substr(p));
67 return;
68 }
69 words.push_back(str.substr(p, q - p));
70 p = q + 1;
71 }
72 words.push_back(string());
73}
74
75#ifndef NDEBUG
76/**
77 * This method is attached as a callback for SSL messages only when debug
78 * output is enabled.
79 */
80static void
81ssl_msg_callback(int write_p, int version, int content_type,
82 const void *, size_t len, SSL *, void *) {
83 std::ostringstream describe;
84 if (write_p) {
85 describe << "sent ";
86 } else {
87 describe << "received ";
88 }
89 switch (version) {
90 case SSL2_VERSION:
91 describe << "SSL 2.0 ";
92 break;
93
94 case SSL3_VERSION:
95 describe << "SSL 3.0 ";
96 break;
97
98 case TLS1_VERSION:
99 describe << "TLS 1.0 ";
100 break;
101
102 default:
103 describe << "unknown protocol ";
104 }
105
106 describe << "message: ";
107
108 if (version != SSL2_VERSION) {
109 switch (content_type) {
110 case 20:
111 describe << "change cipher spec, ";
112 break;
113
114 case 21:
115 describe << "alert, ";
116 break;
117
118 case 22:
119 describe << "handshake, ";
120 break;
121
122 case 23:
123 describe << "application data, ";
124 break;
125
126 default:
127 describe << "unknown content type, ";
128 }
129 }
130
131 describe << len << " bytes.\n";
132
133 downloader_cat.debug() << describe.str();
134}
135#endif // !defined(NDEBUG)
136
137/**
138 *
139 */
140HTTPClient::
141HTTPClient() {
142 ConfigVariableBool verify_ssl
143 ("verify-ssl", true,
144 PRC_DESC("Configure this true (the default) to insist on verifying all SSL "
145 "(e.g. https) servers against a known certificate, or false to allow "
146 "an unverified connection. This controls the default behavior; the "
147 "specific behavior for a particular HTTPClient can be adjusted at "
148 "runtime with set_verify_ssl()."));
149
150 ConfigVariableString ssl_cipher_list
151 ("ssl-cipher-list", "DEFAULT",
152 PRC_DESC("This is the default value for HTTPClient::set_cipher_list()."));
153
154 ConfigVariableString http_proxy
155 ("http-proxy", "",
156 PRC_DESC("This specifies the default value for HTTPClient::set_proxy_spec(). "
157 "It is a semicolon-delimited list of proxies that we use to contact "
158 "all HTTP hosts that don't specify otherwise. See "
159 "set_proxy_spec() for more information."));
160
161 ConfigVariableString http_direct_hosts
162 ("http-direct-hosts", "",
163 PRC_DESC("This specifies the default value for HTTPClient::set_direct_host_spec(). "
164 "It is a semicolon-delimited list of host names that do not require a "
165 "proxy. See set_direct_host_spec() for more information."));
166
167 ConfigVariableBool http_try_all_direct
168 ("http-try-all-direct", true,
169 PRC_DESC("This specifies the default value for HTTPClient::set_try_all_direct(). "
170 "If this is true, a direct connection will always be attempted after an "
171 "attempt to connect through a proxy fails."));
172
173 ConfigVariableString http_proxy_username
174 ("http-proxy-username", "",
175 PRC_DESC("This specifies a default username:password to pass to the proxy."));
176
177 ConfigVariableList http_username
178 ("http-username",
179 PRC_DESC("Adds one or more username/password pairs to all HTTP clients. The client "
180 "will present this username/password when asked to authenticate a request "
181 "for a particular server and/or realm. The username is of the form "
182 "server:realm:username:password, where either or both of server and "
183 "realm may be empty, or just realm:username:password or username:password. "
184 "If the server or realm is empty, they will match anything."));
185
186 ConfigVariableFilename http_client_certificate_filename
187 ("http-client-certificate-filename", "",
188 PRC_DESC("This provides a default client certificate to offer up should an "
189 "SSL server demand one. The file names a PEM-formatted file "
190 "that includes a public and private key specification. A "
191 "connection-specific certificate may also be specified at runtime on "
192 "the HTTPClient object, but this will require having a different "
193 "HTTPClient object for each differently-certificated connection."));
194
195 ConfigVariableString http_client_certificate_passphrase
196 ("http-client-certificate-passphrase", "",
197 PRC_DESC("This specifies the passphrase to use to decode the certificate named "
198 "by http-client-certificate-filename."));
199
200
201 ConfigVariableList http_preapproved_server_certificate_filename
202 ("http-preapproved-server-certificate-filename",
203 PRC_DESC("This specifies a server hostname:port combination, followed by "
204 "at least one space, and the name of a PEM file that contains "
205 "an SSL certificate that we expect this server to offer. If "
206 "it does, we will accept the cert without further question, "
207 "even if the cert does not match any known certificate "
208 "authorities. This option may appear multiple times."));
209
210 ConfigVariableList http_preapproved_server_certificate_name
211 ("http-preapproved-server-certificate-name",
212 PRC_DESC("This specifies a server hostname:port combination, followed by "
213 "at least one space, and a string describing the subject name "
214 "of an SSL certificate we expect this server to offer. If it "
215 "it does, we will accept the cert, but only if it also matches "
216 "a known certificate authority. This option may appear "
217 "multiple times."));
218
219 _http_version = HTTPEnum::HV_11;
220 _verify_ssl = verify_ssl ? VS_normal : VS_no_verify;
221 _ssl_ctx = nullptr;
222
223 set_proxy_spec(http_proxy);
224 set_direct_host_spec(http_direct_hosts);
225 _try_all_direct = http_try_all_direct;
226
227 if (!http_proxy_username.empty()) {
228 set_username("*proxy", "", http_proxy_username);
229 }
230
231 set_cipher_list(ssl_cipher_list);
232
233 {
234 // Also load in the general usernames.
235 int num_unique_values = http_username.get_num_unique_values();
236 for (int i = 0; i < num_unique_values; i++) {
237 string username = http_username.get_unique_value(i);
238 add_http_username(username);
239 }
240 }
241
242 _client_certificate_filename = http_client_certificate_filename;
243 _client_certificate_passphrase = http_client_certificate_passphrase;
244
245 _client_certificate_loaded = false;
246 _client_certificate_pub = nullptr;
247 _client_certificate_priv = nullptr;
248
249 int num_server_certs = http_preapproved_server_certificate_filename.get_num_unique_values();
250 int si;
251 for (si = 0; si < num_server_certs; si++) {
252 string cert_line = http_preapproved_server_certificate_filename.get_unique_value(si);
253 string a, b;
254 split_whitespace(a, b, cert_line);
255 add_preapproved_server_certificate_filename(URLSpec(a, true), b);
256 }
257
258 num_server_certs = http_preapproved_server_certificate_name.get_num_unique_values();
259 for (si = 0; si < num_server_certs; si++) {
260 string cert_line = http_preapproved_server_certificate_name.get_unique_value(si);
261 string a, b;
262 split_whitespace(a, b, cert_line);
263 add_preapproved_server_certificate_name(URLSpec(a, true), b);
264 }
265
266 // The first time we create an HTTPClient, we must initialize the OpenSSL
267 // library. The OpenSSLWrapper object does that.
268 OpenSSLWrapper::get_global_ptr();
269}
270
271/**
272 *
273 */
274HTTPClient::
275HTTPClient(const HTTPClient &copy) {
276 _ssl_ctx = nullptr;
277
278 (*this) = copy;
279}
280
281/**
282 *
283 */
284void HTTPClient::
285operator = (const HTTPClient &copy) {
286 _proxies_by_scheme = copy._proxies_by_scheme;
287 _direct_hosts = copy._direct_hosts;
288 _try_all_direct = copy._try_all_direct;
289 _http_version = copy._http_version;
290 _verify_ssl = copy._verify_ssl;
291 _usernames = copy._usernames;
292 _cookies = copy._cookies;
293}
294
295/**
296 *
297 */
298HTTPClient::
299~HTTPClient() {
300 if (_ssl_ctx != nullptr) {
301#if OPENSSL_VERSION_NUMBER < 0x10100000
302 // Before we can free the context, we must remove the X509_STORE pointer
303 // from it, so it won't be destroyed along with it (this object is shared
304 // among all contexts).
305 _ssl_ctx->cert_store = nullptr;
306#endif
307 SSL_CTX_free(_ssl_ctx);
308 }
309
310 unload_client_certificate();
311}
312
313/**
314 * This may be called once, presumably at the beginning of an application, to
315 * initialize OpenSSL's random seed. On Windows, it is particularly important
316 * to call this at startup if you are going to be performing any https
317 * operations or otherwise use encryption, since the Windows algorithm for
318 * getting a random seed takes 2-3 seconds at startup, but can take 30 seconds
319 * or more after you have opened a 3-D graphics window and started rendering.
320 *
321 * There is no harm in calling this method multiple times, or in not calling
322 * it at all.
323 */
324void HTTPClient::
325init_random_seed() {
326 // Creating the global OpenSSLWrapper object is nowadays sufficient to
327 // ensure that OpenSSL and its random seed have been initialized.
328 OpenSSLWrapper::get_global_ptr();
329}
330
331/**
332 * Specifies the complete set of proxies to use for all schemes. This is
333 * either a semicolon-delimited set of hostname:ports, or a semicolon-
334 * delimited set of pairs of the form "scheme=hostname:port", or a
335 * combination. Use the keyword DIRECT, or an empty string, to represent a
336 * direct connection. A particular scheme and/or proxy host may be listed
337 * more than once. This is a convenience function that can be used in place
338 * of explicit calls to add_proxy() for each scheme/proxy pair.
339 */
340void HTTPClient::
341set_proxy_spec(const string &proxy_spec) {
342 clear_proxy();
343
344 string trim_proxy_spec = trim_blanks(proxy_spec);
345
346 // Tokenize the string based on the semicolons.
347 if (!trim_proxy_spec.empty()) {
348 vector_string proxies;
349 tokenize(trim_proxy_spec, proxies, ";");
350
351 for (vector_string::const_iterator pi = proxies.begin();
352 pi != proxies.end();
353 ++pi) {
354 const string &spec = (*pi);
355
356 // Divide out the scheme and the hostname.
357 string scheme;
358 string proxy;
359 size_t equals = spec.find('=');
360 if (equals == string::npos) {
361 scheme = "";
362 proxy = trim_blanks(spec);
363 } else {
364 scheme = trim_blanks(spec.substr(0, equals));
365 proxy = trim_blanks(spec.substr(equals + 1));
366 }
367
368 if (proxy == "DIRECT" || proxy.empty()) {
369 add_proxy(scheme, URLSpec());
370 } else {
371 add_proxy(scheme, URLSpec(proxy, true));
372 }
373 }
374 }
375}
376
377/**
378 * Returns the complete set of proxies to use for all schemes. This is a
379 * string of the form specified by set_proxy_spec(), above. Note that the
380 * string returned by this function may not be exactly the same as the string
381 * passed into set_proxy_spec(), since the string is regenerated from the
382 * internal storage structures and may therefore be reordered.
383 */
384string HTTPClient::
385get_proxy_spec() const {
386 string result;
387
388 ProxiesByScheme::const_iterator si;
389 for (si = _proxies_by_scheme.begin(); si != _proxies_by_scheme.end(); ++si) {
390 const string &scheme = (*si).first;
391 const Proxies &proxies = (*si).second;
392 Proxies::const_iterator pi;
393 for (pi = proxies.begin(); pi != proxies.end(); ++pi) {
394 const URLSpec &url = (*pi);
395 if (!result.empty()) {
396 result += ";";
397 }
398 if (!scheme.empty()) {
399 result += scheme;
400 result += "=";
401 }
402 if (url.empty()) {
403 result += "DIRECT";
404 } else {
405 result += url.get_url();
406 }
407 }
408 }
409
410 return result;
411}
412
413/**
414 * Specifies the set of hosts that should be connected to directly, without
415 * using a proxy. This is a semicolon-separated list of hostnames that may
416 * contain wildcard characters ("*").
417 */
418void HTTPClient::
419set_direct_host_spec(const string &direct_host_spec) {
420 clear_direct_host();
421
422 // Tokenize the string based on the semicolons.
423 vector_string hosts;
424 tokenize(direct_host_spec, hosts, ";");
425
426 for (vector_string::const_iterator hi = hosts.begin();
427 hi != hosts.end();
428 ++hi) {
429 string spec = trim_blanks(*hi);
430
431 // We should be careful to avoid adding any empty hostnames to the list.
432 // In particular, we will get one empty hostname if the direct_host_spec
433 // is empty.
434 if (!spec.empty()) {
435 add_direct_host(spec);
436 }
437 }
438}
439
440/**
441 * Returns the set of hosts that should be connected to directly, without
442 * using a proxy, as a semicolon-separated list of hostnames that may contain
443 * wildcard characters ("*").
444 */
445string HTTPClient::
446get_direct_host_spec() const {
447 string result;
448
449 DirectHosts::const_iterator si;
450 for (si = _direct_hosts.begin(); si != _direct_hosts.end(); ++si) {
451 const GlobPattern &host = (*si);
452
453 if (!result.empty()) {
454 result += ";";
455 }
456 result += host.get_pattern();
457 }
458
459 return result;
460}
461
462/**
463 * Resets the proxy spec to empty. Subsequent calls to add_proxy() may be
464 * made to build up the set of proxy servers.
465 */
466void HTTPClient::
467clear_proxy() {
468 _proxies_by_scheme.clear();
469}
470
471/**
472 * Adds the indicated proxy host as a proxy for communications on the given
473 * scheme. Usually the scheme is "http" or "https". It may be the empty
474 * string to indicate a general proxy. The proxy string may be the empty URL
475 * to indicate a direct connection.
476 */
477void HTTPClient::
478add_proxy(const string &scheme, const URLSpec &proxy) {
479 URLSpec proxy_url(proxy);
480
481 // The scheme is always converted to lowercase.
482 string lc_scheme;
483 lc_scheme.reserve(scheme.length());
484 string::const_iterator si;
485 for (si = scheme.begin(); si != scheme.end(); ++si) {
486 lc_scheme += tolower(*si);
487 }
488
489 // Remove the trailing colon, if there is one.
490 if (!lc_scheme.empty() && lc_scheme[lc_scheme.length() - 1] == ':') {
491 lc_scheme = lc_scheme.substr(0, lc_scheme.length() - 1);
492 }
493
494 if (!proxy_url.empty()) {
495 // Enforce the scheme that we use to communicate to the proxy itself.
496 // This is not the same as lc_scheme, which is the scheme of the requested
497 // connection. Generally, all proxies speak HTTP, except for Socks
498 // proxies.
499
500 if (lc_scheme == "socks") {
501 // Scheme "socks" implies we talk to the proxy via the "socks" scheme,
502 // no matter what scheme the user actually specified.
503 proxy_url.set_scheme("socks");
504
505 } else if (!proxy_url.has_scheme()) {
506 // Otherwise, if the user didn't specify a scheme to talk to the proxy,
507 // the default is "http".
508 proxy_url.set_scheme("http");
509 }
510 }
511
512 _proxies_by_scheme[lc_scheme].push_back(proxy_url);
513}
514
515/**
516 * Resets the set of direct hosts to empty. Subsequent calls to
517 * add_direct_host() may be made to build up the list of hosts that do not
518 * require a proxy connection.
519 */
520void HTTPClient::
521clear_direct_host() {
522 _direct_hosts.clear();
523}
524
525/**
526 * Adds the indicated name to the set of hostnames that are connected to
527 * directly, without using a proxy. This name may be either a DNS name or an
528 * IP address, and it may include the * as a wildcard character.
529 */
530void HTTPClient::
531add_direct_host(const string &hostname) {
532 // The hostname is always converted to lowercase.
533 string lc_hostname;
534 lc_hostname.reserve(hostname.length());
535 for (string::const_iterator si = hostname.begin();
536 si != hostname.end();
537 ++si) {
538 lc_hostname += tolower(*si);
539 }
540
541 _direct_hosts.push_back(GlobPattern(lc_hostname));
542}
543
544/**
545 * Fills up the indicated vector with the list of URLSpec objects, in the
546 * order in which they should be tried, that are appropriate proxies to try
547 * for the indicated URL. The empty URL is returned for a direct connection.
548 *
549 * It is the user's responsibility to empty this vector before calling this
550 * method; otherwise, the proxy URL's will simply be appended to the existing
551 * list.
552 */
553void HTTPClient::
554get_proxies_for_url(const URLSpec &url, pvector<URLSpec> &proxies) const {
555 // First, check if the hostname matches any listed in direct_hosts.
556 string hostname = url.get_server();
557
558 // If the hostname is empty, treat it as a special case: we don't match any
559 // of the hostnames listed in direct_hosts (even "*").
560 if (!hostname.empty()) {
561 DirectHosts::const_iterator si;
562 for (si = _direct_hosts.begin(); si != _direct_hosts.end(); ++si) {
563 if ((*si).matches(hostname)) {
564 // It matches, so don't use any proxies.
565 proxies.push_back(URLSpec());
566 return;
567 }
568 }
569 }
570
571 // Build our list of proxies into a temporary vector, so we can pull out
572 // duplicates later.
573 pvector<URLSpec> temp_list;
574
575 // Now choose the appropriate proxy based on the scheme.
576 string scheme = url.get_scheme();
577 bool got_any = false;
578
579 if (!scheme.empty()) {
580 // If we have a scheme, try to match it.
581 if (get_proxies_for_scheme(scheme, temp_list)) {
582 got_any = true;
583 }
584 }
585
586 if (!got_any && (scheme.empty() || url.is_ssl())) {
587 // An empty scheme (or an ssl-style scheme) implies we will need to make a
588 // direct connection, so fallback to a socks-style andor https-style
589 // scheme.
590
591 if (get_proxies_for_scheme("socks", temp_list)) {
592 got_any = true;
593 }
594 if (get_proxies_for_scheme("https", temp_list)) {
595 got_any = true;
596 }
597 }
598
599 if (!got_any) {
600 // If we didn't find our scheme of choice, fall back to the default proxy
601 // type, if we've got one.
602 if (get_proxies_for_scheme("", temp_list)) {
603 got_any = true;
604 }
605 }
606
607 if (_try_all_direct) {
608 // We may try a direct connection if all else fails.
609 temp_list.push_back(URLSpec());
610 }
611
612 // Finally, as a very last resort, fall back to the HTTP proxy.
613 if (!got_any) {
614 get_proxies_for_scheme("http", temp_list);
615 }
616
617 // Now remove duplicate entries as we copy the resulting list out.
619 pset<URLSpec> used;
620 for (pi = temp_list.begin(); pi != temp_list.end(); ++pi) {
621 if (used.insert(*pi).second) {
622 // This is a unique one.
623 proxies.push_back(*pi);
624 }
625 }
626}
627
628/**
629 * Returns a semicolon-delimited list of proxies, in the order in which they
630 * should be tried, that are appropriate for the indicated URL. The keyword
631 * DIRECT indicates a direct connection should be tried.
632 */
633string HTTPClient::
634get_proxies_for_url(const URLSpec &url) const {
635 pvector<URLSpec> proxies;
636 get_proxies_for_url(url, proxies);
637
638 string result;
639 if (!proxies.empty()) {
640 pvector<URLSpec>::const_iterator pi = proxies.begin();
641 if ((*pi).get_url().empty()) {
642 result += "DIRECT";
643 } else {
644 result += (*pi).get_url();
645 }
646 ++pi;
647
648 while (pi != proxies.end()) {
649 result += ";";
650 if ((*pi).get_url().empty()) {
651 result += "DIRECT";
652 } else {
653 result += (*pi).get_url();
654 }
655 ++pi;
656 }
657 }
658
659 return result;
660}
661
662/**
663 * Specifies the username:password string corresponding to a particular server
664 * and/or realm, when demanded by the server. Either or both of the server or
665 * realm may be empty; if so, they match anything. Also, the server may be
666 * set to the special string `"*proxy"`, which will match any proxy server.
667 *
668 * If the username is set to the empty string, this clears the password for
669 * the particular server/realm pair.
670 */
671void HTTPClient::
672set_username(const string &server, const string &realm, const string &username) {
673 string key = server + ":" + realm;
674 if (username.empty()) {
675 _usernames.erase(key);
676 } else {
677 _usernames[key] = username;
678 }
679}
680
681/**
682 * Returns the username:password string set for this server/realm pair, or
683 * empty string if nothing has been set. See set_username().
684 */
685string HTTPClient::
686get_username(const string &server, const string &realm) const {
687 string key = server + ":" + realm;
688 Usernames::const_iterator ui;
689 ui = _usernames.find(key);
690 if (ui != _usernames.end()) {
691 return (*ui).second;
692 }
693 return string();
694}
695
696/**
697 * Stores the indicated cookie in the client's list of cookies, as if it had
698 * been received from a server.
699 */
700void HTTPClient::
701set_cookie(const HTTPCookie &cookie) {
702 if (cookie.is_expired()) {
703 clear_cookie(cookie);
704
705 } else {
706 std::pair<Cookies::iterator, bool> result = _cookies.insert(cookie);
707 if (!result.second) {
708 // We already had a cookie matching the supplied domainpathname, so
709 // replace it.
710 const HTTPCookie &orig_cookie = *result.first;
711 ((HTTPCookie &)orig_cookie).update_from(cookie);
712 }
713 }
714}
715
716/**
717 * Removes the cookie with the matching domain/path/name from the client's
718 * list of cookies. Returns true if it was removed, false if the cookie was
719 * not matched.
720 */
721bool HTTPClient::
722clear_cookie(const HTTPCookie &cookie) {
723 Cookies::iterator ci = _cookies.find(cookie);
724 if (ci != _cookies.end()) {
725 _cookies.erase(ci);
726 return true;
727 }
728
729 return false;
730}
731
732/**
733 * Removes the all stored cookies from the client.
734 */
735void HTTPClient::
736clear_all_cookies() {
737 _cookies.clear();
738}
739
740/**
741 * Returns true if there is a cookie in the client matching the given cookie's
742 * domain/path/name, false otherwise.
743 */
744bool HTTPClient::
745has_cookie(const HTTPCookie &cookie) const {
746 Cookies::const_iterator ci = _cookies.find(cookie);
747 return (ci != _cookies.end());
748}
749
750/**
751 * Looks up and returns the cookie in the client matching the given cookie's
752 * domain/path/name. If there is no matching cookie, returns an empty cookie.
753 */
754HTTPCookie HTTPClient::
755get_cookie(const HTTPCookie &cookie) const {
756 Cookies::const_iterator ci = _cookies.find(cookie);
757 if (ci != _cookies.end()) {
758 return (*ci);
759 }
760
761 return HTTPCookie();
762}
763
764/**
765 * Copies all the cookies from the indicated HTTPClient into this one.
766 * Existing cookies in this client are not affected, unless they are shadowed
767 * by the new cookies.
768 */
769void HTTPClient::
770copy_cookies_from(const HTTPClient &other) {
771 Cookies::const_iterator ci;
772 for (ci = other._cookies.begin(); ci != other._cookies.end(); ++ci) {
773 set_cookie(*ci);
774 }
775}
776
777/**
778 * Outputs the complete list of cookies stored on the client, for all domains,
779 * including the expired cookies (which will normally not be sent back to a
780 * host).
781 */
782void HTTPClient::
783write_cookies(std::ostream &out) const {
784 Cookies::const_iterator ci;
785 for (ci = _cookies.begin(); ci != _cookies.end(); ++ci) {
786 out << *ci << "\n";
787 }
788}
789
790/**
791 * Writes to the indicated ostream a "Cookie" header line for sending the
792 * cookies appropriate to the indicated URL along with an HTTP request. This
793 * also removes expired cookies.
794 */
795void HTTPClient::
796send_cookies(std::ostream &out, const URLSpec &url) {
797 HTTPDate now = HTTPDate::now();
798 bool any_expired = false;
799 bool first_cookie = true;
800
801 Cookies::const_iterator ci;
802 for (ci = _cookies.begin(); ci != _cookies.end(); ++ci) {
803 const HTTPCookie &cookie = (*ci);
804 if (cookie.is_expired(now)) {
805 any_expired = true;
806
807 } else if (cookie.matches_url(url)) {
808 if (first_cookie) {
809 out << "Cookie: ";
810 first_cookie = false;
811 } else {
812 out << "; ";
813 }
814 out << cookie.get_name() << "=" << cookie.get_value();
815 }
816 }
817
818 if (!first_cookie) {
819 out << "\r\n";
820 }
821
822 if (any_expired) {
823 Cookies new_cookies;
824 Cookies::const_iterator ci;
825 for (ci = _cookies.begin(); ci != _cookies.end(); ++ci) {
826 const HTTPCookie &cookie = (*ci);
827 if (!cookie.is_expired(now)) {
828 new_cookies.insert(new_cookies.end(), cookie);
829 }
830 }
831 _cookies.swap(new_cookies);
832 }
833}
834
835/**
836 * Attempts to load the certificate named by set_client_certificate_filename()
837 * immediately, and returns true if successful, false otherwise.
838 *
839 * Normally this need not be explicitly called, since it will be called
840 * automatically if the server requests a certificate, but it may be useful to
841 * determine ahead of time if the certificate can be loaded correctly.
842 */
843bool HTTPClient::
844load_client_certificate() {
845 if (!_client_certificate_loaded) {
846 _client_certificate_loaded = true;
847
848 if (!_client_certificate_filename.empty()) {
849 _client_certificate_filename.set_text();
850
851 // First, read the complete file into memory.
853
854 if (!vfs->read_file(_client_certificate_filename,
855 _client_certificate_pem, true)) {
856 // Could not find or read file.
857 downloader_cat.warning()
858 << "Could not read " << _client_certificate_filename << ".\n";
859 return false;
860 }
861 }
862
863 if (!_client_certificate_pem.empty()) {
864 // Create an in-memory BIO to read the "file" from the memory buffer,
865 // and call the low-level routines to read the keys from the BIO.
866 BIO *mbio = BIO_new_mem_buf((void *)_client_certificate_pem.data(),
867 _client_certificate_pem.length());
868
869 ERR_clear_error();
870 _client_certificate_priv =
871 PEM_read_bio_PrivateKey(mbio, nullptr, nullptr,
872 (char *)_client_certificate_passphrase.c_str());
873
874 // Rewind the "file" to the beginning in order to read the public key
875 // (which might appear first in the file).
876 (void)BIO_reset(mbio);
877
878 ERR_clear_error();
879 _client_certificate_pub =
880 PEM_read_bio_X509(mbio, nullptr, nullptr, nullptr);
881
882 BIO_free(mbio);
883
884
885 NotifySeverity sev = NS_debug;
886 string source = "memory";
887 if (!_client_certificate_filename.empty()) {
888 // Only report status to "info" severity if we have read the
889 // certificate from a file. If it came from an in-memory image, a
890 // failure will presumably be handled by whoever set the image.
891 sev = NS_info;
892 source = _client_certificate_filename;
893 }
894
895 if (downloader_cat.is_on(sev)) {
896 if (_client_certificate_priv != nullptr &&
897 _client_certificate_pub != nullptr) {
898 downloader_cat.out(sev)
899 << "Read client certificate from " << source << "\n";
900
901 } else {
902 if (_client_certificate_priv == nullptr) {
903 downloader_cat.out(sev)
904 << "Could not read private key from " << source << "\n";
905 }
906
907 if (_client_certificate_pub == nullptr) {
908 downloader_cat.out(sev)
909 << "Could not read public key from " << source << "\n";
910 }
911 }
912 }
913 }
914 }
915
916 return (_client_certificate_priv != nullptr &&
917 _client_certificate_pub != nullptr);
918}
919
920/**
921 * Adds the certificate defined in the indicated PEM filename as a "pre-
922 * approved" certificate for the indicated server, defined by the hostname and
923 * port (only) from the given URL.
924 *
925 * If the server offers this particular certificate on a secure connection, it
926 * will be accepted without question. This is particularly useful for
927 * communicating with a server using a known self-signed certificate.
928 *
929 * See also the similar add_preapproved_server_certificate_pem(), and the
930 * weaker add_preapproved_server_certificate_name().
931 */
932bool HTTPClient::
933add_preapproved_server_certificate_filename(const URLSpec &url, const Filename &filename) {
935 string pem;
936
937 if (!vfs->read_file(filename, pem, true)) {
938 // Could not find or read file.
939 downloader_cat.warning()
940 << "Could not read " << filename << ".\n";
941 return false;
942 }
943
944 return add_preapproved_server_certificate_pem(url, pem);
945}
946
947/**
948 * Adds the certificate defined in the indicated data string, formatted as a
949 * PEM block, as a "pre-approved" certificate for the indicated server,
950 * defined by the hostname and port (only) from the given URL.
951 *
952 * If the server offers this particular certificate on a secure connection, it
953 * will be accepted without question. This is particularly useful for
954 * communicating with a server using a known self-signed certificate.
955 *
956 * See also the similar add_preapproved_server_certificate_filename(), and the
957 * weaker add_preapproved_server_certificate_name().
958 */
959bool HTTPClient::
960add_preapproved_server_certificate_pem(const URLSpec &url, const string &pem) {
961 // Create an in-memory BIO to read the "file" from the memory buffer, and
962 // call the low-level routine to read the cert from the BIO.
963 BIO *mbio = BIO_new_mem_buf((void *)pem.data(), pem.length());
964
965 ERR_clear_error();
966 X509 *cert = PEM_read_bio_X509(mbio, nullptr, nullptr, nullptr);
967 BIO_free(mbio);
968
969 if (cert == nullptr) {
970 downloader_cat.warning()
971 << "Could not parse PEM data\n";
972 return false;
973 }
974
975 string server_and_port = url.get_server_and_port();
976 PreapprovedServerCerts::iterator psci =
977 _preapproved_server_certs.insert(PreapprovedServerCerts::value_type(server_and_port, PreapprovedServerCert())).first;
978
979 PreapprovedServerCert &psc = (*psci).second;
980 psc._certs.push_back(cert);
981
982 return true;
983}
984
985/**
986 * Adds the certificate *name* only, as a "pre-approved" certificate name for
987 * the indicated server, defined by the hostname and port (only) from the
988 * given URL.
989 *
990 * This is a weaker function than
991 * add_preapproved_server_certificate_filename(). This checks only the
992 * subject name of the certificate, without checking for a particular
993 * certificate by key. This means that a variety of server certificates may
994 * match the indicated name.
995 *
996 * Because this is a weaker verification, it only applies to server
997 * certificates that are signed by a recognized certificate authority. Thus,
998 * it cannot be used to pre-approve self-signed certificates, but it can be
999 * used to accept a server certificate offered by a different hostname than
1000 * the one in the cert itself.
1001 *
1002 * The certificate name should be formatted in the form
1003 * type0=value0/type1=value1/type2=...
1004 */
1005bool HTTPClient::
1006add_preapproved_server_certificate_name(const URLSpec &url, const string &name) {
1007 X509_NAME *cert_name = parse_x509_name(name);
1008 if (cert_name == nullptr) {
1009 downloader_cat.warning()
1010 << "Could not parse certificate name " << name << "\n";
1011 return false;
1012 }
1013
1014 string server_and_port = url.get_server_and_port();
1015 PreapprovedServerCerts::iterator psci =
1016 _preapproved_server_certs.insert(PreapprovedServerCerts::value_type(server_and_port, PreapprovedServerCert())).first;
1017
1018 PreapprovedServerCert &psc = (*psci).second;
1019 psc._cert_names.push_back(cert_name);
1020
1021 return true;
1022}
1023
1024/**
1025 * Removes all preapproved server certificates for the indicated server and
1026 * port.
1027 */
1028void HTTPClient::
1029clear_preapproved_server_certificates(const URLSpec &url) {
1030 string server_and_port = url.get_server_and_port();
1031 _preapproved_server_certs.erase(server_and_port);
1032}
1033
1034/**
1035 * Removes all preapproved server certificates for all servers.
1036 */
1037void HTTPClient::
1038clear_all_preapproved_server_certificates() {
1039 _preapproved_server_certs.clear();
1040}
1041
1042/**
1043 * Returns the current HTTP version setting as a string, e.g. "HTTP/1.0" or
1044 * "HTTP/1.1".
1045 */
1046string HTTPClient::
1047get_http_version_string() const {
1048 switch (_http_version) {
1049 case HTTPEnum::HV_09:
1050 return "HTTP/0.9";
1051
1052 case HTTPEnum::HV_10:
1053 return "HTTP/1.0";
1054
1055 case HTTPEnum::HV_11:
1056 return "HTTP/1.1";
1057
1058 case HTTPEnum::HV_other:
1059 // Report the best we can do.
1060 return "HTTP/1.1";
1061 }
1062
1063 return "unknown";
1064}
1065
1066/**
1067 * Matches the string representing a particular HTTP version against any of
1068 * the known versions and returns the appropriate enumerated value, or
1069 * HV_other if the version is unknown.
1070 */
1071HTTPEnum::HTTPVersion HTTPClient::
1072parse_http_version_string(const string &version) {
1073 if (version == "HTTP/1.0") {
1074 return HTTPEnum::HV_10;
1075 } else if (version == "HTTP/1.1") {
1076 return HTTPEnum::HV_11;
1077 } else if (version.substr(0, 6) == "HTTP/0") {
1078 return HTTPEnum::HV_09;
1079 } else {
1080 return HTTPEnum::HV_other;
1081 }
1082}
1083
1084/**
1085 * Reads the certificate(s) (delimited by -----BEGIN CERTIFICATE----- and
1086 * -----END CERTIFICATE-----) from the indicated file and makes them known as
1087 * trusted public keys for validating future connections. Returns true on
1088 * success, false otherwise.
1089 */
1090bool HTTPClient::
1091load_certificates(const Filename &filename) {
1092 OpenSSLWrapper *sslw = OpenSSLWrapper::get_global_ptr();
1093 return (sslw->load_certificates(filename) != 0);
1094}
1095
1096/**
1097 * Returns a new HTTPChannel object that may be used for reading multiple
1098 * documents using the same connection, for greater network efficiency than
1099 * calling HTTPClient::get_document() repeatedly (which would force a new
1100 * connection for each document).
1101 *
1102 * Also, HTTPChannel has some additional, less common interface methods than
1103 * the basic interface methods that exist on HTTPClient; if you wish to call
1104 * any of these methods you must first obtain an HTTPChannel.
1105 *
1106 * Pass true for persistent_connection to gain this network efficiency. If,
1107 * on the other hand, your intention is to use the channel to retrieve only
1108 * one document, then pass false to inform the server that we will be dropping
1109 * the connection after the first document.
1110 */
1111PT(HTTPChannel) HTTPClient::
1112make_channel(bool persistent_connection) {
1113 PT(HTTPChannel) doc = new HTTPChannel(this);
1114 doc->set_persistent_connection(persistent_connection);
1115 return doc;
1116}
1117
1118/**
1119 * Posts form data to a particular URL and retrieves the response. Returns a
1120 * new HTTPChannel object whether the document is successfully read or not;
1121 * you can test is_valid() and get_return_code() to determine whether the
1122 * document was retrieved.
1123 */
1124PT(HTTPChannel) HTTPClient::
1125post_form(const URLSpec &url, const string &body) {
1126 PT(HTTPChannel) doc = new HTTPChannel(this);
1127 doc->post_form(url, body);
1128 return doc;
1129}
1130
1131/**
1132 * Opens the named document for reading. Returns a new HTTPChannel object
1133 * whether the document is successfully read or not; you can test is_valid()
1134 * and get_return_code() to determine whether the document was retrieved.
1135 */
1136PT(HTTPChannel) HTTPClient::
1137get_document(const URLSpec &url) {
1138 PT(HTTPChannel) doc = new HTTPChannel(this);
1139 doc->get_document(url);
1140 return doc;
1141}
1142
1143/**
1144 * Like get_document(), except only the header associated with the document is
1145 * retrieved. This may be used to test for existence of the document; it
1146 * might also return the size of the document (if the server gives us this
1147 * information).
1148 */
1149PT(HTTPChannel) HTTPClient::
1150get_header(const URLSpec &url) {
1151 PT(HTTPChannel) doc = new HTTPChannel(this);
1152 doc->get_header(url);
1153 return doc;
1154}
1155
1156/**
1157 * Returns the default global HTTPClient.
1158 */
1159HTTPClient *HTTPClient::
1160get_global_ptr() {
1161 if (_global_ptr == nullptr) {
1162 _global_ptr = new HTTPClient;
1163 }
1164 return _global_ptr;
1165}
1166
1167
1168/**
1169 * Returns the OpenSSL context object, creating it first if needed.
1170 */
1171SSL_CTX *HTTPClient::
1172get_ssl_ctx() {
1173 if (_ssl_ctx != nullptr) {
1174 return _ssl_ctx;
1175 }
1176
1177 init_random_seed();
1178
1179 _ssl_ctx = SSL_CTX_new(SSLv23_client_method());
1180
1181#ifndef NDEBUG
1182 // If we have debugging enabled, set a callback that allows us to report the
1183 // SSL messages as they are sent and received.
1184 if (downloader_cat.is_debug()) {
1185 SSL_CTX_set_msg_callback(_ssl_ctx, ssl_msg_callback);
1186 }
1187#endif
1188
1189 // Make sure the error strings are loaded.
1190 OpenSSLWrapper *sslw = OpenSSLWrapper::get_global_ptr();
1191 sslw->notify_ssl_errors();
1192
1193 X509_STORE *store = sslw->get_x509_store();
1194#if OPENSSL_VERSION_NUMBER >= 0x10100000
1195 if (store != nullptr) {
1196 X509_STORE_up_ref(store);
1197 }
1198#endif
1199 SSL_CTX_set_cert_store(_ssl_ctx, store);
1200
1201 return _ssl_ctx;
1202}
1203
1204/**
1205 * Checks to see if the indicated certificate is on the pre-approved list for
1206 * the indicated server's URL.
1207 *
1208 * If the full cert itself (including its key) is on the pre-approved list,
1209 * sets both cert_preapproved and cert_name_preapproved to true.
1210 *
1211 * If the full cert is not on the pre-approved list, but its name matches a
1212 * name on the pre-approved list, sets cert_name_preapproved to true, and
1213 * cert_preapproved to false.
1214 *
1215 * Otherwise, sets both values to false. This doesn't mean the cert is
1216 * necessarily invalid, just that it wasn't on the pre-approved list (which is
1217 * usually empty anyway).
1218 */
1219void HTTPClient::
1220check_preapproved_server_certificate(const URLSpec &url, X509 *cert,
1221 bool &cert_preapproved, bool &cert_name_preapproved) const {
1222 cert_preapproved = false;
1223 cert_name_preapproved = false;
1224
1225 string server_and_port = url.get_server_and_port();
1226 PreapprovedServerCerts::const_iterator psci =
1227 _preapproved_server_certs.find(server_and_port);
1228
1229 if (psci == _preapproved_server_certs.end()) {
1230 // No preapproved certs defined for this server.
1231 return;
1232 }
1233
1234 const PreapprovedServerCert &psc = (*psci).second;
1235
1236 // See if we have an exact match on the cert itself.
1237 ServerCerts::const_iterator sci;
1238 for (sci = psc._certs.begin(); sci != psc._certs.end(); ++sci) {
1239 if (X509_cmp(cert, *sci) == 0) {
1240 cert_preapproved = true;
1241 cert_name_preapproved = true;
1242 downloader_cat.info()
1243 << "Server certificate is pre-approved.\n";
1244 return;
1245 }
1246 }
1247
1248 // OK, look for a match on the cert's name only.
1249 X509_NAME *subject = X509_get_subject_name(cert);
1250 ServerCertNames::const_iterator scni;
1251 for (scni = psc._cert_names.begin(); scni != psc._cert_names.end(); ++scni) {
1252 X509_NAME *cert_name = (*scni);
1253 if (x509_name_subset(cert_name, subject)) {
1254 downloader_cat.info()
1255 << "Server certificate name is pre-approved.\n";
1256 cert_name_preapproved = true;
1257 return;
1258 }
1259 }
1260
1261 // Didn't find any match.
1262 return;
1263}
1264
1265/**
1266 * Adds the proxy servers associated with the indicated scheme, if any, to the
1267 * list. Returns true if any were added, false otherwise.
1268 */
1269bool HTTPClient::
1270get_proxies_for_scheme(const string &scheme, pvector<URLSpec> &proxies) const {
1271 ProxiesByScheme::const_iterator si = _proxies_by_scheme.find(scheme);
1272 if (si == _proxies_by_scheme.end()) {
1273 return false;
1274 }
1275 const Proxies &scheme_proxies = (*si).second;
1276 if (scheme_proxies.empty()) {
1277 return false;
1278 }
1279
1280 Proxies::const_iterator pi;
1281 for (pi = scheme_proxies.begin(); pi != scheme_proxies.end(); ++pi) {
1282 proxies.push_back(*pi);
1283 }
1284
1285 return true;
1286}
1287
1288/**
1289 * Handles a Config definition for http-username as
1290 * server:realm:username:password, where either or both of server and realm
1291 * may be empty, or just server:username:password or username:password.
1292 */
1293void HTTPClient::
1294add_http_username(const string &http_username) {
1295 size_t c1 = http_username.find(':');
1296 if (c1 != string::npos) {
1297 size_t c2 = http_username.find(':', c1 + 1);
1298 if (c2 != string::npos) {
1299 size_t c3 = http_username.find(':', c2 + 1);
1300 if (c3 != string::npos) {
1301 size_t c4 = http_username.find(':', c3 + 1);
1302 if (c4 != string::npos) {
1303 // Oops, we have five? Problem.
1304 downloader_cat.error()
1305 << "Invalid http-username " << http_username << "\n";
1306
1307 } else {
1308 // Ok, we have four.
1309 set_username(http_username.substr(0, c1),
1310 http_username.substr(c1 + 1, c2 - (c1 + 1)),
1311 http_username.substr(c2 + 1));
1312 }
1313
1314 } else {
1315 // We have only three.
1316 set_username(string(),
1317 http_username.substr(0, c1),
1318 http_username.substr(c1 + 1));
1319 }
1320 } else {
1321 // We have only two.
1322 set_username(string(), string(), http_username);
1323 }
1324 } else {
1325 // We have only one? Problem.
1326 downloader_cat.error()
1327 << "Invalid http-username " << http_username << "\n";
1328 }
1329}
1330
1331/**
1332 * Chooses a suitable username:password string for the given URL and realm.
1333 */
1334string HTTPClient::
1335select_username(const URLSpec &url, bool is_proxy, const string &realm) const {
1336 string username;
1337
1338 // Look in several places in order to find the matching username.
1339
1340 // Fist, if there's a username on the URL, that always wins (except when we
1341 // are looking for a proxy username).
1342 if (url.has_username() && !is_proxy) {
1343 username = url.get_username();
1344 }
1345
1346 // Otherwise, start looking on the HTTPClient.
1347 if (is_proxy) {
1348 if (username.empty()) {
1349 // Try the *proxyrealm.
1350 username = get_username("*proxy", realm);
1351 }
1352 if (username.empty()) {
1353 // Then, try *proxyany realm.
1354 username = get_username("*proxy", string());
1355 }
1356 }
1357 if (username.empty()) {
1358 // Try the specific serverrealm.
1359 username = get_username(url.get_server(), realm);
1360 }
1361 if (username.empty()) {
1362 // Then, try the specific serverany realm.
1363 username = get_username(url.get_server(), string());
1364 }
1365 if (username.empty()) {
1366 // Then, try any server with this realm.
1367 username = get_username(string(), realm);
1368 }
1369 if (username.empty()) {
1370 // Then, take the general password.
1371 username = get_username(string(), string());
1372 }
1373
1374 return username;
1375}
1376
1377/**
1378 * Chooses a suitable pre-computed authorization for the indicated URL.
1379 * Returns NULL if no authorization matches.
1380 */
1381HTTPAuthorization *HTTPClient::
1382select_auth(const URLSpec &url, bool is_proxy, const string &last_realm) {
1383 Domains &domains = is_proxy ? _proxy_domains : _www_domains;
1384 string canon = HTTPAuthorization::get_canonical_url(url).get_url();
1385
1386 // Look for the longest domain string that is a prefix of our canonical URL.
1387 // We have to make a linear scan through the list.
1388 Domains::const_iterator best_di = domains.end();
1389 size_t longest_length = 0;
1390 Domains::const_iterator di;
1391 for (di = domains.begin(); di != domains.end(); ++di) {
1392 const string &domain = (*di).first;
1393 size_t length = domain.length();
1394 if (domain == canon.substr(0, length)) {
1395 // This domain string matches. Is it the longest?
1396 if (length > longest_length) {
1397 best_di = di;
1398 longest_length = length;
1399 }
1400 }
1401 }
1402
1403 if (best_di != domains.end()) {
1404 // Ok, we found a matching domain. Use it.
1405 if (downloader_cat.is_spam()) {
1406 downloader_cat.spam()
1407 << "Choosing domain " << (*best_di).first << " for " << url << "\n";
1408 }
1409 const Realms &realms = (*best_di).second._realms;
1410 // First, try our last realm.
1411 Realms::const_iterator ri;
1412 ri = realms.find(last_realm);
1413 if (ri != realms.end()) {
1414 return (*ri).second;
1415 }
1416
1417 if (!realms.empty()) {
1418 // Oh well, just return the first realm.
1419 return (*realms.begin()).second;
1420 }
1421 }
1422
1423 // No matching domains.
1424 return nullptr;
1425}
1426
1427/**
1428 * Generates a new authorization entry in response to a 401 or 407 challenge
1429 * from the server or proxy. The new authorization entry is stored for future
1430 * connections to the same server (or, more precisely, the same domain, which
1431 * may be a subset of the server, or it may include multiple servers).
1432 */
1433PT(HTTPAuthorization) HTTPClient::
1434generate_auth(const URLSpec &url, bool is_proxy, const string &challenge) {
1435 HTTPAuthorization::AuthenticationSchemes schemes;
1436 HTTPAuthorization::parse_authentication_schemes(schemes, challenge);
1437
1438 PT(HTTPAuthorization) auth;
1439 HTTPAuthorization::AuthenticationSchemes::iterator si;
1440
1441 si = schemes.find("digest");
1442 if (si != schemes.end()) {
1443 auth = new HTTPDigestAuthorization((*si).second, url, is_proxy);
1444 }
1445
1446 if (auth == nullptr || !auth->is_valid()) {
1447 si = schemes.find("basic");
1448 if (si != schemes.end()) {
1449 auth = new HTTPBasicAuthorization((*si).second, url, is_proxy);
1450 }
1451 }
1452
1453 if (auth == nullptr || !auth->is_valid()) {
1454 downloader_cat.warning()
1455 << "Don't know how to use any of the server's available authorization schemes:\n";
1456 for (si = schemes.begin(); si != schemes.end(); ++si) {
1457 downloader_cat.warning() << (*si).first << "\n";
1458 }
1459
1460 } else {
1461 // Now that we've got an authorization, store it under under each of its
1462 // suggested domains for future use.
1463 Domains &domains = is_proxy ? _proxy_domains : _www_domains;
1464 const vector_string &domain = auth->get_domain();
1465 vector_string::const_iterator si;
1466 for (si = domain.begin(); si != domain.end(); ++si) {
1467 domains[(*si)]._realms[auth->get_realm()] = auth;
1468 }
1469 }
1470
1471 return auth;
1472}
1473
1474/**
1475 * Frees the resources allocated by a previous call to
1476 * load_client_certificate(), and marks the certificate unloaded.
1477 */
1478void HTTPClient::
1479unload_client_certificate() {
1480 if (_client_certificate_priv != nullptr) {
1481 EVP_PKEY_free(_client_certificate_priv);
1482 _client_certificate_priv = nullptr;
1483 }
1484
1485 if (_client_certificate_pub != nullptr) {
1486 X509_free(_client_certificate_pub);
1487 _client_certificate_pub = nullptr;
1488 }
1489
1490 _client_certificate_loaded = false;
1491}
1492
1493/**
1494 * Parses a string of the form type0=value0/type1=value1/type2=... into a
1495 * newly allocated X509_NAME object. Returns NULL if the string is invalid.
1496 */
1497X509_NAME *HTTPClient::
1498parse_x509_name(const string &source) {
1499 X509_NAME *result = nullptr;
1500
1501 result = X509_NAME_new();
1502 bool added_any = false;
1503
1504 string::const_iterator si;
1505 si = source.begin();
1506 while (si != source.end()) {
1507 if ((*si) == '/') {
1508 // Skip a slash delimiter.
1509 ++si;
1510 } else {
1511 string type;
1512 while (si != source.end() && (*si) != '=' && (*si) != '/') {
1513 if ((*si) == '\\') {
1514 ++si;
1515 if (si != source.end()) {
1516 type += (*si);
1517 ++si;
1518 }
1519 } else {
1520 type += (*si);
1521 ++si;
1522 }
1523 }
1524
1525 int nid = OBJ_txt2nid((char *)type.c_str());
1526 if (nid == NID_undef) {
1527 downloader_cat.info()
1528 << "Unknown type " << type << " in X509 name: " << source
1529 << "\n";
1530 X509_NAME_free(result);
1531 return nullptr;
1532 }
1533
1534 string value;
1535
1536 if (si != source.end() && (*si) == '=') {
1537 ++si;
1538 while (si != source.end() && (*si) != '/') {
1539 if ((*si) == '\\') {
1540 ++si;
1541 if (si != source.end()) {
1542 value += (*si);
1543 ++si;
1544 }
1545 } else {
1546 value += (*si);
1547 ++si;
1548 }
1549 }
1550 }
1551
1552 if (!value.empty()) {
1553 int add_result =
1554 X509_NAME_add_entry_by_NID(result, nid, V_ASN1_APP_CHOOSE,
1555 (unsigned char *)value.c_str(), -1, -1, 0);
1556 if (!add_result) {
1557 downloader_cat.info()
1558 << "Unable to add " << type << "=" << value << " in X509 name: "
1559 << source << "\n";
1560 X509_NAME_free(result);
1561 return nullptr;
1562 }
1563 added_any = true;
1564 }
1565 }
1566 }
1567
1568 if (!added_any) {
1569 downloader_cat.info()
1570 << "Invalid empty X509 name: " << source << "\n";
1571 X509_NAME_free(result);
1572 return nullptr;
1573 }
1574
1575 return result;
1576}
1577
1578/**
1579 * Returns true if name_a is a subset of name_b: each property of name_a is
1580 * defined in name_b, and the defined value is equivalent to that of name_a.
1581 */
1582bool HTTPClient::
1583x509_name_subset(X509_NAME *name_a, X509_NAME *name_b) {
1584 int count_a = X509_NAME_entry_count(name_a);
1585 for (int ai = 0; ai < count_a; ai++) {
1586 X509_NAME_ENTRY *na = X509_NAME_get_entry(name_a, ai);
1587
1588 int bi = X509_NAME_get_index_by_OBJ(name_b, X509_NAME_ENTRY_get_object(na), -1);
1589 if (bi < 0) {
1590 // This entry in name_a is not defined in name_b.
1591 return false;
1592 }
1593
1594 X509_NAME_ENTRY *nb = X509_NAME_get_entry(name_b, bi);
1595 ASN1_STRING *na_value = X509_NAME_ENTRY_get_data(na);
1596 ASN1_STRING *nb_value = X509_NAME_ENTRY_get_data(nb);
1597 if (na_value->length != nb_value->length ||
1598 memcmp(na_value->data, nb_value->data, na_value->length) != 0) {
1599 // This entry in name_a doesn't match that of name_b.
1600 return false;
1601 }
1602 }
1603 return true;
1604}
1605
1606/**
1607 * Puts the first word of c into a, and the remainder into b.
1608 */
1609void HTTPClient::
1610split_whitespace(string &a, string &b, const string &c) {
1611 size_t p = 0;
1612
1613 // Skip initial whitespace
1614 while (p < c.length() && isspace(c[p])) {
1615 ++p;
1616 }
1617
1618 // Find the length of the first word
1619 size_t q = p;
1620 while (p < c.length() && !isspace(c[p])) {
1621 ++p;
1622 }
1623
1624 a = c.substr(q, p - q);
1625
1626 // Skip whitespace between words
1627 while (p < c.length() && isspace(c[p])) {
1628 ++p;
1629 }
1630 b = c.substr(p);
1631}
1632
1633/**
1634 *
1635 */
1636HTTPClient::PreapprovedServerCert::
1637~PreapprovedServerCert() {
1638 ServerCerts::const_iterator sci;
1639 for (sci = _certs.begin(); sci != _certs.end(); ++sci) {
1640 X509_free(*sci);
1641 }
1642
1643 ServerCertNames::const_iterator scni;
1644 for (scni = _cert_names.begin(); scni != _cert_names.end(); ++scni) {
1645 X509_NAME_free(*scni);
1646 }
1647}
1648
1649#endif // HAVE_OPENSSL
This is a convenience class to specialize ConfigVariable as a boolean type.
This is a convenience class to specialize ConfigVariable as a Filename type.
This class is similar to ConfigVariable, but it reports its value as a list of strings.
This is a convenience class to specialize ConfigVariable as a string type.
The name of a file, such as a texture file or an Egg file.
Definition filename.h:44
This class can be used to test for string matches against standard Unix- shell filename globbing conv...
Definition globPattern.h:32
get_pattern
Returns the pattern string that the GlobPattern object matches.
Definition globPattern.h:44
A container for an HTTP-legal time/date indication.
Definition httpDate.h:27
static HTTPDate now()
Returns an HTTPDate that represents the current time and date.
Definition httpDate.I:47
A container for a URL, e.g.
Definition urlSpec.h:28
bool has_username() const
Returns true if the URL specifies a username (and/or password), false otherwise.
Definition urlSpec.I:77
bool empty() const
Returns false if the URLSpec is valid (not empty), or true if it is an empty string.
Definition urlSpec.I:209
get_username
Returns the username specified by the URL, if any.
Definition urlSpec.h:95
const std::string & get_url() const
Returns the complete URL specification.
Definition urlSpec.I:184
get_server
Returns the server name specified by the URL, if any.
Definition urlSpec.h:96
get_server_and_port
Returns a string consisting of the server name, followed by a colon, followed by the port number.
Definition urlSpec.h:98
get_scheme
Returns the scheme specified by the URL, or empty string if no scheme is specified.
Definition urlSpec.h:93
is_ssl
Returns true if the URL's scheme specifies an SSL-secured protocol such as https, or false otherwise.
Definition urlSpec.h:101
A hierarchy of directories and files that appears to be one continuous file system,...
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
This is our own Panda specialization on the default STL set.
Definition pset.h:49
This is our own Panda specialization on the default STL vector.
Definition pvector.h:42
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void tokenize(const string &str, vector_string &words, const string &delimiters, bool discard_repeated_delimiters)
Chops the source string up into pieces delimited by any of the characters specified in delimiters.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.