1 /*        $NetBSD: ldap_sync.c,v 1.3 2021/08/14 16:14:56 christos Exp $         */
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 2006-2021 The OpenLDAP Foundation.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This program was originally developed by Pierangelo Masarati
19  * for inclusion in OpenLDAP Software.
20  */
21 
22 /*
23  * Proof-of-concept API that implement the client-side
24  * of the "LDAP Content Sync Operation" (RFC 4533)
25  */
26 
27 #include <sys/cdefs.h>
28 __RCSID("$NetBSD: ldap_sync.c,v 1.3 2021/08/14 16:14:56 christos Exp $");
29 
30 #include "portable.h"
31 
32 #include <ac/time.h>
33 
34 #include "ldap-int.h"
35 
36 #ifdef LDAP_SYNC_TRACE
37 static const char *
ldap_sync_state2str(int state)38 ldap_sync_state2str( int state )
39 {
40           switch ( state ) {
41           case LDAP_SYNC_PRESENT:
42                     return "LDAP_SYNC_PRESENT";
43 
44           case LDAP_SYNC_ADD:
45                     return "LDAP_SYNC_ADD";
46 
47           case LDAP_SYNC_MODIFY:
48                     return "LDAP_SYNC_MODIFY";
49 
50           case LDAP_SYNC_DELETE:
51                     return "LDAP_SYNC_DELETE";
52 
53           default:
54                     return "(unknown)";
55           }
56 }
57 #endif
58 
59 /*
60  * initialize the persistent search structure
61  */
62 ldap_sync_t *
ldap_sync_initialize(ldap_sync_t * ls_in)63 ldap_sync_initialize( ldap_sync_t *ls_in )
64 {
65           ldap_sync_t         *ls = ls_in;
66 
67           if ( ls == NULL ) {
68                     ls = ldap_memalloc( sizeof( ldap_sync_t ) );
69                     if ( ls == NULL ) {
70                               return NULL;
71                     }
72           }
73           memset( ls, 0, sizeof( ldap_sync_t ) );
74 
75           ls->ls_scope = LDAP_SCOPE_SUBTREE;
76           ls->ls_timeout = -1;
77 
78           return ls;
79 }
80 
81 /*
82  * destroy the persistent search structure
83  */
84 void
ldap_sync_destroy(ldap_sync_t * ls,int freeit)85 ldap_sync_destroy( ldap_sync_t *ls, int freeit )
86 {
87           assert( ls != NULL );
88 
89           if ( ls->ls_base != NULL ) {
90                     ldap_memfree( ls->ls_base );
91                     ls->ls_base = NULL;
92           }
93 
94           if ( ls->ls_filter != NULL ) {
95                     ldap_memfree( ls->ls_filter );
96                     ls->ls_filter = NULL;
97           }
98 
99           if ( ls->ls_attrs != NULL ) {
100                     int       i;
101 
102                     for ( i = 0; ls->ls_attrs[ i ] != NULL; i++ ) {
103                               ldap_memfree( ls->ls_attrs[ i ] );
104                     }
105                     ldap_memfree( ls->ls_attrs );
106                     ls->ls_attrs = NULL;
107           }
108 
109           if ( ls->ls_ld != NULL ) {
110                     (void)ldap_unbind_ext( ls->ls_ld, NULL, NULL );
111 #ifdef LDAP_SYNC_TRACE
112                     fprintf( stderr, "ldap_unbind_ext()\n" );
113 #endif /* LDAP_SYNC_TRACE */
114                     ls->ls_ld = NULL;
115           }
116 
117           if ( ls->ls_cookie.bv_val != NULL ) {
118                     ldap_memfree( ls->ls_cookie.bv_val );
119                     ls->ls_cookie.bv_val = NULL;
120           }
121 
122           if ( freeit ) {
123                     ldap_memfree( ls );
124           }
125 }
126 
127 /*
128  * handle the LDAP_RES_SEARCH_ENTRY response
129  */
130 static int
ldap_sync_search_entry(ldap_sync_t * ls,LDAPMessage * res)131 ldap_sync_search_entry( ldap_sync_t *ls, LDAPMessage *res )
132 {
133           LDAPControl                   **ctrls = NULL;
134           int                           rc = LDAP_OTHER,
135                                         i;
136           BerElement                    *ber = NULL;
137           struct berval                 entryUUID = { 0 },
138                                         cookie = { 0 };
139           int                           state = -1;
140           ber_len_t           len;
141           ldap_sync_refresh_t phase;
142 
143 #ifdef LDAP_SYNC_TRACE
144           fprintf( stderr, "\tgot LDAP_RES_SEARCH_ENTRY\n" );
145 #endif /* LDAP_SYNC_TRACE */
146 
147           assert( ls != NULL );
148           assert( res != NULL );
149 
150           phase = ls->ls_refreshPhase;
151 
152           /* OK */
153 
154           /* extract:
155            * - data
156            * - entryUUID
157            *
158            * check that:
159            * - Sync State Control is "add"
160            */
161 
162           /* the control MUST be present */
163 
164           /* extract controls */
165           ldap_get_entry_controls( ls->ls_ld, res, &ctrls );
166           if ( ctrls == NULL ) {
167                     goto done;
168           }
169 
170           /* lookup the sync state control */
171           for ( i = 0; ctrls[ i ] != NULL; i++ ) {
172                     if ( strcmp( ctrls[ i ]->ldctl_oid, LDAP_CONTROL_SYNC_STATE ) == 0 ) {
173                               break;
174                     }
175           }
176 
177           /* control must be present; there might be other... */
178           if ( ctrls[ i ] == NULL ) {
179                     goto done;
180           }
181 
182           /* extract data */
183           ber = ber_init( &ctrls[ i ]->ldctl_value );
184           if ( ber == NULL ) {
185                     goto done;
186           }
187           /* scan entryUUID in-place ("m") */
188           if ( ber_scanf( ber, "{em" /*"}"*/, &state, &entryUUID ) == LBER_ERROR
189                     || entryUUID.bv_len == 0 )
190           {
191                     goto done;
192           }
193 
194           if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
195                     /* scan cookie in-place ("m") */
196                     if ( ber_scanf( ber, /*"{"*/ "m}", &cookie ) == LBER_ERROR ) {
197                               goto done;
198                     }
199                     if ( cookie.bv_val != NULL ) {
200                               ber_bvreplace( &ls->ls_cookie, &cookie );
201                     }
202 #ifdef LDAP_SYNC_TRACE
203                     fprintf( stderr, "\t\tgot cookie=%s\n",
204                               cookie.bv_val ? cookie.bv_val : "(null)" );
205 #endif /* LDAP_SYNC_TRACE */
206           }
207 
208           switch ( state ) {
209           case LDAP_SYNC_PRESENT:
210           case LDAP_SYNC_DELETE:
211           case LDAP_SYNC_ADD:
212           case LDAP_SYNC_MODIFY:
213                     /* NOTE: ldap_sync_refresh_t is defined
214                      * as the corresponding LDAP_SYNC_*
215                      * for the 4 above cases */
216                     phase = state;
217 #ifdef LDAP_SYNC_TRACE
218                     fprintf( stderr, "\t\tgot syncState=%s\n", ldap_sync_state2str( state ) );
219 #endif /* LDAP_SYNC_TRACE */
220                     break;
221 
222           default:
223 #ifdef LDAP_SYNC_TRACE
224                     fprintf( stderr, "\t\tgot unknown syncState=%d\n", state );
225 #endif /* LDAP_SYNC_TRACE */
226                     goto done;
227           }
228 
229           rc = ls->ls_search_entry
230                     ? ls->ls_search_entry( ls, res, &entryUUID, phase )
231                     : LDAP_SUCCESS;
232 
233 done:;
234           if ( ber != NULL ) {
235                     ber_free( ber, 1 );
236           }
237 
238           if ( ctrls != NULL ) {
239                     ldap_controls_free( ctrls );
240           }
241 
242           return rc;
243 }
244 
245 /*
246  * handle the LDAP_RES_SEARCH_REFERENCE response
247  * (to be implemented yet)
248  */
249 static int
ldap_sync_search_reference(ldap_sync_t * ls,LDAPMessage * res)250 ldap_sync_search_reference( ldap_sync_t *ls, LDAPMessage *res )
251 {
252           int                 rc = 0;
253 
254 #ifdef LDAP_SYNC_TRACE
255           fprintf( stderr, "\tgot LDAP_RES_SEARCH_REFERENCE\n" );
256 #endif /* LDAP_SYNC_TRACE */
257 
258           assert( ls != NULL );
259           assert( res != NULL );
260 
261           if ( ls->ls_search_reference ) {
262                     rc = ls->ls_search_reference( ls, res );
263           }
264 
265           return rc;
266 }
267 
268 /*
269  * handle the LDAP_RES_SEARCH_RESULT response
270  */
271 static int
ldap_sync_search_result(ldap_sync_t * ls,LDAPMessage * res)272 ldap_sync_search_result( ldap_sync_t *ls, LDAPMessage *res )
273 {
274           int                 err;
275           char                *matched = NULL,
276                               *msg = NULL;
277           LDAPControl         **ctrls = NULL;
278           int                 rc;
279           int                 refreshDeletes = -1;
280 
281 #ifdef LDAP_SYNC_TRACE
282           fprintf( stderr, "\tgot LDAP_RES_SEARCH_RESULT\n" );
283 #endif /* LDAP_SYNC_TRACE */
284 
285           assert( ls != NULL );
286           assert( res != NULL );
287 
288           /* should not happen in refreshAndPersist... */
289           rc = ldap_parse_result( ls->ls_ld,
290                     res, &err, &matched, &msg, NULL, &ctrls, 0 );
291 #ifdef LDAP_SYNC_TRACE
292           fprintf( stderr,
293                     "\tldap_parse_result(%d, \"%s\", \"%s\") == %d\n",
294                     err,
295                     matched ? matched : "",
296                     msg ? msg : "",
297                     rc );
298 #endif /* LDAP_SYNC_TRACE */
299           if ( rc == LDAP_SUCCESS ) {
300                     rc = err;
301           }
302 
303           ls->ls_refreshPhase = LDAP_SYNC_CAPI_DONE;
304 
305           switch ( rc ) {
306           case LDAP_SUCCESS: {
307                     int                 i;
308                     BerElement          *ber = NULL;
309                     ber_len_t len;
310                     struct berval       cookie = { 0 };
311 
312                     rc = LDAP_OTHER;
313 
314                     /* deal with control; then fallthru to handler */
315                     if ( ctrls == NULL ) {
316                               goto done;
317                     }
318 
319                     /* lookup the sync state control */
320                     for ( i = 0; ctrls[ i ] != NULL; i++ ) {
321                               if ( strcmp( ctrls[ i ]->ldctl_oid,
322                                         LDAP_CONTROL_SYNC_DONE ) == 0 )
323                               {
324                                         break;
325                               }
326                     }
327 
328                     /* control must be present; there might be other... */
329                     if ( ctrls[ i ] == NULL ) {
330                               goto done;
331                     }
332 
333                     /* extract data */
334                     ber = ber_init( &ctrls[ i ]->ldctl_value );
335                     if ( ber == NULL ) {
336                               goto done;
337                     }
338 
339                     if ( ber_scanf( ber, "{" /*"}"*/) == LBER_ERROR ) {
340                               goto ber_done;
341                     }
342                     if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
343                               if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) {
344                                         goto ber_done;
345                               }
346                               if ( cookie.bv_val != NULL ) {
347                                         ber_bvreplace( &ls->ls_cookie, &cookie );
348                               }
349 #ifdef LDAP_SYNC_TRACE
350                               fprintf( stderr, "\t\tgot cookie=%s\n",
351                                         cookie.bv_val ? cookie.bv_val : "(null)" );
352 #endif /* LDAP_SYNC_TRACE */
353                     }
354 
355                     refreshDeletes = 0;
356                     if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDELETES ) {
357                               if ( ber_scanf( ber, "b", &refreshDeletes ) == LBER_ERROR ) {
358                                         goto ber_done;
359                               }
360                               if ( refreshDeletes ) {
361                                         refreshDeletes = 1;
362                               }
363                     }
364 
365                     if ( ber_scanf( ber, /*"{"*/ "}" ) != LBER_ERROR ) {
366                               rc = LDAP_SUCCESS;
367                     }
368 
369           ber_done:;
370                     ber_free( ber, 1 );
371                     if ( rc != LDAP_SUCCESS ) {
372                               break;
373                     }
374 
375 #ifdef LDAP_SYNC_TRACE
376                     fprintf( stderr, "\t\tgot refreshDeletes=%s\n",
377                               refreshDeletes ? "TRUE" : "FALSE" );
378 #endif /* LDAP_SYNC_TRACE */
379 
380                     /* FIXME: what should we do with the refreshDelete? */
381                     switch ( refreshDeletes ) {
382                     case 0:
383                               ls->ls_refreshPhase = LDAP_SYNC_CAPI_PRESENTS;
384                               break;
385 
386                     default:
387                               ls->ls_refreshPhase = LDAP_SYNC_CAPI_DELETES;
388                               break;
389                     }
390 
391                     } /* fallthru */
392 
393           case LDAP_SYNC_REFRESH_REQUIRED:
394                     /* TODO: check for Sync Done Control */
395                     /* FIXME: perhaps the handler should be called
396                      * also in case of failure; we'll deal with this
397                      * later when implementing refreshOnly */
398                     if ( ls->ls_search_result ) {
399                               err = ls->ls_search_result( ls, res, refreshDeletes );
400                     }
401                     break;
402           }
403 
404 done:;
405           if ( matched != NULL ) {
406                     ldap_memfree( matched );
407           }
408 
409           if ( msg != NULL ) {
410                     ldap_memfree( msg );
411           }
412 
413           if ( ctrls != NULL ) {
414                     ldap_controls_free( ctrls );
415           }
416 
417           ls->ls_refreshPhase = LDAP_SYNC_CAPI_DONE;
418 
419           return rc;
420 }
421 
422 /*
423  * handle the LDAP_RES_INTERMEDIATE response
424  */
425 static int
ldap_sync_search_intermediate(ldap_sync_t * ls,LDAPMessage * res,int * refreshDone)426 ldap_sync_search_intermediate( ldap_sync_t *ls, LDAPMessage *res, int *refreshDone )
427 {
428           int                           rc;
429           char                          *retoid = NULL;
430         struct berval                   *retdata = NULL;
431           BerElement                    *ber = NULL;
432           ber_len_t           len;
433           ber_tag_t           syncinfo_tag;
434           struct berval                 cookie;
435           int                           refreshDeletes = 0;
436           BerVarray           syncUUIDs = NULL;
437           ldap_sync_refresh_t phase;
438 
439 #ifdef LDAP_SYNC_TRACE
440           fprintf( stderr, "\tgot LDAP_RES_INTERMEDIATE\n" );
441 #endif /* LDAP_SYNC_TRACE */
442 
443           assert( ls != NULL );
444           assert( res != NULL );
445           assert( refreshDone != NULL );
446 
447           *refreshDone = 0;
448 
449           rc = ldap_parse_intermediate( ls->ls_ld, res,
450                     &retoid, &retdata, NULL, 0 );
451 #ifdef LDAP_SYNC_TRACE
452           fprintf( stderr, "\t%sldap_parse_intermediate(%s) == %d\n",
453                     rc != LDAP_SUCCESS ? "!!! " : "",
454                     retoid == NULL ? "\"\"" : retoid,
455                     rc );
456 #endif /* LDAP_SYNC_TRACE */
457           /* parsing must be successful, and yield the OID
458            * of the sync info intermediate response */
459           if ( rc != LDAP_SUCCESS ) {
460                     goto done;
461           }
462 
463           rc = LDAP_OTHER;
464 
465           if ( retoid == NULL || strcmp( retoid, LDAP_SYNC_INFO ) != 0 ) {
466                     goto done;
467           }
468 
469           /* init ber using the value in the response */
470           ber = ber_init( retdata );
471           if ( ber == NULL ) {
472                     goto done;
473           }
474 
475           syncinfo_tag = ber_peek_tag( ber, &len );
476           switch ( syncinfo_tag ) {
477           case LDAP_TAG_SYNC_NEW_COOKIE:
478                     if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) {
479                               goto done;
480                     }
481                     if ( cookie.bv_val != NULL ) {
482                               ber_bvreplace( &ls->ls_cookie, &cookie );
483                     }
484 #ifdef LDAP_SYNC_TRACE
485                     fprintf( stderr, "\t\tgot cookie=%s\n",
486                               cookie.bv_val ? cookie.bv_val : "(null)" );
487 #endif /* LDAP_SYNC_TRACE */
488                     break;
489 
490           case LDAP_TAG_SYNC_REFRESH_DELETE:
491           case LDAP_TAG_SYNC_REFRESH_PRESENT:
492                     if ( syncinfo_tag == LDAP_TAG_SYNC_REFRESH_DELETE ) {
493 #ifdef LDAP_SYNC_TRACE
494                               fprintf( stderr, "\t\tgot refreshDelete\n" );
495 #endif /* LDAP_SYNC_TRACE */
496                               switch ( ls->ls_refreshPhase ) {
497                               case LDAP_SYNC_CAPI_NONE:
498                               case LDAP_SYNC_CAPI_PRESENTS:
499                                         ls->ls_refreshPhase = LDAP_SYNC_CAPI_DELETES;
500                                         break;
501 
502                               default:
503                                         /* TODO: impossible; handle */
504                                         goto done;
505                               }
506 
507                     } else {
508 #ifdef LDAP_SYNC_TRACE
509                               fprintf( stderr, "\t\tgot refreshPresent\n" );
510 #endif /* LDAP_SYNC_TRACE */
511                               switch ( ls->ls_refreshPhase ) {
512                               case LDAP_SYNC_CAPI_NONE:
513                                         ls->ls_refreshPhase = LDAP_SYNC_CAPI_PRESENTS;
514                                         break;
515 
516                               default:
517                                         /* TODO: impossible; handle */
518                                         goto done;
519                               }
520                     }
521 
522                     if ( ber_scanf( ber, "{" /*"}"*/ ) == LBER_ERROR ) {
523                               goto done;
524                     }
525                     if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
526                               if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) {
527                                         goto done;
528                               }
529                               if ( cookie.bv_val != NULL ) {
530                                         ber_bvreplace( &ls->ls_cookie, &cookie );
531                               }
532 #ifdef LDAP_SYNC_TRACE
533                               fprintf( stderr, "\t\tgot cookie=%s\n",
534                                         cookie.bv_val ? cookie.bv_val : "(null)" );
535 #endif /* LDAP_SYNC_TRACE */
536                     }
537 
538                     *refreshDone = 1;
539                     if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDONE ) {
540                               if ( ber_scanf( ber, "b", refreshDone ) == LBER_ERROR ) {
541                                         goto done;
542                               }
543                     }
544 
545 #ifdef LDAP_SYNC_TRACE
546                     fprintf( stderr, "\t\tgot refreshDone=%s\n",
547                               *refreshDone ? "TRUE" : "FALSE" );
548 #endif /* LDAP_SYNC_TRACE */
549 
550                     if ( ber_scanf( ber, /*"{"*/ "}" ) == LBER_ERROR ) {
551                               goto done;
552                     }
553 
554                     if ( *refreshDone ) {
555                               ls->ls_refreshPhase = LDAP_SYNC_CAPI_DONE;
556                     }
557 
558                     if ( ls->ls_intermediate ) {
559                               ls->ls_intermediate( ls, res, NULL, ls->ls_refreshPhase );
560                     }
561 
562                     break;
563 
564           case LDAP_TAG_SYNC_ID_SET:
565 #ifdef LDAP_SYNC_TRACE
566                     fprintf( stderr, "\t\tgot syncIdSet\n" );
567 #endif /* LDAP_SYNC_TRACE */
568                     if ( ber_scanf( ber, "{" /*"}"*/ ) == LBER_ERROR ) {
569                               goto done;
570                     }
571                     if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
572                               if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) {
573                                         goto done;
574                               }
575                               if ( cookie.bv_val != NULL ) {
576                                         ber_bvreplace( &ls->ls_cookie, &cookie );
577                               }
578 #ifdef LDAP_SYNC_TRACE
579                               fprintf( stderr, "\t\tgot cookie=%s\n",
580                                         cookie.bv_val ? cookie.bv_val : "(null)" );
581 #endif /* LDAP_SYNC_TRACE */
582                     }
583 
584                     if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDELETES ) {
585                               if ( ber_scanf( ber, "b", &refreshDeletes ) == LBER_ERROR ) {
586                                         goto done;
587                               }
588                     }
589 
590                     if ( ber_scanf( ber, /*"{"*/ "[W]}", &syncUUIDs ) == LBER_ERROR
591                               || syncUUIDs == NULL )
592                     {
593                               goto done;
594                     }
595 
596 #ifdef LDAP_SYNC_TRACE
597                     {
598                               int       i;
599 
600                               fprintf( stderr, "\t\tgot refreshDeletes=%s\n",
601                                         refreshDeletes ? "TRUE" : "FALSE" );
602                               for ( i = 0; syncUUIDs[ i ].bv_val != NULL; i++ ) {
603                                         char      buf[ BUFSIZ ];
604                                         fprintf( stderr, "\t\t%s\n",
605                                                   lutil_uuidstr_from_normalized(
606                                                             syncUUIDs[ i ].bv_val, syncUUIDs[ i ].bv_len,
607                                                             buf, sizeof( buf ) ) );
608                               }
609                     }
610 #endif /* LDAP_SYNC_TRACE */
611 
612                     if ( refreshDeletes ) {
613                               phase = LDAP_SYNC_CAPI_DELETES_IDSET;
614 
615                     } else {
616                               phase = LDAP_SYNC_CAPI_PRESENTS_IDSET;
617                     }
618 
619                     /* FIXME: should touch ls->ls_refreshPhase? */
620                     if ( ls->ls_intermediate ) {
621                               ls->ls_intermediate( ls, res, syncUUIDs, phase );
622                     }
623 
624                     ber_bvarray_free( syncUUIDs );
625                     break;
626 
627           default:
628 #ifdef LDAP_SYNC_TRACE
629                     fprintf( stderr, "\t\tunknown tag!\n" );
630 #endif /* LDAP_SYNC_TRACE */
631                     goto done;
632           }
633 
634           rc = LDAP_SUCCESS;
635 
636 done:;
637           if ( ber != NULL ) {
638                     ber_free( ber, 1 );
639           }
640 
641           if ( retoid != NULL ) {
642                     ldap_memfree( retoid );
643           }
644 
645           if ( retdata != NULL ) {
646                     ber_bvfree( retdata );
647           }
648 
649           return rc;
650 }
651 
652 /*
653  * initialize the sync
654  */
655 int
ldap_sync_init(ldap_sync_t * ls,int mode)656 ldap_sync_init( ldap_sync_t *ls, int mode )
657 {
658           LDAPControl         ctrl = { 0 },
659                               *ctrls[ 2 ];
660           BerElement          *ber = NULL;
661           int                 rc;
662           struct timeval      tv = { 0 },
663                               *tvp = NULL;
664           LDAPMessage         *res = NULL;
665 
666 #ifdef LDAP_SYNC_TRACE
667           fprintf( stderr, "ldap_sync_init(%s)...\n",
668                     mode == LDAP_SYNC_REFRESH_AND_PERSIST ?
669                               "LDAP_SYNC_REFRESH_AND_PERSIST" :
670                               ( mode == LDAP_SYNC_REFRESH_ONLY ?
671                                         "LDAP_SYNC_REFRESH_ONLY" : "unknown" ) );
672 #endif /* LDAP_SYNC_TRACE */
673 
674           assert( ls != NULL );
675           assert( ls->ls_ld != NULL );
676 
677           /* support both refreshOnly and refreshAndPersist */
678           switch ( mode ) {
679           case LDAP_SYNC_REFRESH_AND_PERSIST:
680           case LDAP_SYNC_REFRESH_ONLY:
681                     break;
682 
683           default:
684                     fprintf( stderr, "ldap_sync_init: unknown mode=%d\n", mode );
685                     return LDAP_PARAM_ERROR;
686           }
687 
688           /* check consistency of cookie and reloadHint at initial refresh */
689           if ( ls->ls_cookie.bv_val == NULL && ls->ls_reloadHint != 0 ) {
690                     fprintf( stderr, "ldap_sync_init: inconsistent cookie/rhint\n" );
691                     return LDAP_PARAM_ERROR;
692           }
693 
694           ctrls[ 0 ] = &ctrl;
695           ctrls[ 1 ] = NULL;
696 
697           /* prepare the Sync Request control */
698           ber = ber_alloc_t( LBER_USE_DER );
699 #ifdef LDAP_SYNC_TRACE
700           fprintf( stderr, "%sber_alloc_t() %s= NULL\n",
701                     ber == NULL ? "!!! " : "",
702                     ber == NULL ? "=" : "!" );
703 #endif /* LDAP_SYNC_TRACE */
704           if ( ber == NULL ) {
705                     rc = LDAP_NO_MEMORY;
706                     goto done;
707           }
708 
709           ls->ls_refreshPhase = LDAP_SYNC_CAPI_NONE;
710 
711           if ( ls->ls_cookie.bv_val != NULL ) {
712                     ber_printf( ber, "{eOb}", mode,
713                               &ls->ls_cookie, ls->ls_reloadHint );
714 
715           } else {
716                     ber_printf( ber, "{eb}", mode, ls->ls_reloadHint );
717           }
718 
719           rc = ber_flatten2( ber, &ctrl.ldctl_value, 0 );
720 #ifdef LDAP_SYNC_TRACE
721           fprintf( stderr,
722                     "%sber_flatten2() == %d\n",
723                     rc ? "!!! " : "",
724                     rc );
725 #endif /* LDAP_SYNC_TRACE */
726           if ( rc < 0 ) {
727                     rc = LDAP_OTHER;
728                 goto done;
729         }
730 
731           /* make the control critical, as we cannot proceed without */
732           ctrl.ldctl_oid = LDAP_CONTROL_SYNC;
733           ctrl.ldctl_iscritical = 1;
734 
735           /* timelimit? */
736           if ( ls->ls_timelimit ) {
737                     tv.tv_sec = ls->ls_timelimit;
738                     tvp = &tv;
739           }
740 
741           /* actually run the search */
742           rc = ldap_search_ext( ls->ls_ld,
743                     ls->ls_base, ls->ls_scope, ls->ls_filter,
744                     ls->ls_attrs, 0, ctrls, NULL,
745                     tvp, ls->ls_sizelimit, &ls->ls_msgid );
746 #ifdef LDAP_SYNC_TRACE
747           fprintf( stderr,
748                     "%sldap_search_ext(\"%s\", %d, \"%s\") == %d\n",
749                     rc ? "!!! " : "",
750                     ls->ls_base, ls->ls_scope, ls->ls_filter, rc );
751 #endif /* LDAP_SYNC_TRACE */
752           if ( rc != LDAP_SUCCESS ) {
753                     goto done;
754           }
755 
756           /* initial content/content update phase */
757           for ( ; ; ) {
758                     LDAPMessage         *msg = NULL;
759 
760                     /* NOTE: this very short timeout is just to let
761                      * ldap_result() yield long enough to get something */
762                     tv.tv_sec = 0;
763                     tv.tv_usec = 100000;
764 
765                     rc = ldap_result( ls->ls_ld, ls->ls_msgid,
766                               LDAP_MSG_RECEIVED, &tv, &res );
767 #ifdef LDAP_SYNC_TRACE
768                     fprintf( stderr,
769                               "\t%sldap_result(%d) == %d\n",
770                               rc == -1 ? "!!! " : "",
771                               ls->ls_msgid, rc );
772 #endif /* LDAP_SYNC_TRACE */
773                     switch ( rc ) {
774                     case 0:
775                               /*
776                                * timeout
777                                *
778                                * TODO: can do something else in the meanwhile)
779                                */
780                               break;
781 
782                     case -1:
783                               /* smtg bad! */
784                               goto done;
785 
786                     default:
787                               for ( msg = ldap_first_message( ls->ls_ld, res );
788                                         msg != NULL;
789                                         msg = ldap_next_message( ls->ls_ld, msg ) )
790                               {
791                                         int       refreshDone;
792 
793                                         switch ( ldap_msgtype( msg ) ) {
794                                         case LDAP_RES_SEARCH_ENTRY:
795                                                   rc = ldap_sync_search_entry( ls, res );
796                                                   break;
797 
798                                         case LDAP_RES_SEARCH_REFERENCE:
799                                                   rc = ldap_sync_search_reference( ls, res );
800                                                   break;
801 
802                                         case LDAP_RES_SEARCH_RESULT:
803                                                   rc = ldap_sync_search_result( ls, res );
804                                                   goto done_search;
805 
806                                         case LDAP_RES_INTERMEDIATE:
807                                                   rc = ldap_sync_search_intermediate( ls, res, &refreshDone );
808                                                   if ( rc != LDAP_SUCCESS || refreshDone ) {
809                                                             goto done_search;
810                                                   }
811                                                   break;
812 
813                                         default:
814 #ifdef LDAP_SYNC_TRACE
815                                                   fprintf( stderr, "\tgot something unexpected...\n" );
816 #endif /* LDAP_SYNC_TRACE */
817 
818                                                   ldap_msgfree( res );
819 
820                                                   rc = LDAP_OTHER;
821                                                   goto done;
822                                         }
823                               }
824                               ldap_msgfree( res );
825                               res = NULL;
826                               break;
827                     }
828           }
829 
830 done_search:;
831           ldap_msgfree( res );
832 
833 done:;
834           if ( ber != NULL ) {
835                     ber_free( ber, 1 );
836           }
837 
838           return rc;
839 }
840 
841 /*
842  * initialize the refreshOnly sync
843  */
844 int
ldap_sync_init_refresh_only(ldap_sync_t * ls)845 ldap_sync_init_refresh_only( ldap_sync_t *ls )
846 {
847           return ldap_sync_init( ls, LDAP_SYNC_REFRESH_ONLY );
848 }
849 
850 /*
851  * initialize the refreshAndPersist sync
852  */
853 int
ldap_sync_init_refresh_and_persist(ldap_sync_t * ls)854 ldap_sync_init_refresh_and_persist( ldap_sync_t *ls )
855 {
856           return ldap_sync_init( ls, LDAP_SYNC_REFRESH_AND_PERSIST );
857 }
858 
859 /*
860  * poll for new responses
861  */
862 int
ldap_sync_poll(ldap_sync_t * ls)863 ldap_sync_poll( ldap_sync_t *ls )
864 {
865           struct    timeval             tv,
866                                         *tvp = NULL;
867           LDAPMessage                   *res = NULL,
868                                         *msg;
869           int                           rc = 0;
870 
871 #ifdef LDAP_SYNC_TRACE
872           fprintf( stderr, "ldap_sync_poll...\n" );
873 #endif /* LDAP_SYNC_TRACE */
874 
875           assert( ls != NULL );
876           assert( ls->ls_ld != NULL );
877 
878           if ( ls->ls_timeout != -1 ) {
879                     tv.tv_sec = ls->ls_timeout;
880                     tv.tv_usec = 0;
881                     tvp = &tv;
882           }
883 
884           rc = ldap_result( ls->ls_ld, ls->ls_msgid,
885                     LDAP_MSG_RECEIVED, tvp, &res );
886           if ( rc <= 0 ) {
887                     return rc;
888           }
889 
890           for ( msg = ldap_first_message( ls->ls_ld, res );
891                     msg;
892                     msg = ldap_next_message( ls->ls_ld, msg ) )
893           {
894                     int       refreshDone;
895 
896                     switch ( ldap_msgtype( msg ) ) {
897                     case LDAP_RES_SEARCH_ENTRY:
898                               rc = ldap_sync_search_entry( ls, res );
899                               break;
900 
901                     case LDAP_RES_SEARCH_REFERENCE:
902                               rc = ldap_sync_search_reference( ls, res );
903                               break;
904 
905                     case LDAP_RES_SEARCH_RESULT:
906                               rc = ldap_sync_search_result( ls, res );
907                               goto done_search;
908 
909                     case LDAP_RES_INTERMEDIATE:
910                               rc = ldap_sync_search_intermediate( ls, res, &refreshDone );
911                               if ( rc != LDAP_SUCCESS || refreshDone ) {
912                                         goto done_search;
913                               }
914                               break;
915 
916                     default:
917 #ifdef LDAP_SYNC_TRACE
918                               fprintf( stderr, "\tgot something unexpected...\n" );
919 #endif /* LDAP_SYNC_TRACE */
920 
921                               ldap_msgfree( res );
922 
923                               rc = LDAP_OTHER;
924                               goto done;
925                     }
926           }
927 
928 done_search:;
929           ldap_msgfree( res );
930 
931 done:;
932           return rc;
933 }
934