Coverage Report

Created: 2022-12-08 06:10

/src/libassuan/src/client.c
Line
Count
Source (jump to first uncovered line)
1
/* client.c - Functions common to all clients.
2
 * Copyright (C) 2009 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
 * SPDX-License-Identifier: LGPL-2.1+
19
 */
20
21
22
#ifdef HAVE_CONFIG_H
23
#include <config.h>
24
#endif
25
26
#include <stdlib.h>
27
28
#include "assuan-defs.h"
29
#include "debug.h"
30
31
0
#define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
32
0
                     *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
33
0
#define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
34
35
36
void
37
_assuan_client_finish (assuan_context_t ctx)
38
0
{
39
0
  if (ctx->inbound.fd != ASSUAN_INVALID_FD)
40
0
    {
41
0
      _assuan_close (ctx, ctx->inbound.fd);
42
0
      if (ctx->inbound.fd == ctx->outbound.fd)
43
0
        ctx->outbound.fd = ASSUAN_INVALID_FD;
44
0
      ctx->inbound.fd = ASSUAN_INVALID_FD;
45
0
    }
46
0
  if (ctx->outbound.fd != ASSUAN_INVALID_FD)
47
0
    {
48
0
      _assuan_close (ctx, ctx->outbound.fd);
49
0
      ctx->outbound.fd = ASSUAN_INVALID_FD;
50
0
    }
51
0
  if (ctx->pid != ASSUAN_INVALID_PID && ctx->pid)
52
0
    {
53
0
      if (!ctx->flags.is_socket)
54
0
  _assuan_waitpid (ctx, ctx->pid, ctx->flags.no_waitpid, NULL, 0);
55
0
      ctx->pid = ASSUAN_INVALID_PID;
56
0
    }
57
58
0
  _assuan_uds_deinit (ctx);
59
0
}
60
61
62
/* Disconnect and release the context CTX.  */
63
void
64
_assuan_client_release (assuan_context_t ctx)
65
0
{
66
0
  assuan_write_line (ctx, "BYE");
67
68
0
  _assuan_client_finish (ctx);
69
0
}
70
71

72
/* This function also does deescaping for data lines.  */
73
gpg_error_t
74
assuan_client_read_response (assuan_context_t ctx,
75
           char **line_r, int *linelen_r)
76
0
{
77
0
  gpg_error_t rc;
78
0
  char *line = NULL;
79
0
  int linelen = 0;
80
81
0
  *line_r = NULL;
82
0
  *linelen_r = 0;
83
84
0
  do
85
0
    {
86
0
      do
87
0
  {
88
0
    rc = _assuan_read_line (ctx);
89
0
  }
90
0
      while (_assuan_error_is_eagain (ctx, rc));
91
0
      if (rc)
92
0
        return rc;
93
0
      line = ctx->inbound.line;
94
0
      linelen = ctx->inbound.linelen;
95
0
    }
96
0
  while (!linelen);
97
98
  /* For data lines, we deescape immediately.  The user will never
99
     have to worry about it.  */
100
0
  if (linelen >= 1 && line[0] == 'D' && line[1] == ' ')
101
0
    {
102
0
      char *s, *d;
103
0
      for (s=d=line; linelen; linelen--)
104
0
  {
105
0
    if (*s == '%' && linelen > 2)
106
0
      { /* handle escaping */
107
0
        s++;
108
0
        *d++ = xtoi_2 (s);
109
0
        s += 2;
110
0
        linelen -= 2;
111
0
      }
112
0
    else
113
0
      *d++ = *s++;
114
0
  }
115
0
      *d = 0; /* add a hidden string terminator */
116
117
0
      linelen = d - line;
118
0
      ctx->inbound.linelen = linelen;
119
0
    }
120
121
0
  *line_r = line;
122
0
  *linelen_r = linelen;
123
124
0
  return 0;
125
0
}
126
127
128
gpg_error_t
129
assuan_client_parse_response (assuan_context_t ctx, char *line, int linelen,
130
            assuan_response_t *response, int *off)
