w32: Include winsock2.h to silence warnings.
[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 <http://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
49
50
51 /* Read a fixed amount of data from READER into BUFFER.  */
52 static gpg_error_t
53 read_buffer (ksba_reader_t reader, unsigned char *buffer, size_t count)
54 {
55   gpg_error_t err;
56   size_t nread;
57
58   while (count)
59     {
60       err = ksba_reader_read (reader, buffer, count, &nread);
61       if (err)
62         return err;
63       buffer += nread;
64       count -= nread;
65     }
66   return 0;
67 }
68
69
70
71
72 /* Start the reaper thread for this wrapper.  */
73 void
74 ldap_wrapper_launch_thread (void)
75 {
76   /* Not required.  */
77 }
78
79
80
81
82
83 /* Wait until all ldap wrappers have terminated.  We assume that the
84    kill has already been sent to all of them.  */
85 void
86 ldap_wrapper_wait_connections ()
87 {
88   /* Not required.  */
89 }
90
91
92 /* Cleanup all resources held by the connection associated with
93    CTRL.  This is used after a cancel to kill running wrappers.  */
94 void
95 ldap_wrapper_connection_cleanup (ctrl_t ctrl)
96 {
97   (void)ctrl;
98
99   /* Not required.  */
100 }
101
102
103 \f
104 /* The cookie we use to implement the outstream of the wrapper thread.  */
105 struct outstream_cookie_s
106 {
107   int refcount; /* Reference counter - possible values are 1 and 2.  */
108
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
112      waiting on it.  */
113   npth_cond_t wait_data; /* Condition that data is available.  */
114   npth_cond_t wait_space; /* Condition that space is available.  */
115
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 };
121
122 #define BUFFER_EMPTY(c) ((c)->buffer_len == 0)
123 #define BUFFER_FULL(c) ((c)->buffer_len == DIM((c)->buffer))
124 #define BUFFER_DATA_AVAILABLE(c) ((c)->buffer_len)
125 #define BUFFER_SPACE_AVAILABLE(c) (DIM((c)->buffer) - (c)->buffer_len)
126 #define BUFFER_INC_POS(c,n) (c)->buffer_pos = ((c)->buffer_pos + (n)) % DIM((c)->buffer)
127 #define BUFFER_CUR_POS(c) (&(c)->buffer[(c)->buffer_pos])
128
129 static int
130 buffer_get_data (struct outstream_cookie_s *cookie, char *dst, int cnt)
131 {
132   int amount;
133   int left;
134   int chunk;
135
136   amount = cnt;
137   if (BUFFER_DATA_AVAILABLE (cookie) < amount)
138     amount = BUFFER_DATA_AVAILABLE (cookie);
139   left = amount;
140
141   /* How large is the part up to the end of the buffer array?  */
142   chunk = DIM(cookie->buffer) - cookie->buffer_pos;
143   if (chunk > left)
144     chunk = left;
145
146   memcpy (dst, BUFFER_CUR_POS (cookie), chunk);
147   BUFFER_INC_POS (cookie, chunk);
148   left -= chunk;
149   dst += chunk;
150
151   if (left)
152     {
153       memcpy (dst, BUFFER_CUR_POS (cookie), left);
154       BUFFER_INC_POS (cookie, left);
155     }
156
157   return amount;
158 }
159
160
161 static int
162 buffer_put_data (struct outstream_cookie_s *cookie, const char *src, int cnt)
163 {
164   int amount;
165   int remain;
166   int left;
167   int chunk;
168
169   remain = DIM(cookie->buffer) - cookie->buffer_len;
170
171   amount = cnt;
172   if (remain < amount)
173     amount = remain;
174   left = amount;
175
176   /* How large is the part up to the end of the buffer array?  */
177   chunk = DIM(cookie->buffer) - cookie->buffer_pos;
178   if (chunk > left)
179     chunk = left;
180
181   memcpy (BUFFER_CUR_POS (cookie), src, chunk);
182   BUFFER_INC_POS (cookie, chunk);
183   left -= chunk;
184   src += chunk;
185
186   if (left)
187     {
188       memcpy (BUFFER_CUR_POS (cookie), src, left);
189       BUFFER_INC_POS (cookie, left);
190     }
191
192   cookie->buffer_len -= amount;
193   return amount;
194 }
195
196
197 /* The writer function for the outstream.  This is used to transfer
198    the output of the ldap wrapper thread to the ksba reader object.  */
199 static ssize_t
200 outstream_cookie_writer (void *cookie_arg, const void *buffer, size_t size)
201 {
202   struct outstream_cookie_s *cookie = cookie_arg;
203   const char *src;
204   ssize_t nwritten = 0;
205   int res;
206   ssize_t amount = 0;
207
208   src = buffer;
209   do
210     {
211       int was_empty = 0;
212
213       /* Wait for free space.  */
214       while (BUFFER_FULL(cookie))
215         {
216           /* Buffer is full:  Wait for space.  */
217           res = npth_cond_wait (&cookie->wait_space, NULL);
218           if (res)
219             {
220               gpg_err_set_errno (res);
221               return -1;
222             }
223         }
224
225       if (BUFFER_EMPTY(cookie))
226         was_empty = 1;
227
228       /* Copy data.  */
229       nwritten = buffer_put_data (cookie, buffer, size);
230       size -= nwritten;
231       src += nwritten;
232       amount += nwritten;
233
234       if (was_empty)
235         npth_cond_signal (&cookie->wait_data);
236     }
237   while (size);  /* Until done.  */
238
239   return amount;
240 }
241
242
243 static void
244 outstream_release_cookie (struct outstream_cookie_s *cookie)
245 {
246   cookie->refcount--;
247   if (!cookie->refcount)
248     {
249       npth_cond_destroy (&cookie->wait_data);
250       npth_cond_destroy (&cookie->wait_space);
251       xfree (cookie);
252     }
253 }
254
255
256 /* Closer function for the outstream.  This deallocates the cookie if
257    it won't be used anymore.  */
258 static int
259 outstream_cookie_closer (void *cookie_arg)
260 {
261   struct outstream_cookie_s *cookie = cookie_arg;
262
263   if (!cookie)
264     return 0;  /* Nothing to do.  */
265
266   cookie->eof_seen = 1; /* (only useful if refcount > 1)  */
267
268   assert (cookie->refcount > 0);
269   outstream_release_cookie (cookie);
270   return 0;
271 }
272
273
274 /* The KSBA reader callback which takes the output of the ldap thread
275    form the outstream_cookie_writer and make it available to the ksba
276    reader.  */
277 static int
278 outstream_reader_cb (void *cb_value, char *buffer, size_t count,
279                      size_t *r_nread)
280 {
281   struct outstream_cookie_s *cookie = cb_value;
282   char *dst;
283   const char *src;
284   size_t nread = 0;
285   int was_full = 0;
286
287   if (!buffer && !count && !r_nread)
288     return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Rewind is not supported.  */
289
290   *r_nread = 0;
291   dst = buffer;
292
293   while (BUFFER_EMPTY(cookie))
294     {
295       if (cookie->eof_seen)
296         return gpg_error (GPG_ERR_EOF);
297
298       /* Wait for data to become available.  */
299       npth_cond_wait (&cookie->wait_data, NULL);
300     }
301
302   if (BUFFER_FULL(cookie))
303     was_full = 1;
304
305   src = cookie->buffer + cookie->buffer_pos;
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 }