headers: fix spelling
[gnupg.git] / dirmngr / ldap-wrapper-ce.c
1 /* ldap-wrapper-ce.c - LDAP access via W32 threads
2  * Copyright (C) 2010 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
20 /*
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.
25
26    See ldap-wrapper.c for  the standard wrapper interface.
27  */
28
29 #include <config.h>
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <unistd.h>
36 #include <fcntl.h>
37 #include <time.h>
38 #include <npth.h>
39 #include <assert.h>
40
41 #include "dirmngr.h"
42 #include "misc.h"
43 #include "ldap-wrapper.h"
44
45 #ifdef USE_LDAPWRAPPER
46 # error This module is not expected to be build.
47 #endif
48 #error This module might not anymore work.
49
50
51
52 /* Read a fixed amount of data from READER into BUFFER.  */
53 static gpg_error_t
54 read_buffer (ksba_reader_t reader, unsigned char *buffer, size_t count)
55 {
56   gpg_error_t err;
57   size_t nread;
58
59   while (count)
60     {
61       err = ksba_reader_read (reader, buffer, count, &nread);
62       if (err)
63         return err;
64       buffer += nread;
65       count -= nread;
66     }
67   return 0;
68 }
69
70
71
72
73 /* Start the reaper thread for this wrapper.  */
74 void
75 ldap_wrapper_launch_thread (void)
76 {
77   /* Not required.  */
78 }
79
80
81
82
83
84 /* Wait until all ldap wrappers have terminated.  We assume that the
85    kill has already been sent to all of them.  */
86 void
87 ldap_wrapper_wait_connections ()
88 {
89   /* Not required.  */
90 }
91
92
93 /* Cleanup all resources held by the connection associated with
94    CTRL.  This is used after a cancel to kill running wrappers.  */
95 void
96 ldap_wrapper_connection_cleanup (ctrl_t ctrl)
97 {
98   (void)ctrl;
99
100   /* Not required.  */
101 }
102
103
104 \f
105 /* The cookie we use to implement the outstream of the wrapper thread.  */
106 struct outstream_cookie_s
107 {
108   int refcount; /* Reference counter - possible values are 1 and 2.  */
109
110   /* We don't need a mutex for the conditions, as npth provides a
111      simpler condition interface that relies on the global lock.  This
112      can be used if we never yield between testing the condition and
113      waiting on it.  */
114   npth_cond_t wait_data; /* Condition that data is available.  */
115   npth_cond_t wait_space; /* Condition that space is available.  */
116
117   int eof_seen;       /* EOF indicator.  */
118   char buffer[4000];  /* Data ring buffer.  */
119   size_t buffer_len;  /* The amount of data in the BUFFER.  */
120   size_t buffer_pos;  /* The next read position of the BUFFER.  */
121   size_t buffer_read_pos;  /* The next read position of the BUFFER.  */
122 };
123
124 #define BUFFER_EMPTY(c) ((c)->buffer_len == 0)
125 #define BUFFER_FULL(c) ((c)->buffer_len == DIM((c)->buffer))
126 #define BUFFER_DATA_AVAILABLE(c) ((c)->buffer_len)
127 #define BUFFER_SPACE_AVAILABLE(c) (DIM((c)->buffer) - (c)->buffer_len)
128 #define BUFFER_INC_POS(c,n) (c)->buffer_pos = ((c)->buffer_pos + (n)) % DIM((c)->buffer)
129 #define BUFFER_CUR_POS(c) (&(c)->buffer[(c)->buffer_pos])
130 #define BUFFER_INC_READ_POS(c,n) (c)->buffer_read_pos = ((c)->buffer_read_pos + (n)) % DIM((c)->buffer)
131 #define BUFFER_CUR_READ_POS(c) (&(c)->buffer[(c)->buffer_read_pos])
132
133 static int
134 buffer_get_data (struct outstream_cookie_s *cookie, char *dst, int cnt)
135 {
136   int amount;
137   int left;
138   int chunk;
139
140   amount = cnt;
141   if (BUFFER_DATA_AVAILABLE (cookie) < amount)
142     amount = BUFFER_DATA_AVAILABLE (cookie);
143   left = amount;
144
145   /* How large is the part up to the end of the buffer array?  */
146   chunk = DIM(cookie->buffer) - cookie->buffer_pos;
147   if (chunk > left)
148     chunk = left;
149
150   memcpy (dst, BUFFER_CUR_READ_POS (cookie), chunk);
151   BUFFER_INC_READ_POS (cookie, chunk);
152   left -= chunk;
153   dst += chunk;
154
155   if (left)
156     {
157       memcpy (dst, BUFFER_CUR_READ_POS (cookie), left);
158       BUFFER_INC_READ_POS (cookie, left);
159     }
160
161   return amount;
162 }
163
164
165 static int
166 buffer_put_data (struct outstream_cookie_s *cookie, const char *src, int cnt)
167 {
168   int amount;
169   int remain;
170   int left;
171   int chunk;
172
173   remain = DIM(cookie->buffer) - cookie->buffer_len;
174
175   amount = cnt;
176   if (remain < amount)
177     amount = remain;
178   left = amount;
179
180   /* How large is the part up to the end of the buffer array?  */
181   chunk = DIM(cookie->buffer) - cookie->buffer_pos;
182   if (chunk > left)
183     chunk = left;
184
185   memcpy (BUFFER_CUR_POS (cookie), src, chunk);
186   BUFFER_INC_POS (cookie, chunk);
187   left -= chunk;
188   src += chunk;
189
190   if (left)
191     {
192       memcpy (BUFFER_CUR_POS (cookie), src, left);
193       BUFFER_INC_POS (cookie, left);
194     }
195
196   cookie->buffer_len -= amount;
197   return amount;
198 }
199
200
201 /* The writer function for the outstream.  This is used to transfer
202    the output of the ldap wrapper thread to the ksba reader object.  */
203 static gpgrt_ssize_t
204 outstream_cookie_writer (void *cookie_arg, const void *buffer, size_t size)
205 {
206   struct outstream_cookie_s *cookie = cookie_arg;
207   const char *src;
208   ssize_t nwritten = 0;
209   int res;
210   ssize_t amount = 0;
211
212   src = buffer;
213   do
214     {
215       int was_empty = 0;
216
217       /* Wait for free space.  */
218       while (BUFFER_FULL(cookie))
219         {
220           /* Buffer is full:  Wait for space.  */
221           res = npth_cond_wait (&cookie->wait_space, NULL);
222           if (res)
223             {
224               gpg_err_set_errno (res);
225               return -1;
226             }
227         }
228
229       if (BUFFER_EMPTY(cookie))
230         was_empty = 1;
231
232       /* Copy data.  */
233       nwritten = buffer_put_data (cookie, buffer, size);
234       size -= nwritten;
235       src += nwritten;
236       amount += nwritten;
237
238       if (was_empty)
239         npth_cond_signal (&cookie->wait_data);
240     }
241   while (size);  /* Until done.  */
242
243   return amount;
244 }
245
246
247 static void
248 outstream_release_cookie (struct outstream_cookie_s *cookie)
249 {
250   cookie->refcount--;
251   if (!cookie->refcount)
252     {
253       npth_cond_destroy (&cookie->wait_data);
254       npth_cond_destroy (&cookie->wait_space);
255       xfree (cookie);
256     }
257 }
258
259
260 /* Closer function for the outstream.  This deallocates the cookie if
261    it won't be used anymore.  */
262 static int
263 outstream_cookie_closer (void *cookie_arg)
264 {
265   struct outstream_cookie_s *cookie = cookie_arg;
266
267   if (!cookie)
268     return 0;  /* Nothing to do.  */
269
270   cookie->eof_seen = 1; /* (only useful if refcount > 1)  */
271
272   assert (cookie->refcount > 0);
273   outstream_release_cookie (cookie);
274   return 0;
275 }
276
277
278 /* The KSBA reader callback which takes the output of the ldap thread
279    form the outstream_cookie_writer and make it available to the ksba
280    reader.  */
281 static int
282 outstream_reader_cb (void *cb_value, char *buffer, size_t count,
283                      size_t *r_nread)
284 {
285   struct outstream_cookie_s *cookie = cb_value;
286   size_t nread = 0;
287   int was_full = 0;
288
289   if (!buffer && !count && !r_nread)
290     return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Rewind is not supported.  */
291
292   *r_nread = 0;
293
294   while (BUFFER_EMPTY(cookie))
295     {
296       if (cookie->eof_seen)
297         return gpg_error (GPG_ERR_EOF);
298
299       /* Wait for data to become available.  */
300       npth_cond_wait (&cookie->wait_data, NULL);
301     }
302
303   if (BUFFER_FULL(cookie))
304     was_full = 1;
305
306   nread = buffer_get_data (cookie, buffer, count);
307
308   if (was_full)
309     {
310       npth_cond_signal (&cookie->wait_space);
311     }
312
313   *r_nread = nread;
314   return 0; /* Success.  */
315 }
316
317
318 /* This function is called by ksba_reader_release.  */
319 static void
320 outstream_reader_released (void *cb_value, ksba_reader_t r)
321 {
322   struct outstream_cookie_s *cookie = cb_value;
323
324   (void)r;
325
326   assert (cookie->refcount > 0);
327   outstream_release_cookie (cookie);
328 }
329
330
331
332 /* This function is to be used to release a context associated with the
333    given reader object.  This does not release the reader object, though. */
334 void
335 ldap_wrapper_release_context (ksba_reader_t reader)
336 {
337   (void)reader;
338   /* Nothing to do.  */
339 }
340
341
342 \f
343 /* Free a NULL terminated array of malloced strings and the array
344    itself.  */
345 static void
346 free_arg_list (char **arg_list)
347 {
348   int i;
349
350   if (arg_list)
351     {
352       for (i=0; arg_list[i]; i++)
353         xfree (arg_list[i]);
354       xfree (arg_list);
355     }
356 }
357
358
359 /* Copy ARGV into a new array and prepend one element as name of the
360    program (which is more or less a stub).  We need to allocate all
361    the strings to get ownership of them.  */
362 static gpg_error_t
363 create_arg_list (const char *argv[], char ***r_arg_list)
364 {
365   gpg_error_t err;
366   char **arg_list;
367   int i, j;
368
369   for (i = 0; argv[i]; i++)
370     ;
371   arg_list = xtrycalloc (i + 2, sizeof *arg_list);
372   if (!arg_list)
373     goto outofcore;
374
375   i = 0;
376   arg_list[i] = xtrystrdup ("<ldap-wrapper-thread>");
377   if (!arg_list[i])
378     goto outofcore;
379   i++;
380   for (j=0; argv[j]; j++)
381     {
382       arg_list[i] = xtrystrdup (argv[j]);
383       if (!arg_list[i])
384         goto outofcore;
385       i++;
386     }
387   arg_list[i] = NULL;
388   *r_arg_list = arg_list;
389   return 0;
390
391  outofcore:
392   err = gpg_error_from_syserror ();
393   log_error (_("error allocating memory: %s\n"), strerror (errno));
394   free_arg_list (arg_list);
395   *r_arg_list = NULL;
396   return err;
397
398 }
399
400
401 /* Parameters passed to the wrapper thread. */
402 struct ldap_wrapper_thread_parms
403 {
404   char **arg_list;
405   estream_t outstream;
406 };
407
408 /* The thread which runs the LDAP wrapper.  */
409 static void *
410 ldap_wrapper_thread (void *opaque)
411 {
412   struct ldap_wrapper_thread_parms *parms = opaque;
413
414   /*err =*/ ldap_wrapper_main (parms->arg_list, parms->outstream);
415
416   /* FIXME: Do we need to return ERR?  */
417
418   free_arg_list (parms->arg_list);
419   es_fclose (parms->outstream);
420   xfree (parms);
421   return NULL;
422 }
423
424
425
426 /* Start a new LDAP thread and returns a new libksba reader
427    object at READER.  ARGV is a NULL terminated list of arguments for
428    the wrapper.  The function returns 0 on success or an error code.  */
429 gpg_error_t
430 ldap_wrapper (ctrl_t ctrl, ksba_reader_t *r_reader, const char *argv[])
431 {
432   gpg_error_t err;
433   struct ldap_wrapper_thread_parms *parms;
434   npth_attr_t tattr;
435   es_cookie_io_functions_t outstream_func = { NULL };
436   struct outstream_cookie_s *outstream_cookie;
437   ksba_reader_t reader;
438   int res;
439   npth_t thread;
440
441   (void)ctrl;
442
443   *r_reader = NULL;
444
445   parms = xtrycalloc (1, sizeof *parms);
446   if (!parms)
447     return gpg_error_from_syserror ();
448
449   err = create_arg_list (argv, &parms->arg_list);
450   if (err)
451     {
452       xfree (parms);
453       return err;
454     }
455
456   outstream_cookie = xtrycalloc (1, sizeof *outstream_cookie);
457   if (!outstream_cookie)
458     {
459       err = gpg_error_from_syserror ();
460       free_arg_list (parms->arg_list);
461       xfree (parms);
462       return err;
463     }
464   outstream_cookie->refcount++;
465
466   res = npth_cond_init (&outstream_cookie->wait_data, NULL);
467   if (res)
468     {
469       free_arg_list (parms->arg_list);
470       xfree (parms);
471       return gpg_error_from_errno (res);
472     }
473   res = npth_cond_init (&outstream_cookie->wait_space, NULL);
474   if (res)
475     {
476       npth_cond_destroy (&outstream_cookie->wait_data);
477       free_arg_list (parms->arg_list);
478       xfree (parms);
479       return gpg_error_from_errno (res);
480     }
481
482   err = ksba_reader_new (&reader);
483   if (!err)
484     err = ksba_reader_set_release_notify (reader,
485                                           outstream_reader_released,
486                                           outstream_cookie);
487   if (!err)
488     err = ksba_reader_set_cb (reader,
489                               outstream_reader_cb, outstream_cookie);
490   if (err)
491     {
492       log_error (_("error initializing reader object: %s\n"),
493                  gpg_strerror (err));
494       ksba_reader_release (reader);
495       outstream_release_cookie (outstream_cookie);
496       free_arg_list (parms->arg_list);
497       xfree (parms);
498       return err;
499     }
500
501
502   outstream_func.func_write = outstream_cookie_writer;
503   outstream_func.func_close = outstream_cookie_closer;
504   parms->outstream = es_fopencookie (outstream_cookie, "wb", outstream_func);
505   if (!parms->outstream)
506     {
507       err = gpg_error_from_syserror ();
508       ksba_reader_release (reader);
509       outstream_release_cookie (outstream_cookie);
510       free_arg_list (parms->arg_list);
511       xfree (parms);
512       return err;
513     }
514   outstream_cookie->refcount++;
515
516   res = npth_attr_init(&tattr);
517   if (res)
518     {
519       err = gpg_error_from_errno (res);
520       ksba_reader_release (reader);
521       free_arg_list (parms->arg_list);
522       es_fclose (parms->outstream);
523       xfree (parms);
524       return err;
525     }
526   npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
527
528   res = npth_create (&thread, &tattr, ldap_wrapper_thread, parms);
529   npth_attr_destroy (&tattr);
530   if (res)
531     {
532       err = gpg_error_from_errno (res);
533       log_error ("error spawning ldap wrapper thread: %s\n",
534                  strerror (res) );
535     }
536   else
537     parms = NULL; /* Now owned by the thread.  */
538
539   if (parms)
540     {
541       free_arg_list (parms->arg_list);
542       es_fclose (parms->outstream);
543       xfree (parms);
544     }
545   if (err)
546     {
547       ksba_reader_release (reader);
548       return err;
549     }
550
551   /* Need to wait for the first byte so we are able to detect an empty
552      output and not let the consumer see an EOF without further error
553      indications.  The CRL loading logic assumes that after return
554      from this function, a failed search (e.g. host not found ) is
555      indicated right away. */
556   {
557     unsigned char c;
558
559     err = read_buffer (reader, &c, 1);
560     if (err)
561       {
562         ksba_reader_release (reader);
563         reader = NULL;
564         if (gpg_err_code (err) == GPG_ERR_EOF)
565           return gpg_error (GPG_ERR_NO_DATA);
566         else
567           return err;
568       }
569     ksba_reader_unread (reader, &c, 1);
570   }
571
572   *r_reader = reader;
573
574   return 0;
575 }