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