]>
Commit | Line | Data |
---|---|---|
bac41a7b A |
1 | /* |
2 | * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved. | |
3 | * | |
4 | * The contents of this file constitute Original Code as defined in and are | |
5 | * subject to the Apple Public Source License Version 1.2 (the 'License'). | |
6 | * You may not use this file except in compliance with the License. Please obtain | |
7 | * a copy of the License at http://www.apple.com/publicsource and read it before | |
8 | * using this file. | |
9 | * | |
10 | * This Original Code and all software distributed under the License are | |
11 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS | |
12 | * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT | |
13 | * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR | |
14 | * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the | |
15 | * specific language governing rights and limitations under the License. | |
16 | */ | |
17 | ||
18 | ||
19 | // | |
20 | // https-proxy - CONNECT style transparent proxy connection to SSL host. | |
21 | // | |
22 | // This is the CONNECT method of an ordinary (proxying) HTTP server. | |
23 | // Once it switches a connection to transparent proxying, there's no way to get out | |
24 | // again. Hence, our Connection objects belong to the remote host, not the proxy. | |
25 | // | |
26 | #include "https-proxy-protocol.h" | |
27 | #include "netparameters.h" | |
28 | ||
29 | ||
30 | namespace Security { | |
31 | namespace Network { | |
32 | ||
33 | ||
34 | // | |
35 | // Construct the protocol object | |
36 | // | |
37 | ConnectHTTPProtocol::ConnectHTTPProtocol(Manager &mgr, const HostTarget &proxy) | |
38 | : SecureHTTPProtocol(mgr), host(proxy.defaultPort(defaultHttpPort)) | |
39 | { | |
40 | } | |
41 | ||
42 | ||
43 | // | |
44 | // Create a Transfer object for our protocol | |
45 | // | |
46 | ConnectHTTPProtocol::ConnectHTTPTransfer *ConnectHTTPProtocol::makeTransfer(const Target &target, | |
47 | Operation operation) | |
48 | { | |
49 | return new ConnectHTTPTransfer(*this, target, operation, defaultHttpsPort); | |
50 | } | |
51 | ||
52 | ||
53 | // | |
54 | // Construct an HTTPConnection object | |
55 | // | |
56 | ConnectHTTPProtocol::ConnectHTTPConnection::ConnectHTTPConnection(Protocol &proto, | |
57 | const HostTarget &hostTarget) | |
58 | : SecureHTTPConnection(proto, hostTarget) | |
59 | ||
60 | { | |
61 | // SecureHTTPConnection already set up everything for talking to the server | |
62 | connectState = connectConnecting; | |
63 | deferStartSSL = true; // tell parent protocol to break on connect-complete | |
64 | } | |
65 | ||
66 | ConnectHTTPProtocol::ConnectHTTPConnection::~ConnectHTTPConnection() | |
67 | { | |
68 | } | |
69 | ||
70 | ||
71 | // | |
72 | // Start a connection request | |
73 | // | |
74 | void ConnectHTTPProtocol::ConnectHTTPConnection::connectRequest() | |
75 | { | |
76 | switch (connectState) { | |
77 | case connectConnecting: | |
78 | return; // still waiting for TCP | |
79 | case connectStartup: | |
80 | { | |
81 | const HostTarget &host = target().host; | |
82 | flushOutput(false); // hold output | |
83 | printfe("CONNECT %s:%d HTTP/1.1", | |
84 | host.host().name().c_str(), target().host.port()); | |
85 | hostHeader(); | |
86 | authorizationHeader("Proxy-Authorization", hostTarget, | |
87 | kNetworkGenericProxyUsername, kNetworkGenericProxyPassword); | |
88 | printfe(""); // end of headers | |
89 | flushOutput(); // flush accumulated output | |
90 | mode(lineInput); | |
91 | connectState = connectPrimaryResponse; | |
92 | } | |
93 | break; | |
94 | case connectReady: // already set; go ahead next layer (https) | |
95 | sslRequest(); | |
96 | break; | |
97 | default: | |
98 | assert(false); // huh? | |
99 | } | |
100 | } | |
101 | ||
102 | ||
103 | // | |
104 | // Our state transit method controls only the initial SSL handshake. | |
105 | // Think of it as a "prefix" to the HTTP protocol state engine. Once the handshake | |
106 | // is complete, we hand off further state management to the HTTP machine. | |
107 | // | |
108 | void ConnectHTTPProtocol::ConnectHTTPConnection::transit(Event event, | |
109 | char *input, size_t inputLength) | |
110 | { | |
111 | if (event == endOfInput && connectState != connectReady) | |
112 | UnixError::throwMe(ECONNRESET); // @@@ diagnostic? | |
113 | ||
114 | switch (connectState) { | |
115 | case connectConnecting: | |
116 | SecureHTTPConnection::transit(event, input, inputLength); | |
117 | if (SecureHTTPConnection::sslState == sslStartup) { // transport level ready | |
118 | connectState = connectStartup; | |
119 | connectRequest(); | |
120 | } | |
121 | return; | |
122 | case connectPrimaryResponse: | |
123 | { | |
124 | // sketchily read proxy's primary response | |
125 | int major, minor, code; | |
126 | if (sscanf(input, "HTTP/%d.%d %u", &major, &minor, &code) != 3) { | |
127 | fail(input); // malformed response header | |
128 | } | |
129 | if (major != 1 || minor < 0 || minor > 1) | |
130 | fail(input); | |
131 | switch (code) { | |
132 | case 200: // okay, proceed | |
133 | connectState = connectReadHeaders; | |
134 | break; | |
135 | default: // this didn't work | |
136 | transfer().httpResponse() = input; // won't have a better one | |
137 | fail(input); | |
138 | } | |
139 | } | |
140 | break; | |
141 | case connectReadHeaders: | |
142 | { | |
143 | if (inputLength) { | |
144 | headers().add(input); | |
145 | } else { | |
146 | // end of proxy headers: start SSL now | |
147 | connectState = connectReady; | |
148 | try { | |
149 | startSSL(); | |
29654253 | 150 | } catch (const CssmCommonError &err) { |
bac41a7b A |
151 | setError("SSL failed", err.osStatus()); |
152 | throw; | |
153 | } catch (...) { | |
154 | setError("SSL failed"); | |
155 | throw; | |
156 | } | |
157 | } | |
158 | } | |
159 | break; | |
160 | case connectReady: | |
161 | return SecureHTTPConnection::transit(event, input, inputLength); | |
162 | default: | |
163 | assert(false); // huh? | |
164 | } | |
165 | } | |
166 | ||
167 | ||
168 | // | |
169 | // HTTPS Transfer objects. | |
170 | // | |
171 | ConnectHTTPProtocol::ConnectHTTPTransfer::ConnectHTTPTransfer(Protocol &proto, | |
172 | const Target &tgt, Operation operation, IPPort defPort) | |
173 | : SecureHTTPTransfer(proto, tgt, operation, defPort) | |
174 | { | |
175 | } | |
176 | ||
177 | void ConnectHTTPProtocol::ConnectHTTPTransfer::start() | |
178 | { | |
179 | ConnectHTTPConnection *connection = | |
180 | protocol.manager.findConnection<ConnectHTTPConnection>(target); | |
181 | if (connection == NULL) | |
182 | connection = new ConnectHTTPConnection(protocol, target); | |
183 | connection->dock(this); | |
184 | connection->connectRequest(); | |
185 | } | |
186 | ||
187 | ||
188 | // | |
189 | // Even though this is formally a proxy protocol, we should not use | |
190 | // proxy headers, since the proxy is transparent and the remote system | |
191 | // expects a direct request. | |
192 | // | |
193 | bool ConnectHTTPProtocol::ConnectHTTPTransfer::useProxyHeaders() const | |
194 | { | |
195 | return false; | |
196 | } | |
197 | ||
198 | ||
199 | // | |
200 | // We are a proxy protocol | |
201 | // | |
202 | bool ConnectHTTPProtocol::isProxy() const | |
203 | { return true; } | |
204 | ||
205 | const HostTarget &ConnectHTTPProtocol::proxyHost() const | |
206 | { return host; } | |
207 | ||
208 | ||
209 | } // end namespace Network | |
210 | } // end namespace Security |