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 */
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 * Curl_pp_statemach()
81 */
82CURLcode 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 */
135void 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 */
156CURLcode 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#if defined(HAVE_KRB4) || defined(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#if defined(HAVE_KRB4) || defined(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#if defined(HAVE_KRB4) || defined(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 */
243CURLcode 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 */
262CURLcode 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#if defined(HAVE_KRB4) || defined(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#if defined(HAVE_KRB4) || defined(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#if defined(HAVE_KRB4) || defined(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 (and
375               krb4)! */
376            char *meow;
377            int n;
378            for(meow=pp->linestart_resp, n=0; meow<ptr; meow++, n++)
379              buf[n] = *meow;
380            *meow=0; /* zero terminate */
381            keepon=FALSE;
382            pp->linestart_resp = ptr+1; /* advance pointer */
383            i++; /* skip this before getting out */
384
385            *size = pp->nread_resp; /* size of the response */
386            pp->nread_resp = 0; /* restart */
387            break;
388          }
389          perline=0; /* line starts over here */
390          pp->linestart_resp = ptr+1;
391        }
392      }
393
394      if(!keepon && (i != gotbytes)) {
395        /* We found the end of the response lines, but we didn't parse the
396           full chunk of data we have read from the server. We therefore need
397           to store the rest of the data to be checked on the next invoke as
398           it may actually contain another end of response already! */
399        clipamount = gotbytes - i;
400        restart = TRUE;
401        DEBUGF(infof(data, "Curl_pp_readresp_ %d bytes of trailing "
402                     "server response left\n",
403                     (int)clipamount));
404      }
405      else if(keepon) {
406
407        if((perline == gotbytes) && (gotbytes > BUFSIZE/2)) {
408          /* We got an excessive line without newlines and we need to deal
409             with it. We keep the first bytes of the line then we throw
410             away the rest. */
411          infof(data, "Excessive server response line length received, "
412                "%zd bytes. Stripping\n", gotbytes);
413          restart = TRUE;
414
415          /* we keep 40 bytes since all our pingpong protocols are only
416             interested in the first piece */
417          clipamount = 40;
418        }
419        else if(pp->nread_resp > BUFSIZE/2) {
420          /* We got a large chunk of data and there's potentially still
421             trailing data to take care of, so we put any such part in the
422             "cache", clear the buffer to make space and restart. */
423          clipamount = perline;
424          restart = TRUE;
425        }
426      }
427      else if(i == gotbytes)
428        restart = TRUE;
429
430      if(clipamount) {
431        pp->cache_size = clipamount;
432        pp->cache = malloc(pp->cache_size);
433        if(pp->cache)
434          memcpy(pp->cache, pp->linestart_resp, pp->cache_size);
435        else
436          return CURLE_OUT_OF_MEMORY;
437      }
438      if(restart) {
439        /* now reset a few variables to start over nicely from the start of
440           the big buffer */
441        pp->nread_resp = 0; /* start over from scratch in the buffer */
442        ptr = pp->linestart_resp = buf;
443        perline = 0;
444      }
445
446    } /* there was data */
447
448  } /* while there's buffer left and loop is requested */
449
450  pp->pending_resp = FALSE;
451
452  return result;
453}
454
455int Curl_pp_getsock(struct pingpong *pp,
456                    curl_socket_t *socks,
457                    int numsocks)
458{
459  struct connectdata *conn = pp->conn;
460
461  if(!numsocks)
462    return GETSOCK_BLANK;
463
464  socks[0] = conn->sock[FIRSTSOCKET];
465
466  if(pp->sendleft) {
467    /* write mode */
468    return GETSOCK_WRITESOCK(0);
469  }
470
471  /* read mode */
472  return GETSOCK_READSOCK(0);
473}
474
475CURLcode Curl_pp_flushsend(struct pingpong *pp)
476{
477  /* we have a piece of a command still left to send */
478  struct connectdata *conn = pp->conn;
479  ssize_t written;
480  CURLcode result = CURLE_OK;
481  curl_socket_t sock = conn->sock[FIRSTSOCKET];
482
483  result = Curl_write(conn, sock, pp->sendthis + pp->sendsize -
484                      pp->sendleft, pp->sendleft, &written);
485  if(result)
486    return result;
487
488  if(written != (ssize_t)pp->sendleft) {
489    /* only a fraction was sent */
490    pp->sendleft -= written;
491  }
492  else {
493    free(pp->sendthis);
494    pp->sendthis=NULL;
495    pp->sendleft = pp->sendsize = 0;
496    pp->response = Curl_tvnow();
497  }
498  return CURLE_OK;
499}
500
501CURLcode Curl_pp_disconnect(struct pingpong *pp)
502{
503  if(pp->cache) {
504    free(pp->cache);
505    pp->cache = NULL;
506  }
507  return CURLE_OK;
508}
509
510bool Curl_pp_moredata(struct pingpong *pp)
511{
512  return (!pp->sendleft && pp->cache && pp->nread_resp < pp->cache_size) ?
513         TRUE : FALSE;
514}
515
516#endif
517