131
0
{
132
0
  *response = ASSUAN_RESPONSE_ERROR;
133
0
  *off = 0;
134
135
0
  if (linelen >= 1
136
0
      && line[0] == 'D' && line[1] == ' ')
137
0
    {
138
0
      *response = ASSUAN_RESPONSE_DATA; /* data line */
139
0
      *off = 2;
140
0
    }
141
0
  else if (linelen >= 1
142
0
           && line[0] == 'S'
143
0
           && (line[1] == '\0' || line[1] == ' '))
144
0
    {
145
0
      *response = ASSUAN_RESPONSE_STATUS;
146
0
      *off = 1;
147
0
      while (line[*off] == ' ')
148
0
        ++*off;
149
0
    }
150
0
  else if (linelen >= 2
151
0
           && line[0] == 'O' && line[1] == 'K'
152
0
           && (line[2] == '\0' || line[2] == ' '))
153
0
    {
154
0
      *response = ASSUAN_RESPONSE_OK;
155
0
      *off = 2;
156
0
      while (line[*off] == ' ')
157
0
        ++*off;
158
0
    }
159
0
  else if (linelen >= 3
160
0
           && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
161
0
           && (line[3] == '\0' || line[3] == ' '))
162
0
    {
163
0
      *response = ASSUAN_RESPONSE_ERROR;
164
0
      *off = 3;
165
0
      while (line[*off] == ' ')
166
0
        ++*off;
167
0
    }
168
0
  else if (linelen >= 7
169
0
           && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
170
0
           && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
171
0
           && line[6] == 'E'
172
0
           && (line[7] == '\0' || line[7] == ' '))
173
0
    {
174
0
      *response = ASSUAN_RESPONSE_INQUIRE;
175
0
      *off = 7;
176
0
      while (line[*off] == ' ')
177
0
        ++*off;
178
0
    }
179
0
  else if (linelen >= 3
180
0
           && line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
181
0
           && (line[3] == '\0' || line[3] == ' '))
182
0
    {
183
0
      *response = ASSUAN_RESPONSE_END;
184
0
      *off = 3;
185
0
    }
186
0
  else if (linelen >= 1 && line[0] == '#')
187
0
    {
188
0
      *response = ASSUAN_RESPONSE_COMMENT;
189
0
      *off = 1;
190
0
    }
191
0
  else
192
0
    return _assuan_error (ctx, GPG_ERR_ASS_INV_RESPONSE);
193
194
0
  return 0;
195
0
}
196
197
198
gpg_error_t
199
_assuan_read_from_server (assuan_context_t ctx, assuan_response_t *response,
200
        int *off, int convey_comments)
201
0
{
202
0
  gpg_error_t rc;
203
0
  char *line;
204
0
  int linelen;
205
206
0
  do
207
0
    {
208
0
      *response = ASSUAN_RESPONSE_ERROR;
209
0
      *off = 0;
210
0
      rc = assuan_client_read_response (ctx, &line, &linelen);
211
0
      if (!rc)
212
0
        rc = assuan_client_parse_response (ctx, line, linelen, response, off);
213
0
    }
214
0
  while (!rc && *response == ASSUAN_RESPONSE_COMMENT && !convey_comments);
215
216
0
  return rc;
217
0
}
218
219

220
/**
221
 * assuan_transact:
222
 * @ctx: The Assuan context
223
 * @command: Command line to be send to the server
224
 * @data_cb: Callback function for data lines
225
 * @data_cb_arg: first argument passed to @data_cb
226
 * @inquire_cb: Callback function for a inquire response
227
 * @inquire_cb_arg: first argument passed to @inquire_cb
228
 * @status_cb: Callback function for a status response
229
 * @status_cb_arg: first argument passed to @status_cb
230
 *
231
 * FIXME: Write documentation
232
 *
233
 * Return value: 0 on success or an error code.  The error code may be
234
 * the one one returned by the server via error lines or from the
235
 * callback functions.  Take care:  If a callback returns an error
236
 * this function returns immediately with this error.
237
 **/
238
gpg_error_t
239
assuan_transact (assuan_context_t ctx,
240
                 const char *command,
241
                 gpg_error_t (*data_cb)(void *, const void *, size_t),
242
                 void *data_cb_arg,
243
                 gpg_error_t (*inquire_cb)(void*, const char *),
244
                 void *inquire_cb_arg,
245
                 gpg_error_t (*status_cb)(void*, const char *),
246
                 void *status_cb_arg)
