FTP: remove krb4 support
[platform/upstream/curl.git] / lib / pingpong.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2013, 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  *   'pingpong' is for generic back-and-forth support functions used by FTP,
22  *   IMAP, POP3, SMTP and whatever more that likes them.
23  *
24  ***************************************************************************/
25
26 #include "curl_setup.h"
27
28 #include "urldata.h"
29 #include "sendf.h"
30 #include "select.h"
31 #include "progress.h"
32 #include "speedcheck.h"
33 #include "pingpong.h"
34 #include "multiif.h"
35 #include "non-ascii.h"
36
37 #define _MPRINTF_REPLACE /* use our functions only */
38 #include <curl/mprintf.h>
39
40 #include "curl_memory.h"
41 /* The last #include file should be: */
42 #include "memdebug.h"
43
44 #ifdef USE_PINGPONG
45
46 /* Returns timeout in ms. 0 or negative number means the timeout has already
47    triggered */
48 long Curl_pp_state_timeout(struct pingpong *pp)
49 {
50   struct connectdata *conn = pp->conn;
51   struct SessionHandle *data=conn->data;
52   long timeout_ms; /* in milliseconds */
53   long timeout2_ms; /* in milliseconds */
54   long response_time= (data->set.server_response_timeout)?
55     data->set.server_response_timeout: pp->response_time;
56
57   /* if CURLOPT_SERVER_RESPONSE_TIMEOUT is set, use that to determine
58      remaining time, or use pp->response because SERVER_RESPONSE_TIMEOUT is
59      supposed to govern the response for any given server response, not for
60      the time from connect to the given server response. */
61
62   /* Without a requested timeout, we only wait 'response_time' seconds for the
63      full response to arrive before we bail out */
64   timeout_ms = response_time -
65     Curl_tvdiff(Curl_tvnow(), pp->response); /* spent time */
66
67   if(data->set.timeout) {
68     /* if timeout is requested, find out how much remaining time we have */
69     timeout2_ms = data->set.timeout - /* timeout time */
70       Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
71
72     /* pick the lowest number */
73     timeout_ms = CURLMIN(timeout_ms, timeout2_ms);
74   }
75
76   return timeout_ms;
77 }
78
79 /*
80  * Curl_pp_statemach()
81  */
82 CURLcode Curl_pp_statemach(struct pingpong *pp, bool block)
83 {
84   struct connectdata *conn = pp->conn;
85   curl_socket_t sock = conn->sock[FIRSTSOCKET];
86   int rc;
87   long interval_ms;
88   long timeout_ms = Curl_pp_state_timeout(pp);
89   struct SessionHandle *data=conn->data;
90   CURLcode result = CURLE_OK;
91
92   if(timeout_ms <=0 ) {
93     failf(data, "server response timeout");
94     return CURLE_OPERATION_TIMEDOUT; /* already too little time */
95   }
96
97   if(block) {
98     interval_ms = 1000;  /* use 1 second timeout intervals */
99     if(timeout_ms < interval_ms)
100       interval_ms = timeout_ms;
101   }
102   else
103     interval_ms = 0; /* immediate */
104
105   if(Curl_pp_moredata(pp))
106     /* We are receiving and there is data in the cache so just read it */
107     rc = 1;
108   else
109     rc = Curl_socket_ready(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
110                            pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
111                            interval_ms);
112
113   if(block) {
114     /* if we didn't wait, we don't have to spend time on this now */
115     if(Curl_pgrsUpdate(conn))
116       result = CURLE_ABORTED_BY_CALLBACK;
117     else
118       result = Curl_speedcheck(data, Curl_tvnow());
119
120     if(result)
121       return result;
122   }
123
124   if(rc == -1) {
125     failf(data, "select/poll error");
126     result = CURLE_OUT_OF_MEMORY;
127   }
128   else if(rc)
129     result = pp->statemach_act(conn);
130
131   return result;
132 }
133
134 /* initialize stuff to prepare for reading a fresh new response */
135 void Curl_pp_init(struct pingpong *pp)
136 {
137   struct connectdata *conn = pp->conn;
138   pp->nread_resp = 0;
139   pp->linestart_resp = conn->data->state.buffer;
140   pp->pending_resp = TRUE;
141   pp->response = Curl_tvnow(); /* start response time-out now! */
142 }
143
144
145
146 /***********************************************************************
147  *
148  * Curl_pp_vsendf()
149  *
150  * Send the formated string as a command to a pingpong server. Note that
151  * the string should not have any CRLF appended, as this function will
152  * append the necessary things itself.
153  *
154  * made to never block
155  */
156 CURLcode Curl_pp_vsendf(struct pingpong *pp,
157                         const char *fmt,
158                         va_list args)
159 {
160   ssize_t bytes_written;
161   size_t write_len;
162   char *fmt_crlf;
163   char *s;
164   CURLcode error;
165   struct connectdata *conn = pp->conn;
166   struct SessionHandle *data = conn->data;
167
168 #ifdef HAVE_GSSAPI
169   enum protection_level data_sec = conn->data_prot;
170 #endif
171
172   DEBUGASSERT(pp->sendleft == 0);
173   DEBUGASSERT(pp->sendsize == 0);
174   DEBUGASSERT(pp->sendthis == NULL);
175
176   fmt_crlf = aprintf("%s\r\n", fmt); /* append a trailing CRLF */
177   if(!fmt_crlf)
178     return CURLE_OUT_OF_MEMORY;
179
180   s = vaprintf(fmt_crlf, args); /* trailing CRLF appended */
181   free(fmt_crlf);
182   if(!s)
183     return CURLE_OUT_OF_MEMORY;
184
185   bytes_written = 0;
186   write_len = strlen(s);
187
188   Curl_pp_init(pp);
189
190   error = Curl_convert_to_network(data, s, write_len);
191   /* Curl_convert_to_network calls failf if unsuccessful */
192   if(error) {
193     free(s);
194     return error;
195   }
196
197 #ifdef HAVE_GSSAPI
198   conn->data_prot = PROT_CMD;
199 #endif
200   error = Curl_write(conn, conn->sock[FIRSTSOCKET], s, write_len,
201                      &bytes_written);
202 #ifdef HAVE_GSSAPI
203   DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
204   conn->data_prot = data_sec;
205 #endif
206
207   if(error) {
208     free(s);
209     return error;
210   }
211
212   if(conn->data->set.verbose)
213     Curl_debug(conn->data, CURLINFO_HEADER_OUT,
214                s, (size_t)bytes_written, conn);
215
216   if(bytes_written != (ssize_t)write_len) {
217     /* the whole chunk was not sent, keep it around and adjust sizes */
218     pp->sendthis = s;
219     pp->sendsize = write_len;
220     pp->sendleft = write_len - bytes_written;
221   }
222   else {
223     free(s);
224     pp->sendthis = NULL;
225     pp->sendleft = pp->sendsize = 0;
226     pp->response = Curl_tvnow();
227   }
228
229   return CURLE_OK;
230 }
231
232
233 /***********************************************************************
234  *
235  * Curl_pp_sendf()
236  *
237  * Send the formated string as a command to a pingpong server. Note that
238  * the string should not have any CRLF appended, as this function will
239  * append the necessary things itself.
240  *
241  * made to never block
242  */
243 CURLcode Curl_pp_sendf(struct pingpong *pp,
244                        const char *fmt, ...)
245 {
246   CURLcode res;
247   va_list ap;
248   va_start(ap, fmt);
249
250   res = Curl_pp_vsendf(pp, fmt, ap);
251
252   va_end(ap);
253
254   return res;
255 }
256
257 /*
258  * Curl_pp_readresp()
259  *
260  * Reads a piece of a server response.
261  */
262 CURLcode Curl_pp_readresp(curl_socket_t sockfd,
263                           struct pingpong *pp,
264                           int *code, /* return the server code if done */
265                           size_t *size) /* size of the response */
266 {
267   ssize_t perline; /* count bytes per line */
268   bool keepon=TRUE;
269   ssize_t gotbytes;
270   char *ptr;
271   struct connectdata *conn = pp->conn;
272   struct SessionHandle *data = conn->data;
273   char * const buf = data->state.buffer;
274   CURLcode result = CURLE_OK;
275
276   *code = 0; /* 0 for errors or not done */
277   *size = 0;
278
279   ptr=buf + pp->nread_resp;
280
281   /* number of bytes in the current line, so far */
282   perline = (ssize_t)(ptr-pp->linestart_resp);
283
284   keepon=TRUE;
285
286   while((pp->nread_resp<BUFSIZE) && (keepon && !result)) {
287
288     if(pp->cache) {
289       /* we had data in the "cache", copy that instead of doing an actual
290        * read
291        *
292        * pp->cache_size is cast to ssize_t here.  This should be safe, because
293        * it would have been populated with something of size int to begin
294        * with, even though its datatype may be larger than an int.
295        */
296       DEBUGASSERT((ptr+pp->cache_size) <= (buf+BUFSIZE+1));
297       memcpy(ptr, pp->cache, pp->cache_size);
298       gotbytes = (ssize_t)pp->cache_size;
299       free(pp->cache);    /* free the cache */
300       pp->cache = NULL;   /* clear the pointer */
301       pp->cache_size = 0; /* zero the size just in case */
302     }
303     else {
304       int res;
305 #ifdef HAVE_GSSAPI
306       enum protection_level prot = conn->data_prot;
307       conn->data_prot = PROT_CLEAR;
308 #endif
309       DEBUGASSERT((ptr+BUFSIZE-pp->nread_resp) <= (buf+BUFSIZE+1));
310       res = Curl_read(conn, sockfd, ptr, BUFSIZE-pp->nread_resp,
311                       &gotbytes);
312 #ifdef HAVE_GSSAPI
313       DEBUGASSERT(prot  > PROT_NONE && prot < PROT_LAST);
314       conn->data_prot = prot;
315 #endif
316       if(res == CURLE_AGAIN)
317         return CURLE_OK; /* return */
318
319       if((res == CURLE_OK) && (gotbytes > 0))
320         /* convert from the network encoding */
321         res = Curl_convert_from_network(data, ptr, gotbytes);
322       /* Curl_convert_from_network calls failf if unsuccessful */
323
324       if(CURLE_OK != res) {
325         result = (CURLcode)res; /* Set outer result variable to this error. */
326         keepon = FALSE;
327       }
328     }
329
330     if(!keepon)
331       ;
332     else if(gotbytes <= 0) {
333       keepon = FALSE;
334       result = CURLE_RECV_ERROR;
335       failf(data, "response reading failed");
336     }
337     else {
338       /* we got a whole chunk of data, which can be anything from one
339        * byte to a set of lines and possible just a piece of the last
340        * line */
341       ssize_t i;
342       ssize_t clipamount = 0;
343       bool restart = FALSE;
344
345       data->req.headerbytecount += (long)gotbytes;
346
347       pp->nread_resp += gotbytes;
348       for(i = 0; i < gotbytes; ptr++, i++) {
349         perline++;
350         if(*ptr=='\n') {
351           /* a newline is CRLF in pp-talk, so the CR is ignored as
352              the line isn't really terminated until the LF comes */
353
354           /* output debug output if that is requested */
355 #ifdef HAVE_GSSAPI
356           if(!conn->sec_complete)
357 #endif
358             if(data->set.verbose)
359               Curl_debug(data, CURLINFO_HEADER_IN,
360                          pp->linestart_resp, (size_t)perline, conn);
361
362           /*
363            * We pass all response-lines to the callback function registered
364            * for "headers". The response lines can be seen as a kind of
365            * headers.
366            */
367           result = Curl_client_write(conn, CLIENTWRITE_HEADER,
368                                      pp->linestart_resp, perline);
369           if(result)
370             return result;
371
372           if(pp->endofresp(conn, pp->linestart_resp, perline, code)) {
373             /* This is the end of the last line, copy the last line to the
374                start of the buffer and zero terminate, for old times sake */
375             char *meow;
376             int n;
377             for(meow=pp->linestart_resp, n=0; meow<ptr; meow++, n++)
378               buf[n] = *meow;
379             *meow=0; /* zero terminate */
380             keepon=FALSE;
381             pp->linestart_resp = ptr+1; /* advance pointer */
382             i++; /* skip this before getting out */
383
384             *size = pp->nread_resp; /* size of the response */
385             pp->nread_resp = 0; /* restart */
386             break;
387           }
388           perline=0; /* line starts over here */
389           pp->linestart_resp = ptr+1;
390         }
391       }
392
393       if(!keepon && (i != gotbytes)) {
394         /* We found the end of the response lines, but we didn't parse the
395            full chunk of data we have read from the server. We therefore need
396            to store the rest of the data to be checked on the next invoke as
397            it may actually contain another end of response already! */
398         clipamount = gotbytes - i;
399         restart = TRUE;
400         DEBUGF(infof(data, "Curl_pp_readresp_ %d bytes of trailing "
401                      "server response left\n",
402                      (int)clipamount));
403       }
404       else if(keepon) {
405
406         if((perline == gotbytes) && (gotbytes > BUFSIZE/2)) {
407           /* We got an excessive line without newlines and we need to deal
408              with it. We keep the first bytes of the line then we throw
409              away the rest. */
410           infof(data, "Excessive server response line length received, "
411                 "%zd bytes. Stripping\n", gotbytes);
412           restart = TRUE;
413
414           /* we keep 40 bytes since all our pingpong protocols are only
415              interested in the first piece */
416           clipamount = 40;
417         }
418         else if(pp->nread_resp > BUFSIZE/2) {
419           /* We got a large chunk of data and there's potentially still
420              trailing data to take care of, so we put any such part in the
421              "cache", clear the buffer to make space and restart. */
422           clipamount = perline;
423           restart = TRUE;
424         }
425       }
426       else if(i == gotbytes)
427         restart = TRUE;
428
429       if(clipamount) {
430         pp->cache_size = clipamount;
431         pp->cache = malloc(pp->cache_size);
432         if(pp->cache)
433           memcpy(pp->cache, pp->linestart_resp, pp->cache_size);
434         else
435           return CURLE_OUT_OF_MEMORY;
436       }
437       if(restart) {
438         /* now reset a few variables to start over nicely from the start of
439            the big buffer */
440         pp->nread_resp = 0; /* start over from scratch in the buffer */
441         ptr = pp->linestart_resp = buf;
442         perline = 0;
443       }
444
445     } /* there was data */
446
447   } /* while there's buffer left and loop is requested */
448
449   pp->pending_resp = FALSE;
450
451   return result;
452 }
453
454 int Curl_pp_getsock(struct pingpong *pp,
455                     curl_socket_t *socks,
456                     int numsocks)
457 {
458   struct connectdata *conn = pp->conn;
459
460   if(!numsocks)
461     return GETSOCK_BLANK;
462
463   socks[0] = conn->sock[FIRSTSOCKET];
464
465   if(pp->sendleft) {
466     /* write mode */
467     return GETSOCK_WRITESOCK(0);
468   }
469
470   /* read mode */
471   return GETSOCK_READSOCK(0);
472 }
473
474 CURLcode Curl_pp_flushsend(struct pingpong *pp)
475 {
476   /* we have a piece of a command still left to send */
477   struct connectdata *conn = pp->conn;
478   ssize_t written;
479   CURLcode result = CURLE_OK;
480   curl_socket_t sock = conn->sock[FIRSTSOCKET];
481
482   result = Curl_write(conn, sock, pp->sendthis + pp->sendsize -
483                       pp->sendleft, pp->sendleft, &written);
484   if(result)
485     return result;
486
487   if(written != (ssize_t)pp->sendleft) {
488     /* only a fraction was sent */
489     pp->sendleft -= written;
490   }
491   else {
492     free(pp->sendthis);
493     pp->sendthis=NULL;
494     pp->sendleft = pp->sendsize = 0;
495     pp->response = Curl_tvnow();
496   }
497   return CURLE_OK;
498 }
499
500 CURLcode Curl_pp_disconnect(struct pingpong *pp)
501 {
502   if(pp->cache) {
503     free(pp->cache);
504     pp->cache = NULL;
505   }
506   return CURLE_OK;
507 }
508
509 bool Curl_pp_moredata(struct pingpong *pp)
510 {
511   return (!pp->sendleft && pp->cache && pp->nread_resp < pp->cache_size) ?
512          TRUE : FALSE;
513 }
514
515 #endif