source: UsbWattMeter/trunk/curl-7.47.1/lib/http_proxy.c@ 167

Last change on this file since 167 was 167, checked in by coas-nagasima, 8 years ago

MIMEにSJISを設定

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
  • Property svn:mime-type set to text/x-csrc; charset=SHIFT_JIS
File size: 21.9 KB
Line 
1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.haxx.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ***************************************************************************/
22
23#include "curl_setup.h"
24
25#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
26
27#include "urldata.h"
28#include <curl/curl.h>
29#include "http_proxy.h"
30#include "sendf.h"
31#include "http.h"
32#include "url.h"
33#include "select.h"
34#include "rawstr.h"
35#include "progress.h"
36#include "non-ascii.h"
37#include "connect.h"
38#include "curl_printf.h"
39#include "curlx.h"
40
41#include "curl_memory.h"
42/* The last #include file should be: */
43#include "memdebug.h"
44
45CURLcode Curl_proxy_connect(struct connectdata *conn)
46{
47 if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
48#ifndef CURL_DISABLE_PROXY
49 /* for [protocol] tunneled through HTTP proxy */
50 struct HTTP http_proxy;
51 void *prot_save;
52 CURLcode result;
53
54 /* BLOCKING */
55 /* We want "seamless" operations through HTTP proxy tunnel */
56
57 /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the
58 * member conn->proto.http; we want [protocol] through HTTP and we have
59 * to change the member temporarily for connecting to the HTTP
60 * proxy. After Curl_proxyCONNECT we have to set back the member to the
61 * original pointer
62 *
63 * This function might be called several times in the multi interface case
64 * if the proxy's CONNTECT response is not instant.
65 */
66 prot_save = conn->data->req.protop;
67 memset(&http_proxy, 0, sizeof(http_proxy));
68 conn->data->req.protop = &http_proxy;
69 connkeep(conn, "HTTP proxy CONNECT");
70 result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
71 conn->host.name, conn->remote_port, FALSE);
72 conn->data->req.protop = prot_save;
73 if(CURLE_OK != result)
74 return result;
75 Curl_safefree(conn->allocptr.proxyuserpwd);
76#else
77 return CURLE_NOT_BUILT_IN;
78#endif
79 }
80 /* no HTTP tunnel proxy, just return */
81 return CURLE_OK;
82}
83
84/*
85 * Curl_proxyCONNECT() requires that we're connected to a HTTP proxy. This
86 * function will issue the necessary commands to get a seamless tunnel through
87 * this proxy. After that, the socket can be used just as a normal socket.
88 *
89 * 'blocking' set to TRUE means that this function will do the entire CONNECT
90 * + response in a blocking fashion. Should be avoided!
91 */
92
93CURLcode Curl_proxyCONNECT(struct connectdata *conn,
94 int sockindex,
95 const char *hostname,
96 int remote_port,
97 bool blocking)
98{
99 int subversion=0;
100 struct SessionHandle *data=conn->data;
101 struct SingleRequest *k = &data->req;
102 CURLcode result;
103 curl_socket_t tunnelsocket = conn->sock[sockindex];
104 curl_off_t cl=0;
105 bool closeConnection = FALSE;
106 bool chunked_encoding = FALSE;
107 long check;
108
109#define SELECT_OK 0
110#define SELECT_ERROR 1
111#define SELECT_TIMEOUT 2
112 int error = SELECT_OK;
113
114 if(conn->tunnel_state[sockindex] == TUNNEL_COMPLETE)
115 return CURLE_OK; /* CONNECT is already completed */
116
117 conn->bits.proxy_connect_closed = FALSE;
118
119 do {
120 if(TUNNEL_INIT == conn->tunnel_state[sockindex]) {
121 /* BEGIN CONNECT PHASE */
122 char *host_port;
123 Curl_send_buffer *req_buffer;
124
125 infof(data, "Establish HTTP proxy tunnel to %s:%hu\n",
126 hostname, remote_port);
127
128 /* This only happens if we've looped here due to authentication
129 reasons, and we don't really use the newly cloned URL here
130 then. Just free() it. */
131 free(data->req.newurl);
132 data->req.newurl = NULL;
133
134 /* initialize a dynamic send-buffer */
135 req_buffer = Curl_add_buffer_init();
136
137 if(!req_buffer)
138 return CURLE_OUT_OF_MEMORY;
139
140 host_port = aprintf("%s:%hu", hostname, remote_port);
141 if(!host_port) {
142 Curl_add_buffer_free(req_buffer);
143 return CURLE_OUT_OF_MEMORY;
144 }
145
146 /* Setup the proxy-authorization header, if any */
147 result = Curl_http_output_auth(conn, "CONNECT", host_port, TRUE);
148
149 free(host_port);
150
151 if(!result) {
152 char *host=(char *)"";
153 const char *proxyconn="";
154 const char *useragent="";
155 const char *http = (conn->proxytype == CURLPROXY_HTTP_1_0) ?
156 "1.0" : "1.1";
157 char *hostheader= /* host:port with IPv6 support */
158 aprintf("%s%s%s:%hu", conn->bits.ipv6_ip?"[":"",
159 hostname, conn->bits.ipv6_ip?"]":"",
160 remote_port);
161 if(!hostheader) {
162 Curl_add_buffer_free(req_buffer);
163 return CURLE_OUT_OF_MEMORY;
164 }
165
166 if(!Curl_checkProxyheaders(conn, "Host:")) {
167 host = aprintf("Host: %s\r\n", hostheader);
168 if(!host) {
169 free(hostheader);
170 Curl_add_buffer_free(req_buffer);
171 return CURLE_OUT_OF_MEMORY;
172 }
173 }
174 if(!Curl_checkProxyheaders(conn, "Proxy-Connection:"))
175 proxyconn = "Proxy-Connection: Keep-Alive\r\n";
176
177 if(!Curl_checkProxyheaders(conn, "User-Agent:") &&
178 data->set.str[STRING_USERAGENT])
179 useragent = conn->allocptr.uagent;
180
181 result =
182 Curl_add_bufferf(req_buffer,
183 "CONNECT %s HTTP/%s\r\n"
184 "%s" /* Host: */
185 "%s" /* Proxy-Authorization */
186 "%s" /* User-Agent */
187 "%s", /* Proxy-Connection */
188 hostheader,
189 http,
190 host,
191 conn->allocptr.proxyuserpwd?
192 conn->allocptr.proxyuserpwd:"",
193 useragent,
194 proxyconn);
195
196 if(host && *host)
197 free(host);
198 free(hostheader);
199
200 if(!result)
201 result = Curl_add_custom_headers(conn, TRUE, req_buffer);
202
203 if(!result)
204 /* CRLF terminate the request */
205 result = Curl_add_bufferf(req_buffer, "\r\n");
206
207 if(!result) {
208 /* Send the connect request to the proxy */
209 /* BLOCKING */
210 result =
211 Curl_add_buffer_send(req_buffer, conn,
212 &data->info.request_size, 0, sockindex);
213 }
214 req_buffer = NULL;
215 if(result)
216 failf(data, "Failed sending CONNECT to proxy");
217 }
218
219 Curl_add_buffer_free(req_buffer);
220 if(result)
221 return result;
222
223 conn->tunnel_state[sockindex] = TUNNEL_CONNECT;
224 } /* END CONNECT PHASE */
225
226 check = Curl_timeleft(data, NULL, TRUE);
227 if(check <= 0) {
228 failf(data, "Proxy CONNECT aborted due to timeout");
229 return CURLE_RECV_ERROR;
230 }
231
232 if(!blocking) {
233 if(0 == Curl_socket_ready(tunnelsocket, CURL_SOCKET_BAD, 0))
234 /* return so we'll be called again polling-style */
235 return CURLE_OK;
236 else {
237 DEBUGF(infof(data,
238 "Read response immediately from proxy CONNECT\n"));
239 }
240 }
241
242 /* at this point, the tunnel_connecting phase is over. */
243
244 { /* READING RESPONSE PHASE */
245 size_t nread; /* total size read */
246 int perline; /* count bytes per line */
247 int keepon=TRUE;
248 ssize_t gotbytes;
249 char *ptr;
250 char *line_start;
251
252 ptr=data->state.buffer;
253 line_start = ptr;
254
255 nread=0;
256 perline=0;
257
258 while((nread<BUFSIZE) && (keepon && !error)) {
259
260 check = Curl_timeleft(data, NULL, TRUE);
261 if(check <= 0) {
262 failf(data, "Proxy CONNECT aborted due to timeout");
263 error = SELECT_TIMEOUT; /* already too little time */
264 break;
265 }
266
267 /* loop every second at least, less if the timeout is near */
268 switch (Curl_socket_ready(tunnelsocket, CURL_SOCKET_BAD,
269 check<1000L?check:1000)) {
270 case -1: /* select() error, stop reading */
271 error = SELECT_ERROR;
272 failf(data, "Proxy CONNECT aborted due to select/poll error");
273 break;
274 case 0: /* timeout */
275 break;
276 default:
277 DEBUGASSERT(ptr+BUFSIZE-nread <= data->state.buffer+BUFSIZE+1);
278 result = Curl_read(conn, tunnelsocket, ptr, BUFSIZE-nread,
279 &gotbytes);
280 if(result==CURLE_AGAIN)
281 continue; /* go loop yourself */
282 else if(result)
283 keepon = FALSE;
284 else if(gotbytes <= 0) {
285 keepon = FALSE;
286 if(data->set.proxyauth && data->state.authproxy.avail) {
287 /* proxy auth was requested and there was proxy auth available,
288 then deem this as "mere" proxy disconnect */
289 conn->bits.proxy_connect_closed = TRUE;
290 infof(data, "Proxy CONNECT connection closed\n");
291 }
292 else {
293 error = SELECT_ERROR;
294 failf(data, "Proxy CONNECT aborted");
295 }
296 }
297 else {
298 /*
299 * We got a whole chunk of data, which can be anything from one
300 * byte to a set of lines and possibly just a piece of the last
301 * line.
302 */
303 int i;
304
305 nread += gotbytes;
306
307 if(keepon > TRUE) {
308 /* This means we are currently ignoring a response-body */
309
310 nread = 0; /* make next read start over in the read buffer */
311 ptr=data->state.buffer;
312 if(cl) {
313 /* A Content-Length based body: simply count down the counter
314 and make sure to break out of the loop when we're done! */
315 cl -= gotbytes;
316 if(cl<=0) {
317 keepon = FALSE;
318 break;
319 }
320 }
321 else {
322 /* chunked-encoded body, so we need to do the chunked dance
323 properly to know when the end of the body is reached */
324 CHUNKcode r;
325 ssize_t tookcareof=0;
326
327 /* now parse the chunked piece of data so that we can
328 properly tell when the stream ends */
329 r = Curl_httpchunk_read(conn, ptr, gotbytes, &tookcareof);
330 if(r == CHUNKE_STOP) {
331 /* we're done reading chunks! */
332 infof(data, "chunk reading DONE\n");
333 keepon = FALSE;
334 /* we did the full CONNECT treatment, go COMPLETE */
335 conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
336 }
337 else
338 infof(data, "Read %zd bytes of chunk, continue\n",
339 tookcareof);
340 }
341 }
342 else
343 for(i = 0; i < gotbytes; ptr++, i++) {
344 perline++; /* amount of bytes in this line so far */
345 if(*ptr == 0x0a) {
346 char letter;
347 int writetype;
348
349 /* convert from the network encoding */
350 result = Curl_convert_from_network(data, line_start,
351 perline);
352 /* Curl_convert_from_network calls failf if unsuccessful */
353 if(result)
354 return result;
355
356 /* output debug if that is requested */
357 if(data->set.verbose)
358 Curl_debug(data, CURLINFO_HEADER_IN,
359 line_start, (size_t)perline, conn);
360
361 /* send the header to the callback */
362 writetype = CLIENTWRITE_HEADER;
363 if(data->set.include_header)
364 writetype |= CLIENTWRITE_BODY;
365
366 result = Curl_client_write(conn, writetype, line_start,
367 perline);
368
369 data->info.header_size += (long)perline;
370 data->req.headerbytecount += (long)perline;
371
372 if(result)
373 return result;
374
375 /* Newlines are CRLF, so the CR is ignored as the line isn't
376 really terminated until the LF comes. Treat a following CR
377 as end-of-headers as well.*/
378
379 if(('\r' == line_start[0]) ||
380 ('\n' == line_start[0])) {
381 /* end of response-headers from the proxy */
382 nread = 0; /* make next read start over in the read
383 buffer */
384 ptr=data->state.buffer;
385 if((407 == k->httpcode) && !data->state.authproblem) {
386 /* If we get a 407 response code with content length
387 when we have no auth problem, we must ignore the
388 whole response-body */
389 keepon = 2;
390
391 if(cl) {
392 infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T
393 " bytes of response-body\n", cl);
394
395 /* remove the remaining chunk of what we already
396 read */
397 cl -= (gotbytes - i);
398
399 if(cl<=0)
400 /* if the whole thing was already read, we are done!
401 */
402 keepon=FALSE;
403 }
404 else if(chunked_encoding) {
405 CHUNKcode r;
406 /* We set ignorebody true here since the chunked
407 decoder function will acknowledge that. Pay
408 attention so that this is cleared again when this
409 function returns! */
410 k->ignorebody = TRUE;
411 infof(data, "%zd bytes of chunk left\n", gotbytes-i);
412
413 if(line_start[1] == '\n') {
414 /* this can only be a LF if the letter at index 0
415 was a CR */
416 line_start++;
417 i++;
418 }
419
420 /* now parse the chunked piece of data so that we can
421 properly tell when the stream ends */
422 r = Curl_httpchunk_read(conn, line_start+1,
423 gotbytes -i, &gotbytes);
424 if(r == CHUNKE_STOP) {
425 /* we're done reading chunks! */
426 infof(data, "chunk reading DONE\n");
427 keepon = FALSE;
428 /* we did the full CONNECT treatment, go to
429 COMPLETE */
430 conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
431 }
432 else
433 infof(data, "Read %zd bytes of chunk, continue\n",
434 gotbytes);
435 }
436 else {
437 /* without content-length or chunked encoding, we
438 can't keep the connection alive since the close is
439 the end signal so we bail out at once instead */
440 keepon=FALSE;
441 }
442 }
443 else {
444 keepon = FALSE;
445 if(200 == data->info.httpproxycode) {
446 if(gotbytes - (i+1))
447 failf(data, "Proxy CONNECT followed by %zd bytes "
448 "of opaque data. Data ignored (known bug #39)",
449 gotbytes - (i+1));
450 }
451 }
452 /* we did the full CONNECT treatment, go to COMPLETE */
453 conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
454 break; /* breaks out of for-loop, not switch() */
455 }
456
457 /* keep a backup of the position we are about to blank */
458 letter = line_start[perline];
459 line_start[perline]=0; /* zero terminate the buffer */
460 if((checkprefix("WWW-Authenticate:", line_start) &&
461 (401 == k->httpcode)) ||
462 (checkprefix("Proxy-authenticate:", line_start) &&
463 (407 == k->httpcode))) {
464
465 bool proxy = (k->httpcode == 407) ? TRUE : FALSE;
466 char *auth = Curl_copy_header_value(line_start);
467 if(!auth)
468 return CURLE_OUT_OF_MEMORY;
469
470 result = Curl_http_input_auth(conn, proxy, auth);
471
472 free(auth);
473
474 if(result)
475 return result;
476 }
477 else if(checkprefix("Content-Length:", line_start)) {
478 cl = curlx_strtoofft(line_start +
479 strlen("Content-Length:"), NULL, 10);
480 }
481 else if(Curl_compareheader(line_start,
482 "Connection:", "close"))
483 closeConnection = TRUE;
484 else if(Curl_compareheader(line_start,
485 "Transfer-Encoding:",
486 "chunked")) {
487 infof(data, "CONNECT responded chunked\n");
488 chunked_encoding = TRUE;
489 /* init our chunky engine */
490 Curl_httpchunk_init(conn);
491 }
492 else if(Curl_compareheader(line_start,
493 "Proxy-Connection:", "close"))
494 closeConnection = TRUE;
495 else if(2 == sscanf(line_start, "HTTP/1.%d %d",
496 &subversion,
497 &k->httpcode)) {
498 /* store the HTTP code from the proxy */
499 data->info.httpproxycode = k->httpcode;
500 }
501 /* put back the letter we blanked out before */
502 line_start[perline]= letter;
503
504 perline=0; /* line starts over here */
505 line_start = ptr+1; /* this skips the zero byte we wrote */
506 }
507 }
508 }
509 break;
510 } /* switch */
511 if(Curl_pgrsUpdate(conn))
512 return CURLE_ABORTED_BY_CALLBACK;
513 } /* while there's buffer left and loop is requested */
514
515 if(error)
516 return CURLE_RECV_ERROR;
517
518 if(data->info.httpproxycode != 200) {
519 /* Deal with the possibly already received authenticate
520 headers. 'newurl' is set to a new URL if we must loop. */
521 result = Curl_http_auth_act(conn);
522 if(result)
523 return result;
524
525 if(conn->bits.close)
526 /* the connection has been marked for closure, most likely in the
527 Curl_http_auth_act() function and thus we can kill it at once
528 below
529 */
530 closeConnection = TRUE;
531 }
532
533 if(closeConnection && data->req.newurl) {
534 /* Connection closed by server. Don't use it anymore */
535 Curl_closesocket(conn, conn->sock[sockindex]);
536 conn->sock[sockindex] = CURL_SOCKET_BAD;
537 break;
538 }
539 } /* END READING RESPONSE PHASE */
540
541 /* If we are supposed to continue and request a new URL, which basically
542 * means the HTTP authentication is still going on so if the tunnel
543 * is complete we start over in INIT state */
544 if(data->req.newurl &&
545 (TUNNEL_COMPLETE == conn->tunnel_state[sockindex])) {
546 conn->tunnel_state[sockindex] = TUNNEL_INIT;
547 infof(data, "TUNNEL_STATE switched to: %d\n",
548 conn->tunnel_state[sockindex]);
549 }
550
551 } while(data->req.newurl);
552
553 if(200 != data->req.httpcode) {
554 if(closeConnection && data->req.newurl) {
555 conn->bits.proxy_connect_closed = TRUE;
556 infof(data, "Connect me again please\n");
557 }
558 else {
559 free(data->req.newurl);
560 data->req.newurl = NULL;
561 /* failure, close this connection to avoid re-use */
562 connclose(conn, "proxy CONNECT failure");
563 Curl_closesocket(conn, conn->sock[sockindex]);
564 conn->sock[sockindex] = CURL_SOCKET_BAD;
565 }
566
567 /* to back to init state */
568 conn->tunnel_state[sockindex] = TUNNEL_INIT;
569
570 if(conn->bits.proxy_connect_closed)
571 /* this is not an error, just part of the connection negotiation */
572 return CURLE_OK;
573 else {
574 failf(data, "Received HTTP code %d from proxy after CONNECT",
575 data->req.httpcode);
576 return CURLE_RECV_ERROR;
577 }
578 }
579
580 conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
581
582 /* If a proxy-authorization header was used for the proxy, then we should
583 make sure that it isn't accidentally used for the document request
584 after we've connected. So let's free and clear it here. */
585 Curl_safefree(conn->allocptr.proxyuserpwd);
586 conn->allocptr.proxyuserpwd = NULL;
587
588 data->state.authproxy.done = TRUE;
589
590 infof (data, "Proxy replied OK to CONNECT request\n");
591 data->req.ignorebody = FALSE; /* put it (back) to non-ignore state */
592 conn->bits.rewindaftersend = FALSE; /* make sure this isn't set for the
593 document request */
594 return CURLE_OK;
595}
596#endif /* CURL_DISABLE_PROXY */
Note: See TracBrowser for help on using the repository browser.