836ced0c1394685acbc8ee73be27b438b4d291f4
[gnupg.git] / dirmngr / dirmngr_ldap.c
1 /* dirmngr-ldap.c  -  The LDAP helper for dirmngr.
2  * Copyright (C) 2004 g10 Code GmbH
3  * Copyright (C) 2010 Free Software Foundation, Inc.
4  *
5  * This file is part of GnuPG.
6  *
7  * GnuPG is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * GnuPG is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <https://www.gnu.org/licenses/>.
19  */
20
21 #include <config.h>
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <stddef.h>
26 #include <stdarg.h>
27 #include <string.h>
28 #ifdef HAVE_SIGNAL_H
29 # include <signal.h>
30 #endif
31 #include <errno.h>
32 #include <assert.h>
33 #include <sys/time.h>
34 #include <unistd.h>
35 #ifndef USE_LDAPWRAPPER
36 # include <npth.h>
37 #endif
38
39 #ifdef HAVE_W32_SYSTEM
40 # include <winsock2.h>
41 # include <winldap.h>
42 # include <winber.h>
43 # include <fcntl.h>
44 # include "ldap-url.h"
45 #else
46   /* For OpenLDAP, to enable the API that we're using. */
47 # define LDAP_DEPRECATED 1
48 # include <ldap.h>
49 #endif
50
51
52 #include <gpg-error.h>
53 #include "../common/logging.h"
54 #include "../common/argparse.h"
55 #include "../common/stringhelp.h"
56 #include "../common/mischelp.h"
57 #include "../common/strlist.h"
58
59 #include "../common/i18n.h"
60 #include "../common/util.h"
61 #include "../common/init.h"
62
63 /* With the ldap wrapper, there is no need for the npth_unprotect and leave
64    functions; thus we redefine them to nops.  If we are not using the
65    ldap wrapper process we need to include the prototype for our
66    module's main function.  */
67 #ifdef USE_LDAPWRAPPER
68 static void npth_unprotect (void) { }
69 static void npth_protect (void) { }
70 #else
71 # include "./ldap-wrapper.h"
72 #endif
73
74 #ifdef HAVE_W32CE_SYSTEM
75 # include "w32-ldap-help.h"
76 # define my_ldap_init(a,b)                      \
77   _dirmngr_ldap_init ((a), (b))
78 # define my_ldap_simple_bind_s(a,b,c)           \
79   _dirmngr_ldap_simple_bind_s ((a),(b),(c))
80 # define my_ldap_search_st(a,b,c,d,e,f,g,h)     \
81   _dirmngr_ldap_search_st ((a), (b), (c), (d), (e), (f), (g), (h))
82 # define my_ldap_first_attribute(a,b,c)         \
83   _dirmngr_ldap_first_attribute ((a),(b),(c))
84 # define my_ldap_next_attribute(a,b,c)          \
85   _dirmngr_ldap_next_attribute ((a),(b),(c))
86 # define my_ldap_get_values_len(a,b,c)          \
87   _dirmngr_ldap_get_values_len ((a),(b),(c))
88 # define my_ldap_free_attr(a)                   \
89   xfree ((a))
90 #else
91 # define my_ldap_init(a,b)              ldap_init ((a), (b))
92 # define my_ldap_simple_bind_s(a,b,c)   ldap_simple_bind_s ((a), (b), (c))
93 # define my_ldap_search_st(a,b,c,d,e,f,g,h)     \
94   ldap_search_st ((a), (b), (c), (d), (e), (f), (g), (h))
95 # define my_ldap_first_attribute(a,b,c) ldap_first_attribute ((a),(b),(c))
96 # define my_ldap_next_attribute(a,b,c)  ldap_next_attribute ((a),(b),(c))
97 # define my_ldap_get_values_len(a,b,c)  ldap_get_values_len ((a),(b),(c))
98 # define my_ldap_free_attr(a)           ldap_memfree ((a))
99 #endif
100
101 #ifdef HAVE_W32_SYSTEM
102  typedef LDAP_TIMEVAL  my_ldap_timeval_t;
103 #else
104  typedef struct timeval my_ldap_timeval_t;
105 #endif
106
107 #define DEFAULT_LDAP_TIMEOUT 100 /* Arbitrary long timeout. */
108
109
110 /* Constants for the options.  */
111 enum
112   {
113     oQuiet        = 'q',
114     oVerbose      = 'v',
115
116     oTimeout      = 500,
117     oMulti,
118     oProxy,
119     oHost,
120     oPort,
121     oUser,
122     oPass,
123     oEnvPass,
124     oDN,
125     oFilter,
126     oAttr,
127
128     oOnlySearchTimeout,
129     oLogWithPID
130   };
131
132
133 /* The list of options as used by the argparse.c code.  */
134 static ARGPARSE_OPTS opts[] = {
135   { oVerbose,  "verbose",   0, N_("verbose") },
136   { oQuiet,    "quiet",     0, N_("be somewhat more quiet") },
137   { oTimeout,  "timeout",   1, N_("|N|set LDAP timeout to N seconds")},
138   { oMulti,    "multi",     0, N_("return all values in"
139                                   " a record oriented format")},
140   { oProxy,    "proxy",     2,
141     N_("|NAME|ignore host part and connect through NAME")},
142   { oHost,     "host",      2, N_("|NAME|connect to host NAME")},
143   { oPort,     "port",      1, N_("|N|connect to port N")},
144   { oUser,     "user",      2, N_("|NAME|use user NAME for authentication")},
145   { oPass,     "pass",      2, N_("|PASS|use password PASS"
146                                   " for authentication")},
147   { oEnvPass,  "env-pass",  0, N_("take password from $DIRMNGR_LDAP_PASS")},
148   { oDN,       "dn",        2, N_("|STRING|query DN STRING")},
149   { oFilter,   "filter",    2, N_("|STRING|use STRING as filter expression")},
150   { oAttr,     "attr",      2, N_("|STRING|return the attribute STRING")},
151   { oOnlySearchTimeout, "only-search-timeout", 0, "@"},
152   { oLogWithPID,"log-with-pid", 0, "@"},
153   { 0, NULL, 0, NULL }
154 };
155
156
157 /* A structure with module options.  This is not a static variable
158    because if we are not build as a standalone binary, each thread
159    using this module needs to handle its own values.  */
160 struct my_opt_s
161 {
162   int quiet;
163   int verbose;
164   my_ldap_timeval_t timeout;/* Timeout for the LDAP search functions.  */
165   unsigned int alarm_timeout; /* And for the alarm based timeout.  */
166   int multi;
167
168   estream_t outstream;    /* Send output to this stream.  */
169
170   /* Note that we can't use const for the strings because ldap_* are
171      not defined that way.  */
172   char *proxy; /* Host and Port override.  */
173   char *user;  /* Authentication user.  */
174   char *pass;  /* Authentication password.  */
175   char *host;  /* Override host.  */
176   int port;    /* Override port.  */
177   char *dn;    /* Override DN.  */
178   char *filter;/* Override filter.  */
179   char *attr;  /* Override attribute.  */
180 };
181 typedef struct my_opt_s *my_opt_t;
182
183
184 /* Prototypes.  */
185 #ifndef HAVE_W32_SYSTEM
186 static void catch_alarm (int dummy);
187 #endif
188 static int process_url (my_opt_t myopt, const char *url);
189
190
191
192 /* Function called by argparse.c to display information.  */
193 #ifdef USE_LDAPWRAPPER
194 static const char *
195 my_strusage (int level)
196 {
197   const char *p;
198
199   switch(level)
200     {
201     case 11: p = "dirmngr_ldap (@GNUPG@)";
202       break;
203     case 13: p = VERSION; break;
204     case 17: p = PRINTABLE_OS_NAME; break;
205     case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
206     case 49: p = PACKAGE_BUGREPORT; break;
207     case 1:
208     case 40: p =
209                _("Usage: dirmngr_ldap [options] [URL] (-h for help)\n");
210       break;
211     case 41: p =
212           _("Syntax: dirmngr_ldap [options] [URL]\n"
213             "Internal LDAP helper for Dirmngr\n"
214             "Interface and options may change without notice\n");
215       break;
216
217     default: p = NULL;
218     }
219   return p;
220 }
221 #endif /*!USE_LDAPWRAPPER*/
222
223
224 int
225 #ifdef USE_LDAPWRAPPER
226 main (int argc, char **argv)
227 #else
228 ldap_wrapper_main (char **argv, estream_t outstream)
229 #endif
230 {
231 #ifndef USE_LDAPWRAPPER
232   int argc;
233 #endif
234   ARGPARSE_ARGS pargs;
235   int any_err = 0;
236   char *p;
237   int only_search_timeout = 0;
238   struct my_opt_s my_opt_buffer;
239   my_opt_t myopt = &my_opt_buffer;
240   char *malloced_buffer1 = NULL;
241
242   memset (&my_opt_buffer, 0, sizeof my_opt_buffer);
243
244   early_system_init ();
245
246 #ifdef USE_LDAPWRAPPER
247   set_strusage (my_strusage);
248   log_set_prefix ("dirmngr_ldap", GPGRT_LOG_WITH_PREFIX);
249
250   /* Setup I18N and common subsystems. */
251   i18n_init();
252
253   init_common_subsystems (&argc, &argv);
254
255   es_set_binary (es_stdout);
256   myopt->outstream = es_stdout;
257 #else /*!USE_LDAPWRAPPER*/
258   myopt->outstream = outstream;
259   for (argc=0; argv[argc]; argc++)
260     ;
261 #endif /*!USE_LDAPWRAPPER*/
262
263   /* LDAP defaults */
264   myopt->timeout.tv_sec = DEFAULT_LDAP_TIMEOUT;
265   myopt->timeout.tv_usec = 0;
266   myopt->alarm_timeout = 0;
267
268   /* Parse the command line.  */
269   pargs.argc = &argc;
270   pargs.argv = &argv;
271   pargs.flags= 1;  /* Do not remove the args. */
272   while (arg_parse (&pargs, opts) )
273     {
274       switch (pargs.r_opt)
275         {
276         case oVerbose: myopt->verbose++; break;
277         case oQuiet: myopt->quiet++; break;
278         case oTimeout:
279           myopt->timeout.tv_sec = pargs.r.ret_int;
280           myopt->timeout.tv_usec = 0;
281           myopt->alarm_timeout = pargs.r.ret_int;
282           break;
283         case oOnlySearchTimeout: only_search_timeout = 1; break;
284         case oMulti: myopt->multi = 1; break;
285         case oUser: myopt->user = pargs.r.ret_str; break;
286         case oPass: myopt->pass = pargs.r.ret_str; break;
287         case oEnvPass:
288           myopt->pass = getenv ("DIRMNGR_LDAP_PASS");
289           break;
290         case oProxy: myopt->proxy = pargs.r.ret_str; break;
291         case oHost: myopt->host = pargs.r.ret_str; break;
292         case oPort: myopt->port = pargs.r.ret_int; break;
293         case oDN:   myopt->dn = pargs.r.ret_str; break;
294         case oFilter: myopt->filter = pargs.r.ret_str; break;
295         case oAttr: myopt->attr = pargs.r.ret_str; break;
296         case oLogWithPID:
297           {
298             unsigned int oldflags;
299             log_get_prefix (&oldflags);
300             log_set_prefix (NULL, oldflags | GPGRT_LOG_WITH_PID);
301           }
302           break;
303
304         default :
305 #ifdef USE_LDAPWRAPPER
306           pargs.err = ARGPARSE_PRINT_ERROR;
307 #else
308           pargs.err = ARGPARSE_PRINT_WARNING;  /* No exit() please.  */
309 #endif
310           break;
311         }
312     }
313
314   if (only_search_timeout)
315     myopt->alarm_timeout = 0;
316
317   if (myopt->proxy)
318     {
319       malloced_buffer1 = xtrystrdup (myopt->proxy);
320       if (!malloced_buffer1)
321         {
322           log_error ("error copying string: %s\n", strerror (errno));
323           return 1;
324         }
325       myopt->host = malloced_buffer1;
326       p = strchr (myopt->host, ':');
327       if (p)
328         {
329           *p++ = 0;
330           myopt->port = atoi (p);
331         }
332       if (!myopt->port)
333         myopt->port = 389;  /* make sure ports gets overridden.  */
334     }
335
336   if (myopt->port < 0 || myopt->port > 65535)
337     log_error (_("invalid port number %d\n"), myopt->port);
338
339 #ifdef USE_LDAPWRAPPER
340   if (log_get_errorcount (0))
341     exit (2);
342   if (argc < 1)
343     usage (1);
344 #else
345   /* All passed arguments should be fine in this case.  */
346   assert (argc);
347 #endif
348
349 #ifdef USE_LDAPWRAPPER
350   if (myopt->alarm_timeout)
351     {
352 #ifndef HAVE_W32_SYSTEM
353 # if defined(HAVE_SIGACTION) && defined(HAVE_STRUCT_SIGACTION)
354       struct sigaction act;
355
356       act.sa_handler = catch_alarm;
357       sigemptyset (&act.sa_mask);
358       act.sa_flags = 0;
359       if (sigaction (SIGALRM,&act,NULL))
360 # else
361       if (signal (SIGALRM, catch_alarm) == SIG_ERR)
362 # endif
363           log_fatal ("unable to register timeout handler\n");
364 #endif
365     }
366 #endif /*USE_LDAPWRAPPER*/
367
368   for (; argc; argc--, argv++)
369     if (process_url (myopt, *argv))
370       any_err = 1;
371
372   xfree (malloced_buffer1);
373   return any_err;
374 }
375
376 #ifndef HAVE_W32_SYSTEM
377 static void
378 catch_alarm (int dummy)
379 {
380   (void)dummy;
381   _exit (10);
382 }
383 #endif
384
385 static void
386 set_timeout (my_opt_t myopt)
387 {
388 #ifdef HAVE_W32_SYSTEM
389   /* FIXME for W32.  */
390   (void)myopt;
391 #else
392   if (myopt->alarm_timeout)
393     alarm (myopt->alarm_timeout);
394 #endif
395 }
396
397
398 /* Helper for fetch_ldap().  */
399 static int
400 print_ldap_entries (my_opt_t myopt, LDAP *ld, LDAPMessage *msg, char *want_attr)
401 {
402   LDAPMessage *item;
403   int any = 0;
404
405   for (npth_unprotect (), item = ldap_first_entry (ld, msg), npth_protect ();
406        item;
407        npth_unprotect (), item = ldap_next_entry (ld, item), npth_protect ())
408     {
409       BerElement *berctx;
410       char *attr;
411
412       if (myopt->verbose > 1)
413         log_info (_("scanning result for attribute '%s'\n"),
414                   want_attr? want_attr : "[all]");
415
416       if (myopt->multi)
417         { /*  Write item marker. */
418           if (es_fwrite ("I\0\0\0\0", 5, 1, myopt->outstream) != 1)
419             {
420               log_error (_("error writing to stdout: %s\n"),
421                          strerror (errno));
422               return -1;
423             }
424         }
425
426
427       for (npth_unprotect (), attr = my_ldap_first_attribute (ld, item, &berctx),
428              npth_protect ();
429            attr;
430            npth_unprotect (), attr = my_ldap_next_attribute (ld, item, berctx),
431              npth_protect ())
432         {
433           struct berval **values;
434           int idx;
435
436           if (myopt->verbose > 1)
437             log_info (_("          available attribute '%s'\n"), attr);
438
439           set_timeout (myopt);
440
441           /* I case we want only one attribute we do a case
442              insensitive compare without the optional extension
443              (i.e. ";binary").  Case insensitive is not really correct
444              but the best we can do.  */
445           if (want_attr)
446             {
447               char *cp1, *cp2;
448               int cmpres;
449
450               cp1 = strchr (want_attr, ';');
451               if (cp1)
452                 *cp1 = 0;
453               cp2 = strchr (attr, ';');
454               if (cp2)
455                 *cp2 = 0;
456               cmpres = ascii_strcasecmp (want_attr, attr);
457               if (cp1)
458                 *cp1 = ';';
459               if (cp2)
460                 *cp2 = ';';
461               if (cmpres)
462                 {
463                   my_ldap_free_attr (attr);
464                   continue; /* Not found:  Try next attribute.  */
465                 }
466             }
467
468           npth_unprotect ();
469           values = my_ldap_get_values_len (ld, item, attr);
470           npth_protect ();
471
472           if (!values)
473             {
474               if (myopt->verbose)
475                 log_info (_("attribute '%s' not found\n"), attr);
476               my_ldap_free_attr (attr);
477               continue;
478             }
479
480           if (myopt->verbose)
481             {
482               log_info (_("found attribute '%s'\n"), attr);
483               if (myopt->verbose > 1)
484                 for (idx=0; values[idx]; idx++)
485                   log_info ("         length[%d]=%d\n",
486                             idx, (int)values[0]->bv_len);
487
488             }
489
490           if (myopt->multi)
491             { /*  Write attribute marker. */
492               unsigned char tmp[5];
493               size_t n = strlen (attr);
494
495               tmp[0] = 'A';
496               tmp[1] = (n >> 24);
497               tmp[2] = (n >> 16);
498               tmp[3] = (n >> 8);
499               tmp[4] = (n);
500               if (es_fwrite (tmp, 5, 1, myopt->outstream) != 1
501                   || es_fwrite (attr, n, 1, myopt->outstream) != 1)
502                 {
503                   log_error (_("error writing to stdout: %s\n"),
504                              strerror (errno));
505                   ldap_value_free_len (values);
506                   my_ldap_free_attr (attr);
507                   ber_free (berctx, 0);
508                   return -1;
509                 }
510             }
511
512           for (idx=0; values[idx]; idx++)
513             {
514               if (myopt->multi)
515                 { /* Write value marker.  */
516                   unsigned char tmp[5];
517                   size_t n = values[0]->bv_len;
518
519                   tmp[0] = 'V';
520                   tmp[1] = (n >> 24);
521                   tmp[2] = (n >> 16);
522                   tmp[3] = (n >> 8);
523                   tmp[4] = (n);
524
525                   if (es_fwrite (tmp, 5, 1, myopt->outstream) != 1)
526                     {
527                       log_error (_("error writing to stdout: %s\n"),
528                                  strerror (errno));
529                       ldap_value_free_len (values);
530                       my_ldap_free_attr (attr);
531                       ber_free (berctx, 0);
532                       return -1;
533                     }
534                 }
535
536               if (es_fwrite (values[0]->bv_val, values[0]->bv_len,
537                              1, myopt->outstream) != 1)
538                 {
539                   log_error (_("error writing to stdout: %s\n"),
540                              strerror (errno));
541                   ldap_value_free_len (values);
542                   my_ldap_free_attr (attr);
543                   ber_free (berctx, 0);
544                   return -1;
545                 }
546
547               any = 1;
548               if (!myopt->multi)
549                 break; /* Print only the first value.  */
550             }
551           ldap_value_free_len (values);
552           my_ldap_free_attr (attr);
553           if (want_attr || !myopt->multi)
554             break; /* We only want to return the first attribute.  */
555         }
556       ber_free (berctx, 0);
557     }
558
559   if (myopt->verbose > 1 && any)
560     log_info ("result has been printed\n");
561
562   return any?0:-1;
563 }
564
565
566
567 /* Helper for the URL based LDAP query. */
568 static int
569 fetch_ldap (my_opt_t myopt, const char *url, const LDAPURLDesc *ludp)
570 {
571   LDAP *ld;
572   LDAPMessage *msg;
573   int rc = 0;
574   char *host, *dn, *filter, *attrs[2], *attr;
575   int port;
576   int ret;
577
578   host     = myopt->host?   myopt->host   : ludp->lud_host;
579   port     = myopt->port?   myopt->port   : ludp->lud_port;
580   dn       = myopt->dn?     myopt->dn     : ludp->lud_dn;
581   filter   = myopt->filter? myopt->filter : ludp->lud_filter;
582   attrs[0] = myopt->attr?   myopt->attr   : ludp->lud_attrs? ludp->lud_attrs[0]:NULL;
583   attrs[1] = NULL;
584   attr = attrs[0];
585
586   if (!port)
587     port = (ludp->lud_scheme && !strcmp (ludp->lud_scheme, "ldaps"))? 636:389;
588
589   if (myopt->verbose)
590     {
591       log_info (_("processing url '%s'\n"), url);
592       if (myopt->user)
593         log_info (_("          user '%s'\n"), myopt->user);
594       if (myopt->pass)
595         log_info (_("          pass '%s'\n"), *myopt->pass?"*****":"");
596       if (host)
597         log_info (_("          host '%s'\n"), host);
598       log_info (_("          port %d\n"), port);
599       if (dn)
600         log_info (_("            DN '%s'\n"), dn);
601       if (filter)
602         log_info (_("        filter '%s'\n"), filter);
603       if (myopt->multi && !myopt->attr && ludp->lud_attrs)
604         {
605           int i;
606           for (i=0; ludp->lud_attrs[i]; i++)
607             log_info (_("          attr '%s'\n"), ludp->lud_attrs[i]);
608         }
609       else if (attr)
610         log_info (_("          attr '%s'\n"), attr);
611     }
612
613
614   if (!host || !*host)
615     {
616       log_error (_("no host name in '%s'\n"), url);
617       return -1;
618     }
619   if (!myopt->multi && !attr)
620     {
621       log_error (_("no attribute given for query '%s'\n"), url);
622       return -1;
623     }
624
625   if (!myopt->multi && !myopt->attr
626       && ludp->lud_attrs && ludp->lud_attrs[0] && ludp->lud_attrs[1])
627     log_info (_("WARNING: using first attribute only\n"));
628
629
630   set_timeout (myopt);
631   npth_unprotect ();
632   ld = my_ldap_init (host, port);
633   npth_protect ();
634   if (!ld)
635     {
636       log_error (_("LDAP init to '%s:%d' failed: %s\n"),
637                  host, port, strerror (errno));
638       return -1;
639     }
640   npth_unprotect ();
641   /* Fixme:  Can we use MYOPT->user or is it shared with other theeads?.  */
642   ret = my_ldap_simple_bind_s (ld, myopt->user, myopt->pass);
643   npth_protect ();
644 #ifdef LDAP_VERSION3
645   if (ret == LDAP_PROTOCOL_ERROR)
646     {
647       /* Protocol error could mean that the server only supports v3. */
648       int version = LDAP_VERSION3;
649       if (myopt->verbose)
650         log_info ("protocol error; retrying bind with v3 protocol\n");
651       npth_unprotect ();
652       ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &version);
653       ret = my_ldap_simple_bind_s (ld, myopt->user, myopt->pass);
654       npth_protect ();
655     }
656 #endif
657   if (ret)
658     {
659       log_error (_("binding to '%s:%d' failed: %s\n"),
660                  host, port, ldap_err2string (ret));
661       ldap_unbind (ld);
662       return -1;
663     }
664
665   set_timeout (myopt);
666   npth_unprotect ();
667   rc = my_ldap_search_st (ld, dn, ludp->lud_scope, filter,
668                           myopt->multi && !myopt->attr && ludp->lud_attrs?
669                           ludp->lud_attrs:attrs,
670                           0,
671                           &myopt->timeout, &msg);
672   npth_protect ();
673   if (rc == LDAP_SIZELIMIT_EXCEEDED && myopt->multi)
674     {
675       if (es_fwrite ("E\0\0\0\x09truncated", 14, 1, myopt->outstream) != 1)
676         {
677           log_error (_("error writing to stdout: %s\n"), strerror (errno));
678           return -1;
679         }
680     }
681   else if (rc)
682     {
683 #ifdef HAVE_W32CE_SYSTEM
684       log_error ("searching '%s' failed: %d\n", url, rc);
685 #else
686       log_error (_("searching '%s' failed: %s\n"),
687                  url, ldap_err2string (rc));
688 #endif
689       if (rc != LDAP_NO_SUCH_OBJECT)
690         {
691           /* FIXME: Need deinit (ld)?  */
692           /* Hmmm: Do we need to released MSG in case of an error? */
693           return -1;
694         }
695     }
696
697   rc = print_ldap_entries (myopt, ld, msg, myopt->multi? NULL:attr);
698
699   ldap_msgfree (msg);
700   ldap_unbind (ld);
701   return rc;
702 }
703
704
705
706
707 /* Main processing.  Take the URL and run the LDAP query. The result
708    is printed to stdout, errors are logged to the log stream. */
709 static int
710 process_url (my_opt_t myopt, const char *url)
711 {
712   int rc;
713   LDAPURLDesc *ludp = NULL;
714
715
716   if (!ldap_is_ldap_url (url))
717     {
718       log_error (_("'%s' is not an LDAP URL\n"), url);
719       return -1;
720     }
721
722   if (ldap_url_parse (url, &ludp))
723     {
724       log_error (_("'%s' is an invalid LDAP URL\n"), url);
725       return -1;
726     }
727
728   rc = fetch_ldap (myopt, url, ludp);
729
730   ldap_free_urldesc (ludp);
731   return rc;
732 }