1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef NET_PROXY_RESOLUTION_DHCP_PAC_FILE_ADAPTER_FETCHER_WIN_H_
6 #define NET_PROXY_RESOLUTION_DHCP_PAC_FILE_ADAPTER_FETCHER_WIN_H_
7 
8 #include <stddef.h>
9 
10 #include <memory>
11 #include <string>
12 
13 #include "base/macros.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/strings/string16.h"
17 #include "base/threading/thread_checker.h"
18 #include "base/timer/timer.h"
19 #include "net/base/completion_once_callback.h"
20 #include "net/base/net_export.h"
21 #include "net/traffic_annotation/network_traffic_annotation.h"
22 #include "url/gurl.h"
23 
24 namespace base {
25 class TaskRunner;
26 }
27 
28 namespace net {
29 
30 class PacFileFetcher;
31 class URLRequestContext;
32 
33 // For a given adapter, this class takes care of first doing a DHCP lookup
34 // to get the PAC URL, then if there is one, trying to fetch it.
35 class NET_EXPORT_PRIVATE DhcpPacFileAdapterFetcher
36     : public base::SupportsWeakPtr<DhcpPacFileAdapterFetcher> {
37  public:
38   // |url_request_context| must outlive DhcpPacFileAdapterFetcher.
39   // |task_runner| will be used to post tasks to a thread.
40   DhcpPacFileAdapterFetcher(URLRequestContext* url_request_context,
41                             scoped_refptr<base::TaskRunner> task_runner);
42   virtual ~DhcpPacFileAdapterFetcher();
43 
44   // Starts a fetch.  On completion (but not cancellation), |callback|
45   // will be invoked with the network error indicating success or failure
46   // of fetching a DHCP-configured PAC file on this adapter.
47   //
48   // On completion, results can be obtained via |GetPacScript()|, |GetPacURL()|.
49   //
50   // You may only call Fetch() once on a given instance of
51   // DhcpPacFileAdapterFetcher.
52   virtual void Fetch(const std::string& adapter_name,
53                      CompletionOnceCallback callback,
54                      const NetworkTrafficAnnotationTag traffic_annotation);
55 
56   // Cancels the fetch on this adapter.
57   virtual void Cancel();
58 
59   // Returns true if in the FINISH state (not CANCEL).
60   virtual bool DidFinish() const;
61 
62   // Returns the network error indicating the result of the fetch. Will
63   // return IO_PENDING until the fetch is complete or cancelled. This is
64   // the same network error passed to the |callback| provided to |Fetch()|.
65   virtual int GetResult() const;
66 
67   // Returns the contents of the PAC file retrieved.  Only valid if
68   // |IsComplete()| is true.  Returns the empty string if |GetResult()|
69   // returns anything other than OK.
70   virtual base::string16 GetPacScript() const;
71 
72   // Returns the PAC URL retrieved from DHCP.  Only guaranteed to be
73   // valid if |IsComplete()| is true.  Returns an empty URL if no URL was
74   // configured in DHCP.  May return a valid URL even if |result()| does
75   // not return OK (this would indicate that we found a URL configured in
76   // DHCP but failed to download it).
77   virtual GURL GetPacURL() const;
78 
79   // Returns the PAC URL configured in DHCP for the given |adapter_name|, or
80   // the empty string if none is configured.
81   //
82   // This function executes synchronously due to limitations of the Windows
83   // DHCP client API.
84   static std::string GetPacURLFromDhcp(const std::string& adapter_name);
85 
86   // Sanitizes a string returned via the DHCP API.
87   static std::string SanitizeDhcpApiString(const char* data,
88                                            size_t count_bytes);
89 
90  protected:
91   // This is the state machine for fetching from a given adapter.
92   //
93   // The state machine goes from START->WAIT_DHCP when it starts
94   // a worker thread to fetch the PAC URL from DHCP.
95   //
96   // In state WAIT_DHCP, if the DHCP query finishes and has no URL, it
97   // moves to state FINISH.  If there is a URL, it starts a
98   // PacFileFetcher to fetch it and moves to state WAIT_URL.
99   //
100   // It goes from WAIT_URL->FINISH when the PacFileFetcher completes.
101   //
102   // In state FINISH, completion is indicated to the outer class, with
103   // the results of the fetch if a PAC script was successfully fetched.
104   //
105   // In state WAIT_DHCP, our timeout occurring can push us to FINISH.
106   //
107   // In any state except FINISH, a call to Cancel() will move to state
108   // CANCEL and cause all outstanding work to be cancelled or its
109   // results ignored when available.
110   enum State {
111     STATE_START,
112     STATE_WAIT_DHCP,
113     STATE_WAIT_URL,
114     STATE_FINISH,
115     STATE_CANCEL,
116   };
117 
118   State state() const;
119 
120   // This inner class encapsulates work done on a worker pool thread.
121   // By using a separate object, we can keep the main object completely
122   // thread safe and let it be non-refcounted.
123   class NET_EXPORT_PRIVATE DhcpQuery
124       : public base::RefCountedThreadSafe<DhcpQuery> {
125    public:
126     DhcpQuery();
127 
128     // This method should run on a worker pool thread, via PostTaskAndReply.
129     // After it has run, the |url()| method on this object will return the
130     // URL retrieved.
131     void GetPacURLForAdapter(const std::string& adapter_name);
132 
133     // Returns the URL retrieved for the given adapter, once the task has run.
134     const std::string& url() const;
135 
136    protected:
137     // Virtual method introduced to allow unit testing.
138     virtual std::string ImplGetPacURLFromDhcp(const std::string& adapter_name);
139 
140     friend class base::RefCountedThreadSafe<DhcpQuery>;
141     virtual ~DhcpQuery();
142 
143    private:
144     // The URL retrieved for the given adapter.
145     std::string url_;
146 
147     DISALLOW_COPY_AND_ASSIGN(DhcpQuery);
148   };
149 
150   // Virtual methods introduced to allow unit testing.
151   virtual std::unique_ptr<PacFileFetcher> ImplCreateScriptFetcher();
152   virtual DhcpQuery* ImplCreateDhcpQuery();
153   virtual base::TimeDelta ImplGetTimeout() const;
154 
155  private:
156   // Event/state transition handlers
157   void OnDhcpQueryDone(scoped_refptr<DhcpQuery> dhcp_query,
158                        const NetworkTrafficAnnotationTag traffic_annotation);
159   void OnTimeout();
160   void OnFetcherDone(int result);
161   void TransitionToFinish();
162 
163   // TaskRunner for posting tasks to a worker thread.
164   scoped_refptr<base::TaskRunner> task_runner_;
165 
166   // Current state of this state machine.
167   State state_;
168 
169   // A network error indicating result of operation.
170   int result_;
171 
172   // Empty string or the PAC script downloaded.
173   base::string16 pac_script_;
174 
175   // Empty URL or the PAC URL configured in DHCP.
176   GURL pac_url_;
177 
178   // Callback to let our client know we're done. Invalid in states
179   // START, FINISH and CANCEL.
180   CompletionOnceCallback callback_;
181 
182   // Fetcher to retrieve PAC files once URL is known.
183   std::unique_ptr<PacFileFetcher> script_fetcher_;
184 
185   // Implements a timeout on the call to the Win32 DHCP API.
186   base::OneShotTimer wait_timer_;
187 
188   URLRequestContext* const url_request_context_;
189 
190   THREAD_CHECKER(thread_checker_);
191 
192   DISALLOW_IMPLICIT_CONSTRUCTORS(DhcpPacFileAdapterFetcher);
193 };
194 
195 }  // namespace net
196 
197 #endif  // NET_PROXY_RESOLUTION_DHCP_PAC_FILE_ADAPTER_FETCHER_WIN_H_
198