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 : }
|