Imported Upstream version 7.44.0
[platform/upstream/curl.git] / lib / http_proxy.c
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 http://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
45 CURLcode 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
93 CURLcode 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 */