LCOV - code coverage report
Current view: top level - src - assuan-buffer.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 144 230 62.6 %
Date: 2016-09-12 12:52:30 Functions: 10 12 83.3 %

          Line data    Source code
       1             : /* assuan-buffer.c - read and send data
       2             :    Copyright (C) 2001-2004, 2006, 2009, 2010 Free Software Foundation, Inc.
       3             : 
       4             :    This file is part of Assuan.
       5             : 
       6             :    Assuan is free software; you can redistribute it and/or modify it
       7             :    under the terms of the GNU Lesser General Public License as
       8             :    published by the Free Software Foundation; either version 2.1 of
       9             :    the License, or (at your option) any later version.
      10             : 
      11             :    Assuan is distributed in the hope that it will be useful, but
      12             :    WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14             :    Lesser General Public License for more details.
      15             : 
      16             :    You should have received a copy of the GNU Lesser General Public
      17             :    License along with this program; if not, see <http://www.gnu.org/licenses/>.
      18             :  */
      19             : 
      20             : #include <config.h>
      21             : #include <stdlib.h>
      22             : #include <string.h>
      23             : #include <stdio.h>
      24             : #include <errno.h>
      25             : #ifdef HAVE_UNISTD_H
      26             : # include <unistd.h>
      27             : #endif
      28             : #include <assert.h>
      29             : #ifdef HAVE_W32_SYSTEM
      30             : #ifndef HAVE_W32CE_SYSTEM
      31             : # include <process.h>
      32             : #endif
      33             : #endif
      34             : #include "assuan-defs.h"
      35             : 
      36             : 
      37             : /* Extended version of write(2) to guarantee that all bytes are
      38             :    written.  Returns 0 on success or -1 and ERRNO on failure.  NOTE:
      39             :    This function does not return the number of bytes written, so any
      40             :    error must be treated as fatal for this connection as the state of
      41             :    the receiver is unknown.  This works best if blocking is allowed
      42             :    (so EAGAIN cannot occur).  */
      43             : static int
      44          65 : writen (assuan_context_t ctx, const char *buffer, size_t length)
      45             : {
      46         193 :   while (length)
      47             :     {
      48          65 :       ssize_t nwritten = ctx->engine.writefnc (ctx, buffer, length);
      49             : 
      50          65 :       if (nwritten < 0)
      51             :         {
      52           2 :           if (errno == EINTR)
      53           0 :             continue;
      54           2 :           return -1; /* write error */
      55             :         }
      56          63 :       length -= nwritten;
      57          63 :       buffer += nwritten;
      58             :     }
      59          63 :   return 0;  /* okay */
      60             : }
      61             : 
      62             : /* Read an entire line. Returns 0 on success or -1 and ERRNO on
      63             :    failure.  EOF is indictated by setting the integer at address
      64             :    R_EOF.  Note: BUF, R_NREAD and R_EOF contain a valid result even if
      65             :    an error is returned.  */
      66             : static int
      67          37 : readline (assuan_context_t ctx, char *buf, size_t buflen,
      68             :           int *r_nread, int *r_eof)
      69             : {
      70          37 :   size_t nleft = buflen;
      71             :   char *p;
      72             : 
      73          37 :   *r_eof = 0;
      74          37 :   *r_nread = 0;
      75          74 :   while (nleft > 0)
      76             :     {
      77          37 :       ssize_t n = ctx->engine.readfnc (ctx, buf, nleft);
      78             : 
      79          37 :       if (n < 0)
      80             :         {
      81           0 :           if (errno == EINTR)
      82           0 :             continue;
      83             : #ifdef HAVE_W32_SYSTEM
      84             :           if (errno == EPIPE)
      85             :             {
      86             :               /* Under Windows we get EPIPE (actually ECONNRESET)
      87             :                  after termination of the client.  Assume an EOF.  */
      88             :               *r_eof = 1;
      89             :               break; /* allow incomplete lines */
      90             :             }
      91             : #endif /*HAVE_W32_SYSTEM*/
      92           0 :           return -1; /* read error */
      93             :         }
      94          37 :       else if (!n)
      95             :         {
      96           0 :           *r_eof = 1;
      97           0 :           break; /* allow incomplete lines */
      98             :         }
      99             : 
     100          37 :       p = buf;
     101          37 :       nleft -= n;
     102          37 :       buf += n;
     103          37 :       *r_nread += n;
     104             : 
     105          37 :       p = memrchr (p, '\n', n);
     106          37 :       if (p)
     107          37 :         break; /* at least one full line available - that's enough for now */
     108             :     }
     109          37 :   return 0;
     110             : }
     111             : 
     112             : 
     113             : /* Read a line with buffering of partial lines.  Function returns an
     114             :    Assuan error.  */
     115             : gpg_error_t
     116          38 : _assuan_read_line (assuan_context_t ctx)
     117             : {
     118          38 :   gpg_error_t rc = 0;
     119          38 :   char *line = ctx->inbound.line;
     120             :   int nread, atticlen;
     121          38 :   char *endp = 0;
     122             : 
     123          38 :   if (ctx->inbound.eof)
     124           0 :     return _assuan_error (ctx, GPG_ERR_EOF);
     125             : 
     126          38 :   atticlen = ctx->inbound.attic.linelen;
     127          38 :   if (atticlen)
     128             :     {
     129           1 :       memcpy (line, ctx->inbound.attic.line, atticlen);
     130           1 :       ctx->inbound.attic.linelen = 0;
     131             : 
     132           1 :       endp = memchr (line, '\n', atticlen);
     133           1 :       if (endp)
     134             :         {
     135             :           /* Found another line in the attic.  */
     136           1 :           nread = atticlen;
     137           1 :           atticlen = 0;
     138             :         }
     139             :       else
     140             :         {
     141             :           /* There is pending data but not a full line.  */
     142           0 :           assert (atticlen < LINELENGTH);
     143           0 :           rc = readline (ctx, line + atticlen,
     144           0 :                          LINELENGTH - atticlen, &nread, &ctx->inbound.eof);
     145             :         }
     146             :     }
     147             :   else
     148             :     /* No pending data.  */
     149          37 :     rc = readline (ctx, line, LINELENGTH,
     150             :                    &nread, &ctx->inbound.eof);
     151          38 :   if (rc)
     152             :     {
     153           0 :       int saved_errno = errno;
     154             :       char buf[100];
     155             : 
     156           0 :       snprintf (buf, sizeof buf, "error: %s", strerror (errno));
     157           0 :       _assuan_log_control_channel (ctx, 0, buf, NULL, 0, NULL, 0);
     158             : 
     159           0 :       if (saved_errno == EAGAIN)
     160             :         {
     161             :           /* We have to save a partial line.  Due to readline's
     162             :              behaviour, we know that this is not a complete line yet
     163             :              (no newline).  So we don't set PENDING to true.  */
     164           0 :           memcpy (ctx->inbound.attic.line, line, atticlen + nread);
     165           0 :           ctx->inbound.attic.pending = 0;
     166           0 :           ctx->inbound.attic.linelen = atticlen + nread;
     167             :         }
     168             : 
     169           0 :       gpg_err_set_errno (saved_errno);
     170           0 :       return _assuan_error (ctx, gpg_err_code_from_syserror ());
     171             :     }
     172          38 :   if (!nread)
     173             :     {
     174           0 :       assert (ctx->inbound.eof);
     175           0 :       _assuan_log_control_channel (ctx, 0, "eof", NULL, 0, NULL, 0);
     176           0 :       return _assuan_error (ctx, GPG_ERR_EOF);
     177             :     }
     178             : 
     179          38 :   ctx->inbound.attic.pending = 0;
     180          38 :   nread += atticlen;
     181             : 
     182          38 :   if (! endp)
     183          37 :     endp = memchr (line, '\n', nread);
     184             : 
     185          38 :   if (endp)
     186             :     {
     187             :       unsigned monitor_result;
     188          38 :       int n = endp - line + 1;
     189             : 
     190          38 :       if (n < nread)
     191             :         /* LINE contains more than one line.  We copy it to the attic
     192             :            now as handlers are allowed to modify the passed
     193             :            buffer.  */
     194             :         {
     195           1 :           int len = nread - n;
     196           1 :           memcpy (ctx->inbound.attic.line, endp + 1, len);
     197           1 :           ctx->inbound.attic.pending = memrchr (endp + 1, '\n', len) ? 1 : 0;
     198           1 :           ctx->inbound.attic.linelen = len;
     199             :         }
     200             : 
     201          38 :       if (endp != line && endp[-1] == '\r')
     202           0 :         endp --;
     203          38 :       *endp = 0;
     204             : 
     205          38 :       ctx->inbound.linelen = endp - line;
     206             : 
     207          38 :       monitor_result = 0;
     208          38 :       if (ctx->io_monitor)
     209           0 :         monitor_result = ctx->io_monitor (ctx, ctx->io_monitor_data, 0,
     210           0 :                                           ctx->inbound.line,
     211           0 :                                           ctx->inbound.linelen);
     212          38 :       if (monitor_result & ASSUAN_IO_MONITOR_IGNORE)
     213           0 :         ctx->inbound.linelen = 0;
     214             : 
     215          38 :       if ( !(monitor_result & ASSUAN_IO_MONITOR_NOLOG))
     216          76 :         _assuan_log_control_channel (ctx, 0, NULL,
     217          76 :                                      ctx->inbound.line, ctx->inbound.linelen,
     218             :                                      NULL, 0);
     219          38 :       return 0;
     220             :     }
     221             :   else
     222             :     {
     223           0 :       _assuan_log_control_channel (ctx, 0, "invalid line",
     224             :                                    NULL, 0, NULL, 0);
     225           0 :       *line = 0;
     226           0 :       ctx->inbound.linelen = 0;
     227           0 :       return _assuan_error (ctx, ctx->inbound.eof
     228             :                             ? GPG_ERR_ASS_INCOMPLETE_LINE
     229             :                             : GPG_ERR_ASS_LINE_TOO_LONG);
     230             :     }
     231             : }
     232             : 
     233             : 
     234             : /* Read the next line from the client or server and return a pointer
     235             :    in *LINE to a buffer holding the line.  LINELEN is the length of
     236             :    *LINE.  The buffer is valid until the next read operation on it.
     237             :    The caller may modify the buffer.  The buffer is invalid (i.e. must
     238             :    not be used) if an error is returned.
     239             : 
     240             :    Returns 0 on success or an assuan error code.
     241             :    See also: assuan_pending_line().
     242             : */
     243             : gpg_error_t
     244           0 : assuan_read_line (assuan_context_t ctx, char **line, size_t *linelen)
     245             : {
     246             :   gpg_error_t err;
     247             : 
     248           0 :   if (!ctx)
     249           0 :     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
     250             : 
     251             :   do
     252             :     {
     253           0 :       err = _assuan_read_line (ctx);
     254             :     }
     255           0 :   while (_assuan_error_is_eagain (ctx, err));
     256             : 
     257           0 :   *line = ctx->inbound.line;
     258           0 :   *linelen = ctx->inbound.linelen;
     259             : 
     260           0 :   return err;
     261             : }
     262             : 
     263             : 
     264             : /* Return true if a full line is buffered (i.e. an entire line may be
     265             :    read without any I/O).  */
     266             : int
     267           0 : assuan_pending_line (assuan_context_t ctx)
     268             : {
     269           0 :   return ctx && ctx->inbound.attic.pending;
     270             : }
     271             : 
     272             : 
     273             : gpg_error_t
     274          33 : _assuan_write_line (assuan_context_t ctx, const char *prefix,
     275             :                     const char *line, size_t len)
     276             : {
     277          33 :   gpg_error_t rc = 0;
     278          33 :   size_t prefixlen = prefix? strlen (prefix):0;
     279             :   unsigned int monitor_result;
     280             : 
     281             :   /* Make sure that the line is short enough. */
     282          33 :   if (len + prefixlen + 2 > ASSUAN_LINELENGTH)
     283             :     {
     284           0 :       _assuan_log_control_channel (ctx, 1,
     285             :                                    "supplied line too long - truncated",
     286             :                                    NULL, 0, NULL, 0);
     287           0 :       if (prefixlen > 5)
     288           0 :         prefixlen = 5;
     289           0 :       if (len > ASSUAN_LINELENGTH - prefixlen - 2)
     290           0 :         len = ASSUAN_LINELENGTH - prefixlen - 2 - 1;
     291             :     }
     292             : 
     293          33 :   monitor_result = 0;
     294          33 :   if (ctx->io_monitor)
     295           0 :     monitor_result = ctx->io_monitor (ctx, ctx->io_monitor_data, 1, line, len);
     296             : 
     297             :   /* Fixme: we should do some kind of line buffering.  */
     298          33 :   if (!(monitor_result & ASSUAN_IO_MONITOR_NOLOG))
     299          33 :     _assuan_log_control_channel (ctx, 1, NULL,
     300             :                                  prefixlen? prefix:NULL, prefixlen,
     301             :                                  line, len);
     302             : 
     303          33 :   if (prefixlen && !(monitor_result & ASSUAN_IO_MONITOR_IGNORE))
     304             :     {
     305           0 :       rc = writen (ctx, prefix, prefixlen);
     306           0 :       if (rc)
     307           0 :         rc = _assuan_error (ctx, gpg_err_code_from_syserror ());
     308             :     }
     309          33 :   if (!rc && !(monitor_result & ASSUAN_IO_MONITOR_IGNORE))
     310             :     {
     311          33 :       rc = writen (ctx, line, len);
     312          33 :       if (rc)
     313           2 :         rc = _assuan_error (ctx, gpg_err_code_from_syserror ());
     314          33 :       if (!rc)
     315             :         {
     316          31 :           rc = writen (ctx, "\n", 1);
     317          31 :           if (rc)
     318           0 :             rc = _assuan_error (ctx, gpg_err_code_from_syserror ());
     319             :         }
     320             :     }
     321          33 :   return rc;
     322             : }
     323             : 
     324             : 
     325             : gpg_error_t
     326          33 : assuan_write_line (assuan_context_t ctx, const char *line)
     327             : {
     328             :   size_t len;
     329             :   const char *str;
     330             : 
     331          33 :   if (! ctx)
     332           0 :     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
     333             : 
     334             :   /* Make sure that we never take a LF from the user - this might
     335             :      violate the protocol. */
     336          33 :   str = strchr (line, '\n');
     337          33 :   len = str ? (str - line) : strlen (line);
     338             : 
     339          33 :   if (str)
     340           0 :     _assuan_log_control_channel (ctx, 1,
     341             :                                  "supplied line with LF - truncated",
     342             :                                  NULL, 0, NULL, 0);
     343             : 
     344          33 :   return _assuan_write_line (ctx, NULL, line, len);
     345             : }
     346             : 
     347             : 
     348             : 
     349             : /* Write out the data in buffer as datalines with line wrapping and
     350             :    percent escaping.  This function is used for GNU's custom streams. */
     351             : int
     352           1 : _assuan_cookie_write_data (void *cookie, const char *buffer, size_t orig_size)
     353             : {
     354           1 :   assuan_context_t ctx = cookie;
     355           1 :   size_t size = orig_size;
     356             :   char *line;
     357             :   size_t linelen;
     358             : 
     359           1 :   if (ctx->outbound.data.error)
     360           0 :     return 0;
     361             : 
     362           1 :   line = ctx->outbound.data.line;
     363           1 :   linelen = ctx->outbound.data.linelen;
     364           1 :   line += linelen;
     365           3 :   while (size)
     366             :     {
     367             :       unsigned int monitor_result;
     368             : 
     369             :       /* Insert data line header. */
     370           1 :       if (!linelen)
     371             :         {
     372           1 :           *line++ = 'D';
     373           1 :           *line++ = ' ';
     374           1 :           linelen += 2;
     375             :         }
     376             : 
     377             :       /* Copy data, keep space for the CRLF and to escape one character. */
     378          66 :       while (size && linelen < LINELENGTH-2-2)
     379             :         {
     380          64 :           if (*buffer == '%' || *buffer == '\r' || *buffer == '\n')
     381             :             {
     382           0 :               sprintf (line, "%%%02X", *(unsigned char*)buffer);
     383           0 :               line += 3;
     384           0 :               linelen += 3;
     385           0 :               buffer++;
     386             :             }
     387             :           else
     388             :             {
     389          64 :               *line++ = *buffer++;
     390          64 :               linelen++;
     391             :             }
     392          64 :           size--;
     393             :         }
     394             : 
     395             : 
     396           1 :       monitor_result = 0;
     397           1 :       if (ctx->io_monitor)
     398           0 :         monitor_result = ctx->io_monitor (ctx, ctx->io_monitor_data, 1,
     399           0 :                                           ctx->outbound.data.line, linelen);
     400             : 
     401           1 :       if (linelen >= LINELENGTH-2-2)
     402             :         {
     403           0 :           if (!(monitor_result & ASSUAN_IO_MONITOR_NOLOG))
     404           0 :             _assuan_log_control_channel (ctx, 1, NULL,
     405           0 :                                          ctx->outbound.data.line, linelen,
     406             :                                          NULL, 0);
     407             : 
     408           0 :           *line++ = '\n';
     409           0 :           linelen++;
     410           0 :           if ( !(monitor_result & ASSUAN_IO_MONITOR_IGNORE)
     411           0 :                && writen (ctx, ctx->outbound.data.line, linelen))
     412             :             {
     413           0 :               ctx->outbound.data.error = gpg_err_code_from_syserror ();
     414           0 :               return 0;
     415             :             }
     416           0 :           line = ctx->outbound.data.line;
     417           0 :           linelen = 0;
     418             :         }
     419             :     }
     420             : 
     421           1 :   ctx->outbound.data.linelen = linelen;
     422           1 :   return (int) orig_size;
     423             : }
     424             : 
     425             : 
     426             : /* Write out any buffered data
     427             :    This function is used for GNU's custom streams */
     428             : int
     429          15 : _assuan_cookie_write_flush (void *cookie)
     430             : {
     431          15 :   assuan_context_t ctx = cookie;
     432             :   char *line;
     433             :   size_t linelen;
     434             :   unsigned int monitor_result;
     435             : 
     436          15 :   if (ctx->outbound.data.error)
     437           0 :     return 0;
     438             : 
     439          15 :   line = ctx->outbound.data.line;
     440          15 :   linelen = ctx->outbound.data.linelen;
     441          15 :   line += linelen;
     442             : 
     443          15 :   monitor_result = 0;
     444          15 :   if (ctx->io_monitor)
     445           0 :     monitor_result = ctx->io_monitor (ctx, ctx->io_monitor_data, 1,
     446           0 :                                       ctx->outbound.data.line, linelen);
     447             : 
     448          15 :   if (linelen)
     449             :     {
     450           1 :       if (!(monitor_result & ASSUAN_IO_MONITOR_NOLOG))
     451           1 :         _assuan_log_control_channel (ctx, 1, NULL,
     452           1 :                                      ctx->outbound.data.line, linelen,
     453             :                                      NULL, 0);
     454           1 :       *line++ = '\n';
     455           1 :       linelen++;
     456           1 :       if (! (monitor_result & ASSUAN_IO_MONITOR_IGNORE)
     457           1 :            && writen (ctx, ctx->outbound.data.line, linelen))
     458             :         {
     459           0 :           ctx->outbound.data.error = gpg_err_code_from_syserror ();
     460           0 :           return 0;
     461             :         }
     462           1 :       ctx->outbound.data.linelen = 0;
     463             :     }
     464          15 :   return 0;
     465             : }
     466             : 
     467             : 
     468             : /**
     469             :  * assuan_send_data:
     470             :  * @ctx: An assuan context
     471             :  * @buffer: Data to send or NULL to flush
     472             :  * @length: length of the data to send/
     473             :  *
     474             :  * This function may be used by the server or the client to send data
     475             :  * lines.  The data will be escaped as required by the Assuan protocol
     476             :  * and may get buffered until a line is full.  To force sending the
     477             :  * data out @buffer may be passed as NULL (in which case @length must
     478             :  * also be 0); however when used by a client this flush operation does
     479             :  * also send the terminating "END" command to terminate the response on
     480             :  * a INQUIRE response.  However, when assuan_transact() is used, this
     481             :  * function takes care of sending END itself.
     482             :  *
     483             :  * If BUFFER is NULL and LENGTH is 1 and we are a client, a "CAN" is
     484             :  * send instead of an "END".
     485             :  *
     486             :  * Return value: 0 on success or an error code
     487             :  **/
     488             : 
     489             : gpg_error_t
     490          16 : assuan_send_data (assuan_context_t ctx, const void *buffer, size_t length)
     491             : {
     492          16 :   if (!ctx)
     493           0 :     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
     494          16 :   if (!buffer && length > 1)
     495           0 :     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
     496             : 
     497          16 :   if (!buffer)
     498             :     { /* flush what we have */
     499          15 :       _assuan_cookie_write_flush (ctx);
     500          15 :       if (ctx->outbound.data.error)
     501           0 :         return ctx->outbound.data.error;
     502          15 :       if (!ctx->is_server)
     503           0 :         return assuan_write_line (ctx, length == 1? "CAN":"END");
     504             :     }
     505             :   else
     506             :     {
     507           1 :       _assuan_cookie_write_data (ctx, buffer, length);
     508           1 :       if (ctx->outbound.data.error)
     509           0 :         return ctx->outbound.data.error;
     510             :     }
     511             : 
     512          16 :   return 0;
     513             : }
     514             : 
     515             : gpg_error_t
     516           6 : assuan_sendfd (assuan_context_t ctx, assuan_fd_t fd)
     517             : {
     518             :   /* It is explicitly allowed to use (NULL, -1) as a runtime test to
     519             :      check whether descriptor passing is available. */
     520           6 :   if (!ctx && fd == ASSUAN_INVALID_FD)
     521             : #ifdef USE_DESCRIPTOR_PASSING
     522           0 :     return 0;
     523             : #else
     524             :   return _assuan_error (ctx, GPG_ERR_NOT_IMPLEMENTED);
     525             : #endif
     526             : 
     527           6 :   if (!ctx)
     528           0 :     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
     529             : 
     530           6 :   if (! ctx->engine.sendfd)
     531           0 :     return set_error (ctx, GPG_ERR_NOT_IMPLEMENTED,
     532             :                       "server does not support sending and receiving "
     533             :                       "of file descriptors");
     534           6 :   return ctx->engine.sendfd (ctx, fd);
     535             : }
     536             : 
     537             : gpg_error_t
     538           6 : assuan_receivefd (assuan_context_t ctx, assuan_fd_t *fd)
     539             : {
     540           6 :   if (!ctx)
     541           0 :     return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
     542             : 
     543           6 :   if (! ctx->engine.receivefd)
     544           0 :     return set_error (ctx, GPG_ERR_NOT_IMPLEMENTED,
     545             :                       "server does not support sending and receiving "
     546             :                       "of file descriptors");
     547           6 :   return ctx->engine.receivefd (ctx, fd);
     548             : }

Generated by: LCOV version 1.11