247
0
{
248
0
  gpg_error_t rc;
249
0
  assuan_response_t response;
250
0
  int off;
251
0
  char *line;
252
0
  int linelen;
253
254
0
  rc = assuan_write_line (ctx, command);
255
0
  if (rc)
256
0
    return rc;
257
258
0
  if (*command == '#' || !*command)
259
0
    return 0; /* Don't expect a response for a comment line.  */
260
261
0
 again:
262
0
  rc = _assuan_read_from_server (ctx, &response, &off,
263
0
                                 ctx->flags.convey_comments);
264
0
  if (rc)
265
0
    return rc; /* error reading from server */
266
267
0
  line = ctx->inbound.line + off;
268
0
  linelen = ctx->inbound.linelen - off;
269
270
0
  if (response == ASSUAN_RESPONSE_ERROR)
271
0
    rc = atoi (line);
272
0
  else if (response == ASSUAN_RESPONSE_DATA)
273
0
    {
274
0
      if (!data_cb)
275
0
        rc = _assuan_error (ctx, GPG_ERR_ASS_NO_DATA_CB);
276
0
      else
277
0
        {
278
0
          rc = data_cb (data_cb_arg, line, linelen);
279
0
          if (ctx->flags.confidential)
280
0
            wipememory (ctx->inbound.line, LINELENGTH);
281
0
          if (!rc)
282
0
            goto again;
283
0
        }
284
0
    }
285
0
  else if (response == ASSUAN_RESPONSE_INQUIRE)
286
0
    {
287
0
      if (!inquire_cb)
288
0
        {
289
0
          assuan_write_line (ctx, "END"); /* get out of inquire mode */
290
0
          _assuan_read_from_server (ctx, &response, &off, 0); /* dummy read */
291
0
          rc = _assuan_error (ctx, GPG_ERR_ASS_NO_INQUIRE_CB);
292
0
        }
293
0
      else
294
0
        {
295
0
          ctx->flags.confidential_inquiry = 0;
296
0
          ctx->flags.in_inq_cb = 1;
297
298
0
          rc = inquire_cb (inquire_cb_arg, line);
299
0
          if (!rc)
300
0
            rc = assuan_send_data (ctx, NULL, 0); /* flush and send END */
301
0
          else
302
0
            { /* Flush and send CAN.  */
303
              /* Note that in this error case we don't want to return
304
                 an error code from sending the cancel.  The dummy
305
                 read is to remove the response from the server which
306
                 we are not interested in.  */
307
0
              assuan_send_data (ctx, NULL, 1);
308
0
              _assuan_read_from_server (ctx, &response, &off, 0);
309
0
            }
310
311
0
          if (ctx->flags.confidential_inquiry)
312
0
            wipememory (ctx->outbound.data.line, LINELENGTH);
313
314
0
          ctx->flags.confidential_inquiry = 0;
315
0
          ctx->flags.in_inq_cb = 0;
316
317
0
          if (!rc)
318
0
            goto again;
319
0
        }
320
0
    }
321
0
  else if (response == ASSUAN_RESPONSE_STATUS)
322
0
    {
323
0
      if (status_cb)
324
0
        rc = status_cb (status_cb_arg, line);
325
0
      if (!rc)
326
0
        goto again;
327
0
    }
328
0
  else if (response == ASSUAN_RESPONSE_COMMENT && ctx->flags.convey_comments)
329
0
    {
330
0
      line -= off; /* Send line with the comment marker.  */
331
0
      if (status_cb)
332
0
        rc = status_cb (status_cb_arg, line);
333
0
      if (!rc)
334
0
        goto again;
335
0
    }
336
0
  else if (response == ASSUAN_RESPONSE_END)
337
0
    {
338
0
      if (!data_cb)
339
0
        rc = _assuan_error (ctx, GPG_ERR_ASS_NO_DATA_CB);
340
0
      else
341
0
        {
342
0
          rc = data_cb (data_cb_arg, NULL, 0);
343
0
          if (!rc)
344
0
            goto again;
345
0
        }
346
0
    }
347
348
0
  return rc;
349
0
}