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