1 /* ***** BEGIN LICENSE BLOCK *****
2  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Mozilla Public License Version
5  * 1.1 (the "License"); you may not use this file except in compliance with
6  * the License. You may obtain a copy of the License at
7  * http://www.mozilla.org/MPL/
8  *
9  * Software distributed under the License is distributed on an "AS IS" basis,
10  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11  * for the specific language governing rights and limitations under the
12  * License.
13  *
14  * The Original Code is Mozilla Communicator client code, released
15  * March 31, 1998.
16  *
17  * The Initial Developer of the Original Code is
18  * Netscape Communications Corporation.
19  * Portions created by the Initial Developer are Copyright (C) 1998-1999
20  * the Initial Developer. All Rights Reserved.
21  *
22  * Contributor(s):
23  *
24  * Alternatively, the contents of this file may be used under the terms of
25  * either of the GNU General Public License Version 2 or later (the "GPL"),
26  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27  * in which case the provisions of the GPL or the LGPL are applicable instead
28  * of those above. If you wish to allow use of your version of this file only
29  * under the terms of either the GPL or the LGPL, and not to allow others to
30  * use your version of this file under the terms of the MPL, indicate your
31  * decision by deleting the provisions above and replace them with the notice
32  * and other provisions required by the GPL or the LGPL. If you do not delete
33  * the provisions above, a recipient may use your version of this file under
34  * the terms of any one of the MPL, the GPL or the LGPL.
35  *
36  * ***** END LICENSE BLOCK ***** */
37 #include <nspr.h>
38 #include <stdio.h>
39 #include <ldap.h>
40 
41 #define NAME "cn=Directory Manager"
42 #define PASSWORD "secret99"
43 #define BASE "dc=example,dc=com"
44 
45 static int simplebind(LDAP* ld, char* msg, int tries);
46 static void search_thread(void*);
47 static void modify_thread(void*);
48 static void add_thread(void*);
49 static void delete_thread(void*);
50 static void set_ld_error();
51 static int get_ld_error();
52 static void set_errno();
53 static int get_errno();
54 static void tsd_setup();
55 static void* my_mutex_alloc(void);
56 static void my_mutex_free(void*);
57 static int my_mutex_lock(void*);
58 static int my_mutex_unlock(void*);
59 static LDAPHostEnt* my_gethostbyname(const char* name, LDAPHostEnt* result,
60                                      char* buffer, int buflen, int* statusp,
61                                      void* extradata);
62 static LDAPHostEnt* my_gethostbyaddr(const char* addr, int length, int type,
63                                      LDAPHostEnt* result, char* buffer,
64                                      int buflen, int* statusp, void* extradata);
65 static LDAPHostEnt* copyPRHostEnt2LDAPHostEnt(LDAPHostEnt* ldhp,
66                                               PRHostEnt* prhp);
67 
68 typedef struct ldapmsgwrapper {
69   LDAPMessage* lmw_messagep;
70   struct ldapmsgwrapper* lmw_next;
71 } ldapmsgwrapper;
72 
73 #define CONNECTION_ERROR(lderr) \
74   ((lderr) == LDAP_SERVER_DOWN || (lderr) == LDAP_CONNECT_ERROR)
75 
76 LDAP* ld;
77 PRUintn tsdindex;
78 #ifdef LDAP_MEMCACHE
79 LDAPMemCache* memcache = NULL;
80 #  define MEMCACHE_SIZE (256 * 1024) /* 256K bytes */
81 #  define MEMCACHE_TTL (15 * 60)     /* 15 minutes */
82 #endif
83 
main(int argc,char ** argv)84 main(int argc, char** argv) {
85   PRThread *search_tid, *search_tid2, *search_tid3;
86   PRThread *search_tid4, *modify_tid, *add_tid;
87   PRThread* delete_tid;
88   struct ldap_thread_fns tfns;
89   struct ldap_dns_fns dnsfns;
90   int rc;
91 
92   if (argc != 3) {
93     fprintf(stderr, "usage: %s host port\n", argv[0]);
94     exit(1);
95   }
96 
97   PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
98   if (PR_NewThreadPrivateIndex(&tsdindex, NULL) != PR_SUCCESS) {
99     perror("PR_NewThreadPrivateIndex");
100     exit(1);
101   }
102   tsd_setup(); /* for main thread */
103 
104   if ((ld = ldap_init(argv[1], atoi(argv[2]))) == NULL) {
105     perror("ldap_open");
106     exit(1);
107   }
108 
109   /* set thread function pointers */
110   memset(&tfns, '\0', sizeof(struct ldap_thread_fns));
111   tfns.ltf_mutex_alloc = my_mutex_alloc;
112   tfns.ltf_mutex_free = my_mutex_free;
113   tfns.ltf_mutex_lock = my_mutex_lock;
114   tfns.ltf_mutex_unlock = my_mutex_unlock;
115   tfns.ltf_get_errno = get_errno;
116   tfns.ltf_set_errno = set_errno;
117   tfns.ltf_get_lderrno = get_ld_error;
118   tfns.ltf_set_lderrno = set_ld_error;
119   tfns.ltf_lderrno_arg = NULL;
120   if (ldap_set_option(ld, LDAP_OPT_THREAD_FN_PTRS, (void*)&tfns) != 0) {
121     ldap_perror(ld, "ldap_set_option: thread functions");
122     exit(1);
123   }
124 
125   /* set DNS function pointers */
126   memset(&dnsfns, '\0', sizeof(struct ldap_dns_fns));
127   dnsfns.lddnsfn_bufsize = PR_NETDB_BUF_SIZE;
128   dnsfns.lddnsfn_gethostbyname = my_gethostbyname;
129   dnsfns.lddnsfn_gethostbyaddr = my_gethostbyaddr;
130   if (ldap_set_option(ld, LDAP_OPT_DNS_FN_PTRS, (void*)&dnsfns) != 0) {
131     ldap_perror(ld, "ldap_set_option: DNS functions");
132     exit(1);
133   }
134 
135 #ifdef LDAP_MEMCACHE
136   /* create the in-memory cache */
137   if ((rc = ldap_memcache_init(MEMCACHE_TTL, MEMCACHE_SIZE, NULL, &tfns,
138                                &memcache)) != LDAP_SUCCESS) {
139     fprintf(stderr, "ldap_memcache_init failed - %s\n", ldap_err2string(rc));
140     exit(1);
141   }
142   if ((rc = ldap_memcache_set(ld, memcache)) != LDAP_SUCCESS) {
143     fprintf(stderr, "ldap_memcache_set failed - %s\n", ldap_err2string(rc));
144     exit(1);
145   }
146 #endif
147 
148   /*
149    * set option so that the next call to ldap_simple_bind_s() after
150    * the server connection is lost will attempt to reconnect.
151    */
152   if (ldap_set_option(ld, LDAP_OPT_RECONNECT, LDAP_OPT_ON) != 0) {
153     ldap_perror(ld, "ldap_set_option: reconnect");
154     exit(1);
155   }
156 
157   /* initial bind */
158   if (simplebind(ld, "ldap_simple_bind_s/main", 1) != LDAP_SUCCESS) {
159     exit(1);
160   }
161 
162   /* create the operation threads */
163   if ((search_tid = PR_CreateThread(PR_USER_THREAD, search_thread, "1",
164                                     PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
165                                     PR_UNJOINABLE_THREAD, 0)) == NULL) {
166     perror("PR_CreateThread search_thread");
167     exit(1);
168   }
169   if ((modify_tid = PR_CreateThread(PR_USER_THREAD, modify_thread, "2",
170                                     PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
171                                     PR_UNJOINABLE_THREAD, 0)) == NULL) {
172     perror("PR_CreateThread modify_thread");
173     exit(1);
174   }
175   if ((search_tid2 = PR_CreateThread(PR_USER_THREAD, search_thread, "3",
176                                      PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
177                                      PR_UNJOINABLE_THREAD, 0)) == NULL) {
178     perror("PR_CreateThread search_thread 2");
179     exit(1);
180   }
181   if ((add_tid = PR_CreateThread(PR_USER_THREAD, add_thread, "4",
182                                  PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
183                                  PR_UNJOINABLE_THREAD, 0)) == NULL) {
184     perror("PR_CreateThread add_thread");
185     exit(1);
186   }
187   if ((search_tid3 = PR_CreateThread(PR_USER_THREAD, search_thread, "5",
188                                      PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
189                                      PR_UNJOINABLE_THREAD, 0)) == NULL) {
190     perror("PR_CreateThread search_thread 3");
191     exit(1);
192   }
193   if ((delete_tid = PR_CreateThread(PR_USER_THREAD, delete_thread, "6",
194                                     PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
195                                     PR_UNJOINABLE_THREAD, 0)) == NULL) {
196     perror("PR_CreateThread delete_thread");
197     exit(1);
198   }
199   if ((search_tid4 = PR_CreateThread(PR_USER_THREAD, search_thread, "7",
200                                      PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
201                                      PR_UNJOINABLE_THREAD, 0)) == NULL) {
202     perror("PR_CreateThread search_thread 4");
203     exit(1);
204   }
205 
206   PR_Cleanup();
207   return (0);
208 }
209 
simplebind(LDAP * ld,char * msg,int tries)210 static int simplebind(LDAP* ld, char* msg, int tries) {
211   int rc;
212 
213   while (tries-- > 0) {
214     rc = ldap_simple_bind_s(ld, NAME, PASSWORD);
215     if (rc != LDAP_SUCCESS) {
216       ldap_perror(ld, msg);
217     }
218     if (tries == 0 || !CONNECTION_ERROR(rc)) {
219       return (rc);
220     }
221     fprintf(stderr, "%s: sleeping for 5 secs - will try %d more time(s)...\n",
222             msg, tries);
223     sleep(5);
224   }
225 
226   return (rc);
227 }
228 
search_thread(void * arg1)229 static void search_thread(void* arg1) {
230   LDAPMessage* res;
231   LDAPMessage* e;
232   char* a;
233   char** v;
234   char* dn;
235   BerElement* ber;
236   int i, rc, msgid;
237   void* tsd;
238   char* id = arg1;
239 
240   printf("search_thread\n");
241   tsd_setup();
242   for (;;) {
243     printf("%sSearching...\n", id);
244     if ((msgid = ldap_search(ld, BASE, LDAP_SCOPE_SUBTREE, "(objectclass=*)",
245                              NULL, 0)) == -1) {
246       ldap_perror(ld, "ldap_search_s");
247       rc = ldap_get_lderrno(ld, NULL, NULL);
248       if (CONNECTION_ERROR(rc) &&
249           simplebind(ld, "bind-search_thread", 5) != LDAP_SUCCESS) {
250         return;
251       }
252       continue;
253     }
254     while ((rc = ldap_result(ld, msgid, 0, NULL, &res)) ==
255            LDAP_RES_SEARCH_ENTRY) {
256       for (e = ldap_first_entry(ld, res); e != NULL;
257            e = ldap_next_entry(ld, e)) {
258         dn = ldap_get_dn(ld, e);
259         /* printf( "%sdn: %s\n", id, dn ); */
260         free(dn);
261         for (a = ldap_first_attribute(ld, e, &ber); a != NULL;
262              a = ldap_next_attribute(ld, e, ber)) {
263           v = ldap_get_values(ld, e, a);
264           for (i = 0; v && v[i] != 0; i++) {
265             /*
266             printf( "%s%s: %s\n", id, a,
267                 v[i] );
268             */
269           }
270           ldap_value_free(v);
271           ldap_memfree(a);
272         }
273         if (ber != NULL) {
274           ber_free(ber, 0);
275         }
276       }
277       ldap_msgfree(res);
278       /* printf( "%s\n", id ); */
279     }
280 
281     if (rc == -1 || ldap_result2error(ld, res, 0) != LDAP_SUCCESS) {
282       ldap_perror(ld, "ldap_search");
283     } else {
284       printf("%sDone with one round\n", id);
285     }
286 
287     if (rc == -1) {
288       rc = ldap_get_lderrno(ld, NULL, NULL);
289       if (CONNECTION_ERROR(rc) &&
290           simplebind(ld, "bind-search_thread", 5) != LDAP_SUCCESS) {
291         return;
292       }
293     }
294   }
295 }
296 
modify_thread(void * arg1)297 static void modify_thread(void* arg1) {
298   LDAPMessage* res;
299   LDAPMessage* e;
300   int i, modentry, entries, msgid, rc;
301   LDAPMod mod;
302   LDAPMod* mods[2];
303   char* vals[2];
304   char* dn;
305   char* id = arg1;
306   ldapmsgwrapper *list, *lmwp, *lastlmwp;
307 
308   printf("modify_thread\n");
309   tsd_setup();
310   if ((msgid = ldap_search(ld, BASE, LDAP_SCOPE_SUBTREE, "(objectclass=*)",
311                            NULL, 0)) == -1) {
312     ldap_perror(ld, "ldap_search_s");
313     exit(1);
314   }
315   entries = 0;
316   list = lastlmwp = NULL;
317   while ((rc = ldap_result(ld, msgid, 0, NULL, &res)) ==
318          LDAP_RES_SEARCH_ENTRY) {
319     entries++;
320     if ((lmwp = (ldapmsgwrapper*)malloc(sizeof(ldapmsgwrapper))) == NULL) {
321       perror("modify_thread: malloc");
322       exit(1);
323     }
324     lmwp->lmw_messagep = res;
325     lmwp->lmw_next = NULL;
326     if (lastlmwp == NULL) {
327       list = lastlmwp = lmwp;
328     } else {
329       lastlmwp->lmw_next = lmwp;
330     }
331     lastlmwp = lmwp;
332   }
333   if (rc == -1 || ldap_result2error(ld, res, 0) != LDAP_SUCCESS) {
334     ldap_perror(ld, "modify_thread: ldap_search");
335     exit(1);
336   } else {
337     entries++;
338     printf("%sModify got %d entries\n", id, entries);
339   }
340 
341   mods[0] = &mod;
342   mods[1] = NULL;
343   vals[0] = "bar";
344   vals[1] = NULL;
345   for (;;) {
346     modentry = rand() % entries;
347     for (i = 0, lmwp = list; lmwp != NULL && i < modentry;
348          i++, lmwp = lmwp->lmw_next) {
349       /* NULL */
350     }
351 
352     if (lmwp == NULL) {
353       fprintf(stderr, "%sModify could not find entry %d of %d\n", id, modentry,
354               entries);
355       continue;
356     }
357     e = lmwp->lmw_messagep;
358     printf("%sPicked entry %d of %d\n", id, i, entries);
359     dn = ldap_get_dn(ld, e);
360     mod.mod_op = LDAP_MOD_REPLACE;
361     mod.mod_type = "description";
362     mod.mod_values = vals;
363     printf("%sModifying (%s)\n", id, dn);
364     if ((rc = ldap_modify_s(ld, dn, mods)) != LDAP_SUCCESS) {
365       ldap_perror(ld, "ldap_modify_s");
366       if (CONNECTION_ERROR(rc) &&
367           simplebind(ld, "bind-modify_thread", 5) != LDAP_SUCCESS) {
368         return;
369       }
370     }
371     free(dn);
372   }
373 }
374 
add_thread(void * arg1)375 static void add_thread(void* arg1) {
376   LDAPMod mod[5];
377   LDAPMod* mods[6];
378   char dn[BUFSIZ], name[40];
379   char *cnvals[2], *snvals[2], *ocvals[2];
380   int i, rc;
381   char* id = arg1;
382 
383   printf("add_thread\n");
384   tsd_setup();
385   for (i = 0; i < 5; i++) {
386     mods[i] = &mod[i];
387   }
388   mods[5] = NULL;
389   mod[0].mod_op = 0;
390   mod[0].mod_type = "cn";
391   mod[0].mod_values = cnvals;
392   cnvals[1] = NULL;
393   mod[1].mod_op = 0;
394   mod[1].mod_type = "sn";
395   mod[1].mod_values = snvals;
396   snvals[1] = NULL;
397   mod[2].mod_op = 0;
398   mod[2].mod_type = "objectclass";
399   mod[2].mod_values = ocvals;
400   ocvals[0] = "person";
401   ocvals[1] = NULL;
402   mods[3] = NULL;
403 
404   for (;;) {
405     sprintf(name, "%d", rand());
406     sprintf(dn, "cn=%s, " BASE, name);
407     cnvals[0] = name;
408     snvals[0] = name;
409 
410     printf("%sAdding entry (%s)\n", id, dn);
411     if ((rc = ldap_add_s(ld, dn, mods)) != LDAP_SUCCESS) {
412       ldap_perror(ld, "ldap_add_s");
413       if (CONNECTION_ERROR(rc) &&
414           simplebind(ld, "bind-add_thread", 5) != LDAP_SUCCESS) {
415         return;
416       }
417     }
418   }
419 }
420 
delete_thread(void * arg1)421 static void delete_thread(void* arg1) {
422   LDAPMessage* res;
423   char dn[BUFSIZ], name[40];
424   int entries, msgid, rc;
425   char* id = arg1;
426 
427   printf("delete_thread\n");
428   tsd_setup();
429   if ((msgid = ldap_search(ld, BASE, LDAP_SCOPE_SUBTREE, "(objectclass=*)",
430                            NULL, 0)) == -1) {
431     ldap_perror(ld, "delete_thread: ldap_search_s");
432     exit(1);
433   }
434   entries = 0;
435   while ((rc = ldap_result(ld, msgid, 0, NULL, &res)) ==
436          LDAP_RES_SEARCH_ENTRY) {
437     entries++;
438     ldap_msgfree(res);
439   }
440   entries++;
441   if (rc == -1 || ldap_result2error(ld, res, 1) != LDAP_SUCCESS) {
442     ldap_perror(ld, "delete_thread: ldap_search");
443   } else {
444     printf("%sDelete got %d entries\n", id, entries);
445   }
446 
447   for (;;) {
448     sprintf(name, "%d", rand());
449     sprintf(dn, "cn=%s, " BASE, name);
450 
451     printf("%sDeleting entry (%s)\n", id, dn);
452     if ((rc = ldap_delete_s(ld, dn)) != LDAP_SUCCESS) {
453       ldap_perror(ld, "ldap_delete_s");
454       if (CONNECTION_ERROR(rc) &&
455           simplebind(ld, "bind-delete_thread", 5) != LDAP_SUCCESS) {
456         return;
457       }
458     }
459   }
460 }
461 
462 struct ldap_error {
463   int le_errno;
464   char* le_matched;
465   char* le_errmsg;
466 };
467 
tsd_setup()468 static void tsd_setup() {
469   void* tsd;
470 
471   tsd = (void*)PR_GetThreadPrivate(tsdindex);
472   if (tsd != NULL) {
473     fprintf(stderr, "tsd non-null!\n");
474     exit(1);
475   }
476   tsd = (void*)calloc(1, sizeof(struct ldap_error));
477   if (PR_SetThreadPrivate(tsdindex, tsd) != 0) {
478     perror("PR_SetThreadPrivate");
479     exit(1);
480   }
481 }
482 
set_ld_error(int err,char * matched,char * errmsg,void * dummy)483 static void set_ld_error(int err, char* matched, char* errmsg, void* dummy) {
484   struct ldap_error* le;
485 
486   le = (void*)PR_GetThreadPrivate(tsdindex);
487   le->le_errno = err;
488   if (le->le_matched != NULL) {
489     ldap_memfree(le->le_matched);
490   }
491   le->le_matched = matched;
492   if (le->le_errmsg != NULL) {
493     ldap_memfree(le->le_errmsg);
494   }
495   le->le_errmsg = errmsg;
496 }
497 
get_ld_error(char ** matchedp,char ** errmsgp,void * dummy)498 static int get_ld_error(char** matchedp, char** errmsgp, void* dummy) {
499   struct ldap_error* le;
500 
501   le = PR_GetThreadPrivate(tsdindex);
502   if (matchedp != NULL) {
503     *matchedp = le->le_matched;
504   }
505   if (errmsgp != NULL) {
506     *errmsgp = le->le_errmsg;
507   }
508   return (le->le_errno);
509 }
510 
set_errno(int oserrno)511 static void set_errno(int oserrno) {
512   /* XXXmcs: should this be PR_SetError( oserrno, 0 )? */
513   PR_SetError(PR_UNKNOWN_ERROR, oserrno);
514 }
515 
get_errno(void)516 static int get_errno(void) {
517   /* XXXmcs: should this be PR_GetError()? */
518   return (PR_GetOSError());
519 }
520 
my_mutex_alloc(void)521 static void* my_mutex_alloc(void) { return ((void*)PR_NewLock()); }
522 
my_mutex_free(void * mutex)523 static void my_mutex_free(void* mutex) { PR_DestroyLock((PRLock*)mutex); }
524 
my_mutex_lock(void * mutex)525 static int my_mutex_lock(void* mutex) {
526   PR_Lock((PRLock*)mutex);
527   return (0);
528 }
529 
my_mutex_unlock(void * mutex)530 static int my_mutex_unlock(void* mutex) {
531   if (PR_Unlock((PRLock*)mutex) == PR_FAILURE) {
532     return (-1);
533   }
534 
535   return (0);
536 }
537 
my_gethostbyname(const char * name,LDAPHostEnt * result,char * buffer,int buflen,int * statusp,void * extradata)538 static LDAPHostEnt* my_gethostbyname(const char* name, LDAPHostEnt* result,
539                                      char* buffer, int buflen, int* statusp,
540                                      void* extradata) {
541   PRHostEnt prhent;
542 
543   if (PR_GetHostByName(name, buffer, buflen, &prhent) != PR_SUCCESS) {
544     return (NULL);
545   }
546 
547   return (copyPRHostEnt2LDAPHostEnt(result, &prhent));
548 }
549 
my_gethostbyaddr(const char * addr,int length,int type,LDAPHostEnt * result,char * buffer,int buflen,int * statusp,void * extradata)550 static LDAPHostEnt* my_gethostbyaddr(const char* addr, int length, int type,
551                                      LDAPHostEnt* result, char* buffer,
552                                      int buflen, int* statusp,
553                                      void* extradata) {
554   PRHostEnt prhent;
555 
556   if (PR_GetHostByAddr((PRNetAddr*)addr, buffer, buflen, &prhent) !=
557       PR_SUCCESS) {
558     return (NULL);
559   }
560 
561   return (copyPRHostEnt2LDAPHostEnt(result, &prhent));
562 }
563 
copyPRHostEnt2LDAPHostEnt(LDAPHostEnt * ldhp,PRHostEnt * prhp)564 static LDAPHostEnt* copyPRHostEnt2LDAPHostEnt(LDAPHostEnt* ldhp,
565                                               PRHostEnt* prhp) {
566   ldhp->ldaphe_name = prhp->h_name;
567   ldhp->ldaphe_aliases = prhp->h_aliases;
568   ldhp->ldaphe_addrtype = prhp->h_addrtype;
569   ldhp->ldaphe_length = prhp->h_length;
570   ldhp->ldaphe_addr_list = prhp->h_addr_list;
571   return (ldhp);
572 }
573