1 /* ldap-wrapper-ce.c - LDAP access via W32 threads
2 * Copyright (C) 2010 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
6 * GnuPG is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * GnuPG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <https://www.gnu.org/licenses/>.
21 Alternative wrapper for use with WindowsCE. Under WindowsCE the
22 number of processes is strongly limited (32 processes including the
23 kernel processes) and thus we don't use the process approach but
24 implement a wrapper based on native threads.
26 See ldap-wrapper.c for the standard wrapper interface.
43 #include "ldap-wrapper.h"
45 #ifdef USE_LDAPWRAPPER
46 # error This module is not expected to be build.
51 /* Read a fixed amount of data from READER into BUFFER. */
53 read_buffer (ksba_reader_t reader, unsigned char *buffer, size_t count)
60 err = ksba_reader_read (reader, buffer, count, &nread);
72 /* Start the reaper thread for this wrapper. */
74 ldap_wrapper_launch_thread (void)
83 /* Wait until all ldap wrappers have terminated. We assume that the
84 kill has already been sent to all of them. */
86 ldap_wrapper_wait_connections ()
92 /* Cleanup all resources held by the connection associated with
93 CTRL. This is used after a cancel to kill running wrappers. */
95 ldap_wrapper_connection_cleanup (ctrl_t ctrl)
104 /* The cookie we use to implement the outstream of the wrapper thread. */
105 struct outstream_cookie_s
107 int refcount; /* Reference counter - possible values are 1 and 2. */
109 /* We don't need a mutex for the conditions, as npth provides a
110 simpler condition interface that relies on the global lock. This
111 can be used if we never yield between testing the condition and
113 npth_cond_t wait_data; /* Condition that data is available. */
114 npth_cond_t wait_space; /* Condition that space is available. */
116 int eof_seen; /* EOF indicator. */
117 char buffer[4000]; /* Data ring buffer. */
118 size_t buffer_len; /* The amount of data in the BUFFER. */
119 size_t buffer_pos; /* The next read position of the BUFFER. */
120 size_t buffer_read_pos; /* The next read position of the BUFFER. */
123 #define BUFFER_EMPTY(c) ((c)->buffer_len == 0)
124 #define BUFFER_FULL(c) ((c)->buffer_len == DIM((c)->buffer))
125 #define BUFFER_DATA_AVAILABLE(c) ((c)->buffer_len)
126 #define BUFFER_SPACE_AVAILABLE(c) (DIM((c)->buffer) - (c)->buffer_len)
127 #define BUFFER_INC_POS(c,n) (c)->buffer_pos = ((c)->buffer_pos + (n)) % DIM((c)->buffer)
128 #define BUFFER_CUR_POS(c) (&(c)->buffer[(c)->buffer_pos])
129 #define BUFFER_INC_READ_POS(c,n) (c)->buffer_read_pos = ((c)->buffer_read_pos + (n)) % DIM((c)->buffer)
130 #define BUFFER_CUR_READ_POS(c) (&(c)->buffer[(c)->buffer_read_pos])
133 buffer_get_data (struct outstream_cookie_s *cookie, char *dst, int cnt)
140 if (BUFFER_DATA_AVAILABLE (cookie) < amount)
141 amount = BUFFER_DATA_AVAILABLE (cookie);
144 /* How large is the part up to the end of the buffer array? */
145 chunk = DIM(cookie->buffer) - cookie->buffer_pos;
149 memcpy (dst, BUFFER_CUR_READ_POS (cookie), chunk);
150 BUFFER_INC_READ_POS (cookie, chunk);
156 memcpy (dst, BUFFER_CUR_READ_POS (cookie), left);
157 BUFFER_INC_READ_POS (cookie, left);
165 buffer_put_data (struct outstream_cookie_s *cookie, const char *src, int cnt)
172 remain = DIM(cookie->buffer) - cookie->buffer_len;
179 /* How large is the part up to the end of the buffer array? */
180 chunk = DIM(cookie->buffer) - cookie->buffer_pos;
184 memcpy (BUFFER_CUR_POS (cookie), src, chunk);
185 BUFFER_INC_POS (cookie, chunk);
191 memcpy (BUFFER_CUR_POS (cookie), src, left);
192 BUFFER_INC_POS (cookie, left);
195 cookie->buffer_len -= amount;
200 /* The writer function for the outstream. This is used to transfer
201 the output of the ldap wrapper thread to the ksba reader object. */
203 outstream_cookie_writer (void *cookie_arg, const void *buffer, size_t size)
205 struct outstream_cookie_s *cookie = cookie_arg;
207 ssize_t nwritten = 0;
216 /* Wait for free space. */
217 while (BUFFER_FULL(cookie))
219 /* Buffer is full: Wait for space. */
220 res = npth_cond_wait (&cookie->wait_space, NULL);
223 gpg_err_set_errno (res);
228 if (BUFFER_EMPTY(cookie))
232 nwritten = buffer_put_data (cookie, buffer, size);
238 npth_cond_signal (&cookie->wait_data);
240 while (size); /* Until done. */
247 outstream_release_cookie (struct outstream_cookie_s *cookie)
250 if (!cookie->refcount)
252 npth_cond_destroy (&cookie->wait_data);
253 npth_cond_destroy (&cookie->wait_space);
259 /* Closer function for the outstream. This deallocates the cookie if
260 it won't be used anymore. */
262 outstream_cookie_closer (void *cookie_arg)
264 struct outstream_cookie_s *cookie = cookie_arg;
267 return 0; /* Nothing to do. */
269 cookie->eof_seen = 1; /* (only useful if refcount > 1) */
271 assert (cookie->refcount > 0);
272 outstream_release_cookie (cookie);
277 /* The KSBA reader callback which takes the output of the ldap thread
278 form the outstream_cookie_writer and make it available to the ksba
281 outstream_reader_cb (void *cb_value, char *buffer, size_t count,
284 struct outstream_cookie_s *cookie = cb_value;
288 if (!buffer && !count && !r_nread)
289 return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Rewind is not supported. */
293 while (BUFFER_EMPTY(cookie))
295 if (cookie->eof_seen)
296 return gpg_error (GPG_ERR_EOF);
298 /* Wait for data to become available. */
299 npth_cond_wait (&cookie->wait_data, NULL);
302 if (BUFFER_FULL(cookie))
305 nread = buffer_get_data (cookie, buffer, count);
309 npth_cond_signal (&cookie->wait_space);
313 return 0; /* Success. */
317 /* This function is called by ksba_reader_release. */
319 outstream_reader_released (void *cb_value, ksba_reader_t r)
321 struct outstream_cookie_s *cookie = cb_value;
325 assert (cookie->refcount > 0);
326 outstream_release_cookie (cookie);
331 /* This function is to be used to release a context associated with the
332 given reader object. This does not release the reader object, though. */
334 ldap_wrapper_release_context (ksba_reader_t reader)
342 /* Free a NULL terminated array of malloced strings and the array
345 free_arg_list (char **arg_list)
351 for (i=0; arg_list[i]; i++)
358 /* Copy ARGV into a new array and prepend one element as name of the
359 program (which is more or less a stub). We need to allocate all
360 the strings to get ownership of them. */
362 create_arg_list (const char *argv[], char ***r_arg_list)
368 for (i = 0; argv[i]; i++)
370 arg_list = xtrycalloc (i + 2, sizeof *arg_list);
375 arg_list[i] = xtrystrdup ("<ldap-wrapper-thread>");
379 for (j=0; argv[j]; j++)
381 arg_list[i] = xtrystrdup (argv[j]);
387 *r_arg_list = arg_list;
391 err = gpg_error_from_syserror ();
392 log_error (_("error allocating memory: %s\n"), strerror (errno));
393 free_arg_list (arg_list);
400 /* Parameters passed to the wrapper thread. */
401 struct ldap_wrapper_thread_parms
407 /* The thread which runs the LDAP wrapper. */
409 ldap_wrapper_thread (void *opaque)
411 struct ldap_wrapper_thread_parms *parms = opaque;
413 /*err =*/ ldap_wrapper_main (parms->arg_list, parms->outstream);
415 /* FIXME: Do we need to return ERR? */
417 free_arg_list (parms->arg_list);
418 es_fclose (parms->outstream);
425 /* Start a new LDAP thread and returns a new libksba reader
426 object at READER. ARGV is a NULL terminated list of arguments for
427 the wrapper. The function returns 0 on success or an error code. */
429 ldap_wrapper (ctrl_t ctrl, ksba_reader_t *r_reader, const char *argv[])
432 struct ldap_wrapper_thread_parms *parms;
434 es_cookie_io_functions_t outstream_func = { NULL };
435 struct outstream_cookie_s *outstream_cookie;
436 ksba_reader_t reader;
444 parms = xtrycalloc (1, sizeof *parms);
446 return gpg_error_from_syserror ();
448 err = create_arg_list (argv, &parms->arg_list);
455 outstream_cookie = xtrycalloc (1, sizeof *outstream_cookie);
456 if (!outstream_cookie)
458 err = gpg_error_from_syserror ();
459 free_arg_list (parms->arg_list);
463 outstream_cookie->refcount++;
465 res = npth_cond_init (&outstream_cookie->wait_data, NULL);
468 free_arg_list (parms->arg_list);
470 return gpg_error_from_errno (res);
472 res = npth_cond_init (&outstream_cookie->wait_space, NULL);
475 npth_cond_destroy (&outstream_cookie->wait_data);
476 free_arg_list (parms->arg_list);
478 return gpg_error_from_errno (res);
481 err = ksba_reader_new (&reader);
483 err = ksba_reader_set_release_notify (reader,
484 outstream_reader_released,
487 err = ksba_reader_set_cb (reader,
488 outstream_reader_cb, outstream_cookie);
491 log_error (_("error initializing reader object: %s\n"),
493 ksba_reader_release (reader);
494 outstream_release_cookie (outstream_cookie);
495 free_arg_list (parms->arg_list);
501 outstream_func.func_write = outstream_cookie_writer;
502 outstream_func.func_close = outstream_cookie_closer;
503 parms->outstream = es_fopencookie (outstream_cookie, "wb", outstream_func);
504 if (!parms->outstream)
506 err = gpg_error_from_syserror ();
507 ksba_reader_release (reader);
508 outstream_release_cookie (outstream_cookie);
509 free_arg_list (parms->arg_list);
513 outstream_cookie->refcount++;
515 res = npth_attr_init(&tattr);
518 err = gpg_error_from_errno (res);
519 ksba_reader_release (reader);
520 free_arg_list (parms->arg_list);
521 es_fclose (parms->outstream);
525 npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
527 res = npth_create (&thread, &tattr, ldap_wrapper_thread, parms);
528 npth_attr_destroy (&tattr);
531 err = gpg_error_from_errno (res);
532 log_error ("error spawning ldap wrapper thread: %s\n",
536 parms = NULL; /* Now owned by the thread. */
540 free_arg_list (parms->arg_list);
541 es_fclose (parms->outstream);
546 ksba_reader_release (reader);
550 /* Need to wait for the first byte so we are able to detect an empty
551 output and not let the consumer see an EOF without further error
552 indications. The CRL loading logic assumes that after return
553 from this function, a failed search (e.g. host not found ) is
554 indicated right away. */
558 err = read_buffer (reader, &c, 1);
561 ksba_reader_release (reader);
563 if (gpg_err_code (err) == GPG_ERR_EOF)
564 return gpg_error (GPG_ERR_NO_DATA);
568 ksba_reader_unread (reader, &c, 1);