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 * made to never block
181 */
182CURLcode Curl_pp_vsendf(struct pingpong *pp,
183                        const char *fmt,
184                        va_list args)
185{
186  ssize_t bytes_written;
187  size_t write_len;
188  char *fmt_crlf;
189  char *s;
190  CURLcode error;
191  struct connectdata *conn = pp->conn;
192  struct SessionHandle *data = conn->data;
193
194#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
195  enum protection_level data_sec = conn->data_prot;
196#endif
197
198  DEBUGASSERT(pp->sendleft == 0);
199  DEBUGASSERT(pp->sendsize == 0);
200  DEBUGASSERT(pp->sendthis == NULL);
201
202  fmt_crlf = aprintf("%s\r\n", fmt); /* append a trailing CRLF */
203  if(!fmt_crlf)
204    return CURLE_OUT_OF_MEMORY;
205
206  s = vaprintf(fmt_crlf, args); /* trailing CRLF appended */
207  free(fmt_crlf);
208  if(!s)
209    return CURLE_OUT_OF_MEMORY;
210
211  bytes_written = 0;
212  write_len = strlen(s);
213
214  Curl_pp_init(pp);
215
216  error = Curl_convert_to_network(data, s, write_len);
217  /* Curl_convert_to_network calls failf if unsuccessful */
218  if(error) {
219    free(s);
220    return error;
221  }
222
223#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
224  conn->data_prot = PROT_CMD;
225#endif
226  error = Curl_write(conn, conn->sock[FIRSTSOCKET], s, write_len,
227                     &bytes_written);
228#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
229  DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
230  conn->data_prot = data_sec;
231#endif
232
233  if(error) {
234    free(s);
235    return error;
236  }
237
238  if(conn->data->set.verbose)
239    Curl_debug(conn->data, CURLINFO_HEADER_OUT,
240               s, (size_t)bytes_written, conn);
241
242  if(bytes_written != (ssize_t)write_len) {
243    /* the whole chunk was not sent, keep it around and adjust sizes */
244    pp->sendthis = s;
245    pp->sendsize = write_len;
246    pp->sendleft = write_len - bytes_written;
247  }
248  else {
249    free(s);
250    pp->sendthis = NULL;
251    pp->sendleft = pp->sendsize = 0;
252    pp->response = Curl_tvnow();
253  }
254
255  return CURLE_OK;
256}
257
258
259/***********************************************************************
260 *
261 * Curl_pp_sendf()
262 *
263 * Send the formated string as a command to a pingpong server. Note that
264 * the string should not have any CRLF appended, as this function will
265 * append the necessary things itself.
266 *
267 * made to never block
268 */
269CURLcode Curl_pp_sendf(struct pingpong *pp,
270                       const char *fmt, ...)
271{
272  CURLcode res;
273  va_list ap;
274  va_start(ap, fmt);
275
276  res = Curl_pp_vsendf(pp, fmt, ap);
277
278  va_end(ap);
279
280  return res;
281}
282
283/*
284 * Curl_pp_readresp()
285 *
286 * Reads a piece of a server response.
287 */
288CURLcode Curl_pp_readresp(curl_socket_t sockfd,
289                          struct pingpong *pp,
290                          int *code, /* return the server code if done */
291                          size_t *size) /* size of the response */
292{
293  ssize_t perline; /* count bytes per line */
294  bool keepon=TRUE;
295  ssize_t gotbytes;
296  char *ptr;
297  struct connectdata *conn = pp->conn;
298  struct SessionHandle *data = conn->data;
299  char * const buf = data->state.buffer;
300  CURLcode result = CURLE_OK;
301
302  *code = 0; /* 0 for errors or not done */
303  *size = 0;
304
305  ptr=buf + pp->nread_resp;
306
307  /* number of bytes in the current line, so far */
308  perline = (ssize_t)(ptr-pp->linestart_resp);
309
310  keepon=TRUE;
311
312  while((pp->nread_resp<BUFSIZE) && (keepon && !result)) {
313
314    if(pp->cache) {
315      /* we had data in the "cache", copy that instead of doing an actual
316       * read
317       *
318       * pp->cache_size is cast to ssize_t here.  This should be safe, because
319       * it would have been populated with something of size int to begin
320       * with, even though its datatype may be larger 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 pp-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