xref: /aosp_15_r20/external/cronet/net/proxy_resolution/win/proxy_resolver_winhttp.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2011 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/proxy_resolution/win/proxy_resolver_winhttp.h"
6 
7 #include <windows.h>
8 
9 #include <winhttp.h>
10 
11 #include <memory>
12 
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "net/base/net_errors.h"
16 #include "net/proxy_resolution/proxy_info.h"
17 #include "net/proxy_resolution/proxy_resolver.h"
18 #include "url/gurl.h"
19 
20 using base::TimeTicks;
21 
22 namespace net {
23 namespace {
24 
FreeInfo(WINHTTP_PROXY_INFO * info)25 static void FreeInfo(WINHTTP_PROXY_INFO* info) {
26   if (info->lpszProxy)
27     GlobalFree(info->lpszProxy);
28   if (info->lpszProxyBypass)
29     GlobalFree(info->lpszProxyBypass);
30 }
31 
WinHttpErrorToNetError(DWORD win_http_error)32 static Error WinHttpErrorToNetError(DWORD win_http_error) {
33   switch (win_http_error) {
34     case ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR:
35     case ERROR_WINHTTP_INTERNAL_ERROR:
36     case ERROR_WINHTTP_INCORRECT_HANDLE_TYPE:
37       return ERR_FAILED;
38     case ERROR_WINHTTP_LOGIN_FAILURE:
39       return ERR_PROXY_AUTH_UNSUPPORTED;
40     case ERROR_WINHTTP_BAD_AUTO_PROXY_SCRIPT:
41       return ERR_PAC_SCRIPT_FAILED;
42     case ERROR_WINHTTP_INVALID_URL:
43     case ERROR_WINHTTP_OPERATION_CANCELLED:
44     case ERROR_WINHTTP_UNABLE_TO_DOWNLOAD_SCRIPT:
45     case ERROR_WINHTTP_UNRECOGNIZED_SCHEME:
46       return ERR_HTTP_RESPONSE_CODE_FAILURE;
47     case ERROR_NOT_ENOUGH_MEMORY:
48       return ERR_INSUFFICIENT_RESOURCES;
49     default:
50       return ERR_FAILED;
51   }
52 }
53 
54 class ProxyResolverWinHttp : public ProxyResolver {
55  public:
56   ProxyResolverWinHttp(const scoped_refptr<PacFileData>& script_data);
57 
58   ProxyResolverWinHttp(const ProxyResolverWinHttp&) = delete;
59   ProxyResolverWinHttp& operator=(const ProxyResolverWinHttp&) = delete;
60 
61   ~ProxyResolverWinHttp() override;
62 
63   // ProxyResolver implementation:
64   int GetProxyForURL(const GURL& url,
65                      const NetworkAnonymizationKey& network_anymization_key,
66                      ProxyInfo* results,
67                      CompletionOnceCallback /*callback*/,
68                      std::unique_ptr<Request>* /*request*/,
69                      const NetLogWithSource& /*net_log*/) override;
70 
71  private:
72   bool OpenWinHttpSession();
73   void CloseWinHttpSession();
74 
75   // Proxy configuration is cached on the session handle.
76   HINTERNET session_handle_ = nullptr;
77 
78   const GURL pac_url_;
79 };
80 
ProxyResolverWinHttp(const scoped_refptr<PacFileData> & script_data)81 ProxyResolverWinHttp::ProxyResolverWinHttp(
82     const scoped_refptr<PacFileData>& script_data)
83     : pac_url_(script_data->type() == PacFileData::TYPE_AUTO_DETECT
84                    ? GURL("http://wpad/wpad.dat")
85                    : script_data->url()) {}
86 
~ProxyResolverWinHttp()87 ProxyResolverWinHttp::~ProxyResolverWinHttp() {
88   CloseWinHttpSession();
89 }
90 
GetProxyForURL(const GURL & query_url,const NetworkAnonymizationKey & network_anonymization_key,ProxyInfo * results,CompletionOnceCallback,std::unique_ptr<Request> *,const NetLogWithSource &)91 int ProxyResolverWinHttp::GetProxyForURL(
92     const GURL& query_url,
93     const NetworkAnonymizationKey& network_anonymization_key,
94     ProxyInfo* results,
95     CompletionOnceCallback /*callback*/,
96     std::unique_ptr<Request>* /*request*/,
97     const NetLogWithSource& /*net_log*/) {
98   // If we don't have a WinHTTP session, then create a new one.
99   if (!session_handle_ && !OpenWinHttpSession())
100     return ERR_FAILED;
101 
102   // Windows' system resolver does not support WebSocket URLs in proxy.pac. This
103   // was tested in version 10.0.16299, and is also implied by the description of
104   // the ERROR_WINHTTP_UNRECOGNIZED_SCHEME error code in the Microsoft
105   // documentation at
106   // https://docs.microsoft.com/en-us/windows/desktop/api/winhttp/nf-winhttp-winhttpgetproxyforurl.
107   // See https://crbug.com/862121.
108   GURL mutable_query_url = query_url;
109   if (query_url.SchemeIsWSOrWSS()) {
110     GURL::Replacements replacements;
111     replacements.SetSchemeStr(query_url.SchemeIsCryptographic() ? "https"
112                                                                 : "http");
113     mutable_query_url = query_url.ReplaceComponents(replacements);
114   }
115 
116   // If we have been given an empty PAC url, then use auto-detection.
117   //
118   // NOTE: We just use DNS-based auto-detection here like Firefox.  We do this
119   // to avoid WinHTTP's auto-detection code, which while more featureful (it
120   // supports DHCP based auto-detection) also appears to have issues.
121   //
122   WINHTTP_AUTOPROXY_OPTIONS options = {0};
123   options.fAutoLogonIfChallenged = FALSE;
124   options.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
125   std::u16string pac_url16 = base::ASCIIToUTF16(pac_url_.spec());
126   options.lpszAutoConfigUrl = base::as_wcstr(pac_url16);
127 
128   WINHTTP_PROXY_INFO info = {0};
129   DCHECK(session_handle_);
130 
131   // Per http://msdn.microsoft.com/en-us/library/aa383153(VS.85).aspx, it is
132   // necessary to first try resolving with fAutoLogonIfChallenged set to false.
133   // Otherwise, we fail over to trying it with a value of true.  This way we
134   // get good performance in the case where WinHTTP uses an out-of-process
135   // resolver.  This is important for Vista and Win2k3.
136   BOOL ok = WinHttpGetProxyForUrl(
137       session_handle_,
138       base::as_wcstr(base::ASCIIToUTF16(mutable_query_url.spec())), &options,
139       &info);
140   if (!ok) {
141     if (ERROR_WINHTTP_LOGIN_FAILURE == GetLastError()) {
142       options.fAutoLogonIfChallenged = TRUE;
143       ok = WinHttpGetProxyForUrl(
144           session_handle_,
145           base::as_wcstr(base::ASCIIToUTF16(mutable_query_url.spec())),
146           &options, &info);
147     }
148     if (!ok) {
149       DWORD error = GetLastError();
150       // If we got here because of RPC timeout during out of process PAC
151       // resolution, no further requests on this session are going to work.
152       if (ERROR_WINHTTP_TIMEOUT == error ||
153           ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR == error) {
154         CloseWinHttpSession();
155       }
156       return WinHttpErrorToNetError(error);
157     }
158   }
159 
160   int rv = OK;
161 
162   switch (info.dwAccessType) {
163     case WINHTTP_ACCESS_TYPE_NO_PROXY:
164       results->UseDirect();
165       break;
166     case WINHTTP_ACCESS_TYPE_NAMED_PROXY:
167       // According to MSDN:
168       //
169       // The proxy server list contains one or more of the following strings
170       // separated by semicolons or whitespace.
171       //
172       // ([<scheme>=][<scheme>"://"]<server>[":"<port>])
173       //
174       // Based on this description, ProxyInfo::UseNamedProxy() isn't
175       // going to handle all the variations (in particular <scheme>=).
176       //
177       // However in practice, it seems that WinHTTP is simply returning
178       // things like "foopy1:80;foopy2:80". It strips out the non-HTTP
179       // proxy types, and stops the list when PAC encounters a "DIRECT".
180       // So UseNamedProxy() should work OK.
181       results->UseNamedProxy(base::WideToUTF8(info.lpszProxy));
182       break;
183     default:
184       NOTREACHED();
185       rv = ERR_FAILED;
186   }
187 
188   FreeInfo(&info);
189   return rv;
190 }
191 
OpenWinHttpSession()192 bool ProxyResolverWinHttp::OpenWinHttpSession() {
193   DCHECK(!session_handle_);
194   session_handle_ =
195       WinHttpOpen(nullptr, WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME,
196                   WINHTTP_NO_PROXY_BYPASS, 0);
197   if (!session_handle_)
198     return false;
199 
200   // Since this session handle will never be used for WinHTTP connections,
201   // these timeouts don't really mean much individually.  However, WinHTTP's
202   // out of process PAC resolution will use a combined (sum of all timeouts)
203   // value to wait for an RPC reply.
204   BOOL rv = WinHttpSetTimeouts(session_handle_, 10000, 10000, 5000, 5000);
205   DCHECK(rv);
206 
207   return true;
208 }
209 
CloseWinHttpSession()210 void ProxyResolverWinHttp::CloseWinHttpSession() {
211   if (session_handle_) {
212     WinHttpCloseHandle(session_handle_);
213     session_handle_ = nullptr;
214   }
215 }
216 
217 }  // namespace
218 
ProxyResolverFactoryWinHttp()219 ProxyResolverFactoryWinHttp::ProxyResolverFactoryWinHttp()
220     : ProxyResolverFactory(false /*expects_pac_bytes*/) {
221 }
222 
CreateProxyResolver(const scoped_refptr<PacFileData> & pac_script,std::unique_ptr<ProxyResolver> * resolver,CompletionOnceCallback callback,std::unique_ptr<Request> * request)223 int ProxyResolverFactoryWinHttp::CreateProxyResolver(
224     const scoped_refptr<PacFileData>& pac_script,
225     std::unique_ptr<ProxyResolver>* resolver,
226     CompletionOnceCallback callback,
227     std::unique_ptr<Request>* request) {
228   *resolver = std::make_unique<ProxyResolverWinHttp>(pac_script);
229   return OK;
230 }
231 
232 }  // namespace net
233