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