Revert "Update to 7.44.0"
[platform/upstream/curl.git] / lib / http_proxy.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2014, 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
39 #define _MPRINTF_REPLACE /* use our functions only */
40 #include <curl/mprintf.h>
41
42 #include "curlx.h"
43
44 #include "curl_memory.h"
45 /* The last #include file should be: */
46 #include "memdebug.h"
47
48 CURLcode Curl_proxy_connect(struct connectdata *conn)
49 {
50   if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
51 #ifndef CURL_DISABLE_PROXY
52     /* for [protocol] tunneled through HTTP proxy */
53     struct HTTP http_proxy;
54     void *prot_save;
55     CURLcode result;
56
57     /* BLOCKING */
58     /* We want "seamless" operations through HTTP proxy tunnel */
59
60     /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the
61      * member conn->proto.http; we want [protocol] through HTTP and we have
62      * to change the member temporarily for connecting to the HTTP
63      * proxy. After Curl_proxyCONNECT we have to set back the member to the
64      * original pointer
65      *
66      * This function might be called several times in the multi interface case
67      * if the proxy's CONNTECT response is not instant.
68      */
69     prot_save = conn->data->req.protop;
70     memset(&http_proxy, 0, sizeof(http_proxy));
71     conn->data->req.protop = &http_proxy;
72     connkeep(conn, "HTTP proxy CONNECT");
73     result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
74                                conn->host.name, conn->remote_port);
75     conn->data->req.protop = prot_save;
76     if(CURLE_OK != result)
77       return result;
78 #else
79     return CURLE_NOT_BUILT_IN;
80 #endif
81   }
82   /* no HTTP tunnel proxy, just return */
83   return CURLE_OK;
84 }
85
86 /*
87  * Curl_proxyCONNECT() requires that we're connected to a HTTP proxy. This
88  * function will issue the necessary commands to get a seamless tunnel through
89  * this proxy. After that, the socket can be used just as a normal socket.
90  */
91
92 CURLcode Curl_proxyCONNECT(struct connectdata *conn,
93                            int sockindex,
94                            const char *hostname,
95                            int remote_port)
96 {
97   int subversion=0;
98   struct SessionHandle *data=conn->data;
99   struct SingleRequest *k = &data->req;
100   CURLcode result;
101   curl_socket_t tunnelsocket = conn->sock[sockindex];
102   curl_off_t cl=0;
103   bool closeConnection = FALSE;
104   bool chunked_encoding = FALSE;
105   long check;
106
107 #define SELECT_OK      0
108 #define SELECT_ERROR   1
109 #define SELECT_TIMEOUT 2
110   int error = SELECT_OK;
111
112   if(conn->tunnel_state[sockindex] == TUNNEL_COMPLETE)
113     return CURLE_OK; /* CONNECT is already completed */
114
115   conn->bits.proxy_connect_closed = FALSE;
116
117   do {
118     if(TUNNEL_INIT == conn->tunnel_state[sockindex]) {
119       /* BEGIN CONNECT PHASE */
120       char *host_port;
121       Curl_send_buffer *req_buffer;
122
123       infof(data, "Establish HTTP proxy tunnel to %s:%hu\n",
124             hostname, remote_port);
125
126       if(data->req.newurl) {
127         /* This only happens if we've looped here due to authentication
128            reasons, and we don't really use the newly cloned URL here
129            then. Just free() it. */
130         free(data->req.newurl);
131         data->req.newurl = NULL;
132       }
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         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           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             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)
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_safefree(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(0 == Curl_socket_ready(tunnelsocket, CURL_SOCKET_BAD, 0))
233       /* return so we'll be called again polling-style */
234       return CURLE_OK;
235     else {
236       DEBUGF(infof(data,
237                    "Read response immediately from proxy CONNECT\n"));
238     }
239
240     /* at this point, the tunnel_connecting phase is over. */
241
242     { /* READING RESPONSE PHASE */
243       size_t nread;   /* total size read */
244       int perline; /* count bytes per line */
245       int keepon=TRUE;
246       ssize_t gotbytes;
247       char *ptr;
248       char *line_start;
249
250       ptr=data->state.buffer;
251       line_start = ptr;
252
253       nread=0;
254       perline=0;
255
256       while((nread<BUFSIZE) && (keepon && !error)) {
257
258         check = Curl_timeleft(data, NULL, TRUE);
259         if(check <= 0) {
260           failf(data, "Proxy CONNECT aborted due to timeout");
261           error = SELECT_TIMEOUT; /* already too little time */
262           break;
263         }
264
265         /* loop every second at least, less if the timeout is near */
266         switch (Curl_socket_ready(tunnelsocket, CURL_SOCKET_BAD,
267                                   check<1000L?check:1000)) {
268         case -1: /* select() error, stop reading */
269           error = SELECT_ERROR;
270           failf(data, "Proxy CONNECT aborted due to select/poll error");
271           break;
272         case 0: /* timeout */
273           break;
274         default:
275           DEBUGASSERT(ptr+BUFSIZE-nread <= data->state.buffer+BUFSIZE+1);
276           result = Curl_read(conn, tunnelsocket, ptr, BUFSIZE-nread,
277                              &gotbytes);
278           if(result==CURLE_AGAIN)
279             continue; /* go loop yourself */
280           else if(result)
281             keepon = FALSE;
282           else if(gotbytes <= 0) {
283             keepon = FALSE;
284             if(data->set.proxyauth && data->state.authproxy.avail) {
285               /* proxy auth was requested and there was proxy auth available,
286                  then deem this as "mere" proxy disconnect */
287               conn->bits.proxy_connect_closed = TRUE;
288               infof(data, "Proxy CONNECT connection closed");
289             }
290             else {
291               error = SELECT_ERROR;
292               failf(data, "Proxy CONNECT aborted");
293             }
294           }
295           else {
296             /*
297              * We got a whole chunk of data, which can be anything from one
298              * byte to a set of lines and possibly just a piece of the last
299              * line.
300              */
301             int i;
302
303             nread += gotbytes;
304
305             if(keepon > TRUE) {
306               /* This means we are currently ignoring a response-body */
307
308               nread = 0; /* make next read start over in the read buffer */
309               ptr=data->state.buffer;
310               if(cl) {
311                 /* A Content-Length based body: simply count down the counter
312                    and make sure to break out of the loop when we're done! */
313                 cl -= gotbytes;
314                 if(cl<=0) {
315                   keepon = FALSE;
316                   break;
317                 }
318               }
319               else {
320                 /* chunked-encoded body, so we need to do the chunked dance
321                    properly to know when the end of the body is reached */
322                 CHUNKcode r;
323                 ssize_t tookcareof=0;
324
325                 /* now parse the chunked piece of data so that we can
326                    properly tell when the stream ends */
327                 r = Curl_httpchunk_read(conn, ptr, gotbytes, &tookcareof);
328                 if(r == CHUNKE_STOP) {
329                   /* we're done reading chunks! */
330                   infof(data, "chunk reading DONE\n");
331                   keepon = FALSE;
332                   /* we did the full CONNECT treatment, go COMPLETE */
333                   conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
334                 }
335                 else
336                   infof(data, "Read %zd bytes of chunk, continue\n",
337                         tookcareof);
338               }
339             }
340             else
341               for(i = 0; i < gotbytes; ptr++, i++) {
342                 perline++; /* amount of bytes in this line so far */
343                 if(*ptr == 0x0a) {
344                   char letter;
345                   int writetype;
346
347                   /* convert from the network encoding */
348                   result = Curl_convert_from_network(data, line_start,
349                                                      perline);
350                   /* Curl_convert_from_network calls failf if unsuccessful */
351                   if(result)
352                     return result;
353
354                   /* output debug if that is requested */
355                   if(data->set.verbose)
356                     Curl_debug(data, CURLINFO_HEADER_IN,
357                                line_start, (size_t)perline, conn);
358
359                   /* send the header to the callback */
360                   writetype = CLIENTWRITE_HEADER;
361                   if(data->set.include_header)
362                     writetype |= CLIENTWRITE_BODY;
363
364                   result = Curl_client_write(conn, writetype, line_start,
365                                              perline);
366
367                   data->info.header_size += (long)perline;
368                   data->req.headerbytecount += (long)perline;
369
370                   if(result)
371                     return result;
372
373                   /* Newlines are CRLF, so the CR is ignored as the line isn't
374                      really terminated until the LF comes. Treat a following CR
375                      as end-of-headers as well.*/
376
377                   if(('\r' == line_start[0]) ||
378                      ('\n' == line_start[0])) {
379                     /* end of response-headers from the proxy */
380                     nread = 0; /* make next read start over in the read
381                                   buffer */
382                     ptr=data->state.buffer;
383                     if((407 == k->httpcode) && !data->state.authproblem) {
384                       /* If we get a 407 response code with content length
385                          when we have no auth problem, we must ignore the
386                          whole response-body */
387                       keepon = 2;
388
389                       if(cl) {
390                         infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T
391                               " bytes of response-body\n", cl);
392
393                         /* remove the remaining chunk of what we already
394                            read */
395                         cl -= (gotbytes - i);
396
397                         if(cl<=0)
398                           /* if the whole thing was already read, we are done!
399                            */
400                           keepon=FALSE;
401                       }
402                       else if(chunked_encoding) {
403                         CHUNKcode r;
404                         /* We set ignorebody true here since the chunked
405                            decoder function will acknowledge that. Pay
406                            attention so that this is cleared again when this
407                            function returns! */
408                         k->ignorebody = TRUE;
409                         infof(data, "%zd bytes of chunk left\n", gotbytes-i);
410
411                         if(line_start[1] == '\n') {
412                           /* this can only be a LF if the letter at index 0
413                              was a CR */
414                           line_start++;
415                           i++;
416                         }
417
418                         /* now parse the chunked piece of data so that we can
419                            properly tell when the stream ends */
420                         r = Curl_httpchunk_read(conn, line_start+1,
421                                                   gotbytes -i, &gotbytes);
422                         if(r == CHUNKE_STOP) {
423                           /* we're done reading chunks! */
424                           infof(data, "chunk reading DONE\n");
425                           keepon = FALSE;
426                           /* we did the full CONNECT treatment, go to
427                              COMPLETE */
428                           conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
429                         }
430                         else
431                           infof(data, "Read %zd bytes of chunk, continue\n",
432                                 gotbytes);
433                       }
434                       else {
435                         /* without content-length or chunked encoding, we
436                            can't keep the connection alive since the close is
437                            the end signal so we bail out at once instead */
438                         keepon=FALSE;
439                       }
440                     }
441                     else {
442                       keepon = FALSE;
443                       if(200 == data->info.httpproxycode) {
444                         if(gotbytes - (i+1))
445                           failf(data, "Proxy CONNECT followed by %zd bytes "
446                                 "of opaque data. Data ignored (known bug #39)",
447                                 gotbytes - (i+1));
448                       }
449                     }
450                     /* we did the full CONNECT treatment, go to COMPLETE */
451                     conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
452                     break; /* breaks out of for-loop, not switch() */
453                   }
454
455                   /* keep a backup of the position we are about to blank */
456                   letter = line_start[perline];
457                   line_start[perline]=0; /* zero terminate the buffer */
458                   if((checkprefix("WWW-Authenticate:", line_start) &&
459                       (401 == k->httpcode)) ||
460                      (checkprefix("Proxy-authenticate:", line_start) &&
461                       (407 == k->httpcode))) {
462
463                     bool proxy = (k->httpcode == 407) ? TRUE : FALSE;
464                     char *auth = Curl_copy_header_value(line_start);
465                     if(!auth)
466                       return CURLE_OUT_OF_MEMORY;
467
468                     result = Curl_http_input_auth(conn, proxy, auth);
469
470                     Curl_safefree(auth);
471
472                     if(result)
473                       return result;
474                   }
475                   else if(checkprefix("Content-Length:", line_start)) {
476                     cl = curlx_strtoofft(line_start +
477                                          strlen("Content-Length:"), NULL, 10);
478                   }
479                   else if(Curl_compareheader(line_start,
480                                              "Connection:", "close"))
481                     closeConnection = TRUE;
482                   else if(Curl_compareheader(line_start,
483                                              "Transfer-Encoding:",
484                                              "chunked")) {
485                     infof(data, "CONNECT responded chunked\n");
486                     chunked_encoding = TRUE;
487                     /* init our chunky engine */
488                     Curl_httpchunk_init(conn);
489                   }
490                   else if(Curl_compareheader(line_start,
491                                              "Proxy-Connection:", "close"))
492                     closeConnection = TRUE;
493                   else if(2 == sscanf(line_start, "HTTP/1.%d %d",
494                                       &subversion,
495                                       &k->httpcode)) {
496                     /* store the HTTP code from the proxy */
497                     data->info.httpproxycode = k->httpcode;
498                   }
499                   /* put back the letter we blanked out before */
500                   line_start[perline]= letter;
501
502                   perline=0; /* line starts over here */
503                   line_start = ptr+1; /* this skips the zero byte we wrote */
504                 }
505               }
506           }
507           break;
508         } /* switch */
509         if(Curl_pgrsUpdate(conn))
510           return CURLE_ABORTED_BY_CALLBACK;
511       } /* while there's buffer left and loop is requested */
512
513       if(error)
514         return CURLE_RECV_ERROR;
515
516       if(data->info.httpproxycode != 200) {
517         /* Deal with the possibly already received authenticate
518            headers. 'newurl' is set to a new URL if we must loop. */
519         result = Curl_http_auth_act(conn);
520         if(result)
521           return result;
522
523         if(conn->bits.close)
524           /* the connection has been marked for closure, most likely in the
525              Curl_http_auth_act() function and thus we can kill it at once
526              below
527           */
528           closeConnection = TRUE;
529       }
530
531       if(closeConnection && data->req.newurl) {
532         /* Connection closed by server. Don't use it anymore */
533         Curl_closesocket(conn, conn->sock[sockindex]);
534         conn->sock[sockindex] = CURL_SOCKET_BAD;
535         break;
536       }
537     } /* END READING RESPONSE PHASE */
538
539     /* If we are supposed to continue and request a new URL, which basically
540      * means the HTTP authentication is still going on so if the tunnel
541      * is complete we start over in INIT state */
542     if(data->req.newurl &&
543        (TUNNEL_COMPLETE == conn->tunnel_state[sockindex])) {
544       conn->tunnel_state[sockindex] = TUNNEL_INIT;
545       infof(data, "TUNNEL_STATE switched to: %d\n",
546             conn->tunnel_state[sockindex]);
547     }
548
549   } while(data->req.newurl);
550
551   if(200 != data->req.httpcode) {
552     if(closeConnection && data->req.newurl) {
553       conn->bits.proxy_connect_closed = TRUE;
554       infof(data, "Connect me again please\n");
555     }
556     else {
557       if(data->req.newurl) {
558         /* this won't be used anymore for the CONNECT so free it now */
559         free(data->req.newurl);
560         data->req.newurl = NULL;
561       }
562       /* failure, close this connection to avoid re-use */
563       connclose(conn, "proxy CONNECT failure");
564       Curl_closesocket(conn, conn->sock[sockindex]);
565       conn->sock[sockindex] = CURL_SOCKET_BAD;
566     }
567
568     /* to back to init state */
569     conn->tunnel_state[sockindex] = TUNNEL_INIT;
570
571     if(conn->bits.proxy_connect_closed)
572       /* this is not an error, just part of the connection negotiation */
573       return CURLE_OK;
574     else {
575       failf(data, "Received HTTP code %d from proxy after CONNECT",
576             data->req.httpcode);
577       return CURLE_RECV_ERROR;
578     }
579   }
580
581   conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
582
583   /* If a proxy-authorization header was used for the proxy, then we should
584      make sure that it isn't accidentally used for the document request
585      after we've connected. So let's free and clear it here. */
586   Curl_safefree(conn->allocptr.proxyuserpwd);
587   conn->allocptr.proxyuserpwd = NULL;
588
589   data->state.authproxy.done = TRUE;
590
591   infof (data, "Proxy replied OK to CONNECT request\n");
592   data->req.ignorebody = FALSE; /* put it (back) to non-ignore state */
593   conn->bits.rewindaftersend = FALSE; /* make sure this isn't set for the
594                                          document request  */
595   return CURLE_OK;
596 }
597 #endif /* CURL_DISABLE_PROXY */