Back to index

openldap  2.4.31
slapd-search.c
Go to the documentation of this file.
00001 /* $OpenLDAP$ */
00002 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00003  *
00004  * Copyright 1999-2012 The OpenLDAP Foundation.
00005  * All rights reserved.
00006  *
00007  * Redistribution and use in source and binary forms, with or without
00008  * modification, are permitted only as authorized by the OpenLDAP
00009  * Public License.
00010  *
00011  * A copy of this license is available in file LICENSE in the
00012  * top-level directory of the distribution or, alternatively, at
00013  * <http://www.OpenLDAP.org/license.html>.
00014  */
00015 /* ACKNOWLEDGEMENTS:
00016  * This work was initially developed by Kurt Spanier for inclusion
00017  * in OpenLDAP Software.
00018  */
00019 
00020 #include "portable.h"
00021 
00022 #include <stdio.h>
00023 
00024 #include "ac/stdlib.h"
00025 
00026 #include "ac/ctype.h"
00027 #include "ac/param.h"
00028 #include "ac/socket.h"
00029 #include "ac/string.h"
00030 #include "ac/unistd.h"
00031 #include "ac/wait.h"
00032 
00033 #include "ldap.h"
00034 #include "lutil.h"
00035 #include "ldap_pvt.h"
00036 
00037 #include "slapd-common.h"
00038 
00039 #define LOOPS 100
00040 #define RETRIES      0
00041 
00042 static void
00043 do_search( char *uri, char *manager, struct berval *passwd,
00044        char *sbase, int scope, char *filter, LDAP **ldp,
00045        char **attrs, int noattrs, int nobind,
00046        int innerloop, int maxretries, int delay, int force, int chaserefs );
00047 
00048 static void
00049 do_random( char *uri, char *manager, struct berval *passwd,
00050        char *sbase, int scope, char *filter, char *attr,
00051        char **attrs, int noattrs, int nobind,
00052        int innerloop, int maxretries, int delay, int force, int chaserefs );
00053 
00054 static void
00055 usage( char *name, char o )
00056 {
00057        if ( o != '\0' ) {
00058               fprintf( stderr, "unknown/incorrect option \"%c\"\n", o );
00059        }
00060 
00061         fprintf( stderr,
00062               "usage: %s "
00063               "-H <uri> | ([-h <host>] -p <port>) "
00064               "-D <manager> "
00065               "-w <passwd> "
00066               "-b <searchbase> "
00067               "-s <scope> "
00068               "-f <searchfilter> "
00069               "[-a <attr>] "
00070               "[-A] "
00071               "[-C] "
00072               "[-F] "
00073               "[-N] "
00074               "[-S[S[S]]] "
00075               "[-i <ignore>] "
00076               "[-l <loops>] "
00077               "[-L <outerloops>] "
00078               "[-r <maxretries>] "
00079               "[-t <delay>] "
00080               "[<attrs>] "
00081               "\n",
00082                      name );
00083        exit( EXIT_FAILURE );
00084 }
00085 
00086 /* -S: just send requests without reading responses
00087  * -SS: send all requests asynchronous and immediately start reading responses
00088  * -SSS: send all requests asynchronous; then read responses
00089  */
00090 static int swamp;
00091 
00092 int
00093 main( int argc, char **argv )
00094 {
00095        int           i;
00096        char          *uri = NULL;
00097        char          *host = "localhost";
00098        int           port = -1;
00099        char          *manager = NULL;
00100        struct berval passwd = { 0, NULL };
00101        char          *sbase = NULL;
00102        int           scope = LDAP_SCOPE_SUBTREE;
00103        char          *filter  = NULL;
00104        char          *attr = NULL;
00105        char          *srchattrs[] = { "cn", "sn", NULL };
00106        char          **attrs = srchattrs;
00107        int           loops = LOOPS;
00108        int           outerloops = 1;
00109        int           retries = RETRIES;
00110        int           delay = 0;
00111        int           force = 0;
00112        int           chaserefs = 0;
00113        int           noattrs = 0;
00114        int           nobind = 0;
00115 
00116        tester_init( "slapd-search", TESTER_SEARCH );
00117 
00118        /* by default, tolerate referrals and no such object */
00119        tester_ignore_str2errlist( "REFERRAL,NO_SUCH_OBJECT" );
00120 
00121        while ( ( i = getopt( argc, argv, "Aa:b:CD:f:FH:h:i:l:L:Np:r:Ss:t:T:w:" ) ) != EOF )
00122        {
00123               switch ( i ) {
00124               case 'A':
00125                      noattrs++;
00126                      break;
00127 
00128               case 'C':
00129                      chaserefs++;
00130                      break;
00131 
00132               case 'H':            /* the server uri */
00133                      uri = strdup( optarg );
00134                      break;
00135 
00136               case 'h':            /* the servers host */
00137                      host = strdup( optarg );
00138                      break;
00139 
00140               case 'i':
00141                      tester_ignore_str2errlist( optarg );
00142                      break;
00143 
00144               case 'N':
00145                      nobind++;
00146                      break;
00147 
00148               case 'p':            /* the servers port */
00149                      if ( lutil_atoi( &port, optarg ) != 0 ) {
00150                             usage( argv[0], i );
00151                      }
00152                      break;
00153 
00154               case 'D':            /* the servers manager */
00155                      manager = strdup( optarg );
00156                      break;
00157 
00158               case 'w':            /* the server managers password */
00159                      passwd.bv_val = strdup( optarg );
00160                      passwd.bv_len = strlen( optarg );
00161                      memset( optarg, '*', passwd.bv_len );
00162                      break;
00163 
00164               case 'a':
00165                      attr = strdup( optarg );
00166                      break;
00167 
00168               case 'b':            /* file with search base */
00169                      sbase = strdup( optarg );
00170                      break;
00171 
00172               case 'f':            /* the search request */
00173                      filter = strdup( optarg );
00174                      break;
00175 
00176               case 'F':
00177                      force++;
00178                      break;
00179 
00180               case 'l':            /* number of loops */
00181                      if ( lutil_atoi( &loops, optarg ) != 0 ) {
00182                             usage( argv[0], i );
00183                      }
00184                      break;
00185 
00186               case 'L':            /* number of loops */
00187                      if ( lutil_atoi( &outerloops, optarg ) != 0 ) {
00188                             usage( argv[0], i );
00189                      }
00190                      break;
00191 
00192               case 'r':            /* number of retries */
00193                      if ( lutil_atoi( &retries, optarg ) != 0 ) {
00194                             usage( argv[0], i );
00195                      }
00196                      break;
00197 
00198               case 't':            /* delay in seconds */
00199                      if ( lutil_atoi( &delay, optarg ) != 0 ) {
00200                             usage( argv[0], i );
00201                      }
00202                      break;
00203 
00204               case 'T':
00205                      attrs = ldap_str2charray( optarg, "," );
00206                      if ( attrs == NULL ) {
00207                             usage( argv[0], i );
00208                      }
00209                      break;
00210 
00211               case 'S':
00212                      swamp++;
00213                      break;
00214 
00215               case 's':
00216                      scope = ldap_pvt_str2scope( optarg );
00217                      if ( scope == -1 ) {
00218                             usage( argv[0], i );
00219                      }
00220                      break;
00221 
00222               default:
00223                      usage( argv[0], i );
00224                      break;
00225               }
00226        }
00227 
00228        if (( sbase == NULL ) || ( filter == NULL ) || ( port == -1 && uri == NULL ))
00229               usage( argv[0], '\0' );
00230 
00231        if ( *filter == '\0' ) {
00232 
00233               fprintf( stderr, "%s: invalid EMPTY search filter.\n",
00234                             argv[0] );
00235               exit( EXIT_FAILURE );
00236 
00237        }
00238 
00239        if ( argv[optind] != NULL ) {
00240               attrs = &argv[optind];
00241        }
00242 
00243        uri = tester_uri( uri, host, port );
00244 
00245        for ( i = 0; i < outerloops; i++ ) {
00246               if ( attr != NULL ) {
00247                      do_random( uri, manager, &passwd,
00248                             sbase, scope, filter, attr,
00249                             attrs, noattrs, nobind,
00250                             loops, retries, delay, force, chaserefs );
00251 
00252               } else {
00253                      do_search( uri, manager, &passwd,
00254                             sbase, scope, filter, NULL,
00255                             attrs, noattrs, nobind,
00256                             loops, retries, delay, force, chaserefs );
00257               }
00258        }
00259 
00260        exit( EXIT_SUCCESS );
00261 }
00262 
00263 
00264 static void
00265 do_random( char *uri, char *manager, struct berval *passwd,
00266        char *sbase, int scope, char *filter, char *attr,
00267        char **srchattrs, int noattrs, int nobind,
00268        int innerloop, int maxretries, int delay, int force, int chaserefs )
00269 {
00270        LDAP   *ld = NULL;
00271        int    i = 0, do_retry = maxretries;
00272        char   *attrs[ 2 ];
00273        int     rc = LDAP_SUCCESS;
00274        int    version = LDAP_VERSION3;
00275        int    nvalues = 0;
00276        char   **values = NULL;
00277        LDAPMessage *res = NULL, *e = NULL;
00278 
00279        attrs[ 0 ] = attr;
00280        attrs[ 1 ] = NULL;
00281 
00282        ldap_initialize( &ld, uri );
00283        if ( ld == NULL ) {
00284               tester_perror( "ldap_initialize", NULL );
00285               exit( EXIT_FAILURE );
00286        }
00287 
00288        (void) ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version ); 
00289        (void) ldap_set_option( ld, LDAP_OPT_REFERRALS,
00290               chaserefs ? LDAP_OPT_ON : LDAP_OPT_OFF );
00291 
00292        if ( do_retry == maxretries ) {
00293               fprintf( stderr, "PID=%ld - Search(%d): base=\"%s\", filter=\"%s\" attr=\"%s\".\n",
00294                             (long) pid, innerloop, sbase, filter, attr );
00295        }
00296 
00297        if ( nobind == 0 ) {
00298               rc = ldap_sasl_bind_s( ld, manager, LDAP_SASL_SIMPLE, passwd, NULL, NULL, NULL );
00299               if ( rc != LDAP_SUCCESS ) {
00300                      tester_ldap_error( ld, "ldap_sasl_bind_s", NULL );
00301                      switch ( rc ) {
00302                      case LDAP_BUSY:
00303                      case LDAP_UNAVAILABLE:
00304                      /* fallthru */
00305                      default:
00306                             break;
00307                      }
00308                      exit( EXIT_FAILURE );
00309               }
00310        }
00311 
00312        rc = ldap_search_ext_s( ld, sbase, LDAP_SCOPE_SUBTREE,
00313               filter, attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &res );
00314        switch ( rc ) {
00315        case LDAP_SIZELIMIT_EXCEEDED:
00316        case LDAP_TIMELIMIT_EXCEEDED:
00317        case LDAP_SUCCESS:
00318               if ( ldap_count_entries( ld, res ) == 0 ) {
00319                      if ( rc ) {
00320                             tester_ldap_error( ld, "ldap_search_ext_s", NULL );
00321                      }
00322                      break;
00323               }
00324 
00325               for ( e = ldap_first_entry( ld, res ); e != NULL; e = ldap_next_entry( ld, e ) )
00326               {
00327                      struct berval **v = ldap_get_values_len( ld, e, attr );
00328 
00329                      if ( v != NULL ) {
00330                             int n = ldap_count_values_len( v );
00331                             int j;
00332 
00333                             values = realloc( values, ( nvalues + n + 1 )*sizeof( char * ) );
00334                             for ( j = 0; j < n; j++ ) {
00335                                    values[ nvalues + j ] = strdup( v[ j ]->bv_val );
00336                             }
00337                             values[ nvalues + j ] = NULL;
00338                             nvalues += n;
00339                             ldap_value_free_len( v );
00340                      }
00341               }
00342 
00343               ldap_msgfree( res );
00344 
00345               if ( !values ) {
00346                      fprintf( stderr, "  PID=%ld - Search base=\"%s\" filter=\"%s\" got %d values.\n",
00347                             (long) pid, sbase, filter, nvalues );
00348                      exit(EXIT_FAILURE);
00349               }
00350 
00351               if ( do_retry == maxretries ) {
00352                      fprintf( stderr, "  PID=%ld - Search base=\"%s\" filter=\"%s\" got %d values.\n",
00353                             (long) pid, sbase, filter, nvalues );
00354               }
00355 
00356               for ( i = 0; i < innerloop; i++ ) {
00357                      char   buf[ BUFSIZ ];
00358 #if 0  /* use high-order bits for better randomness (Numerical Recipes in "C") */
00359                      int    r = rand() % nvalues;
00360 #endif
00361                      int    r = ((double)nvalues)*rand()/(RAND_MAX + 1.0);
00362 
00363                      snprintf( buf, sizeof( buf ), "(%s=%s)", attr, values[ r ] );
00364 
00365                      do_search( uri, manager, passwd,
00366                             sbase, scope, buf, &ld,
00367                             srchattrs, noattrs, nobind,
00368                             1, maxretries, delay, force, chaserefs );
00369               }
00370               break;
00371 
00372        default:
00373               tester_ldap_error( ld, "ldap_search_ext_s", NULL );
00374               break;
00375        }
00376 
00377        fprintf( stderr, "  PID=%ld - Search done (%d).\n", (long) pid, rc );
00378 
00379        if ( ld != NULL ) {
00380               ldap_unbind_ext( ld, NULL, NULL );
00381        }
00382 }
00383 
00384 static void
00385 do_search( char *uri, char *manager, struct berval *passwd,
00386        char *sbase, int scope, char *filter, LDAP **ldp,
00387        char **attrs, int noattrs, int nobind,
00388        int innerloop, int maxretries, int delay, int force, int chaserefs )
00389 {
00390        LDAP   *ld = ldp ? *ldp : NULL;
00391        int    i = 0, do_retry = maxretries;
00392        int     rc = LDAP_SUCCESS;
00393        int    version = LDAP_VERSION3;
00394        char   buf[ BUFSIZ ];
00395        int           *msgids = NULL, active = 0;
00396 
00397        /* make room for msgid */
00398        if ( swamp > 1 ) {
00399               msgids = (int *)calloc( sizeof(int), innerloop );
00400        }
00401 
00402 retry:;
00403        if ( ld == NULL ) {
00404               ldap_initialize( &ld, uri );
00405               if ( ld == NULL ) {
00406                      tester_perror( "ldap_initialize", NULL );
00407                      exit( EXIT_FAILURE );
00408               }
00409 
00410               (void) ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version ); 
00411               (void) ldap_set_option( ld, LDAP_OPT_REFERRALS,
00412                      chaserefs ? LDAP_OPT_ON : LDAP_OPT_OFF );
00413 
00414               if ( do_retry == maxretries ) {
00415                      fprintf( stderr,
00416                             "PID=%ld - Search(%d): "
00417                             "base=\"%s\" scope=%s filter=\"%s\" "
00418                             "attrs=%s%s.\n",
00419                             (long) pid, innerloop,
00420                             sbase, ldap_pvt_scope2str( scope ), filter,
00421                             attrs[0], attrs[1] ? " (more...)" : "" );
00422               }
00423 
00424               if ( nobind == 0 ) {
00425                      rc = ldap_sasl_bind_s( ld, manager, LDAP_SASL_SIMPLE, passwd, NULL, NULL, NULL );
00426                      if ( rc != LDAP_SUCCESS ) {
00427                             snprintf( buf, sizeof( buf ),
00428                                    "bindDN=\"%s\"", manager );
00429                             tester_ldap_error( ld, "ldap_sasl_bind_s", buf );
00430                             switch ( rc ) {
00431                             case LDAP_BUSY:
00432                             case LDAP_UNAVAILABLE:
00433                                    if ( do_retry > 0 ) {
00434                                           ldap_unbind_ext( ld, NULL, NULL );
00435                                           ld = NULL;
00436                                           do_retry--;
00437                                           if ( delay != 0 ) {
00438                                               sleep( delay );
00439                                           }
00440                                           goto retry;
00441                                    }
00442                             /* fallthru */
00443                             default:
00444                                    break;
00445                             }
00446                             exit( EXIT_FAILURE );
00447                      }
00448               }
00449        }
00450 
00451        if ( swamp > 1 ) {
00452               do {
00453                      LDAPMessage *res = NULL;
00454                      int j, msgid;
00455 
00456                      if ( i < innerloop ) {
00457                             rc = ldap_search_ext( ld, sbase, scope,
00458                                           filter, NULL, noattrs, NULL, NULL,
00459                                           NULL, LDAP_NO_LIMIT, &msgids[i] );
00460 
00461                             active++;
00462 #if 0
00463                             fprintf( stderr,
00464                                    ">>> PID=%ld - Search maxloop=%d cnt=%d active=%d msgid=%d: "
00465                                    "base=\"%s\" scope=%s filter=\"%s\"\n",
00466                                    (long) pid, innerloop, i, active, msgids[i],
00467                                    sbase, ldap_pvt_scope2str( scope ), filter );
00468 #endif
00469                             i++;
00470 
00471                             if ( rc ) {
00472                                    int first = tester_ignore_err( rc );
00473                                    /* if ignore.. */
00474                                    if ( first ) {
00475                                           /* only log if first occurrence */
00476                                           if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) {
00477                                                  tester_ldap_error( ld, "ldap_search_ext", NULL );
00478                                           }
00479                                           continue;
00480                                    }
00481               
00482                                    /* busy needs special handling */
00483                                    snprintf( buf, sizeof( buf ),
00484                                           "base=\"%s\" filter=\"%s\"\n",
00485                                           sbase, filter );
00486                                    tester_ldap_error( ld, "ldap_search_ext", buf );
00487                                    if ( rc == LDAP_BUSY && do_retry > 0 ) {
00488                                           ldap_unbind_ext( ld, NULL, NULL );
00489                                           ld = NULL;
00490                                           do_retry--;
00491                                           goto retry;
00492                                    }
00493                                    break;
00494                             }
00495 
00496                             if ( swamp > 2 ) {
00497                                    continue;
00498                             }
00499                      }
00500 
00501                      rc = ldap_result( ld, LDAP_RES_ANY, 0, NULL, &res );
00502                      switch ( rc ) {
00503                      case -1:
00504                             /* gone really bad */
00505                             goto cleanup;
00506        
00507                      case 0:
00508                             /* timeout (impossible) */
00509                             break;
00510        
00511                      case LDAP_RES_SEARCH_ENTRY:
00512                      case LDAP_RES_SEARCH_REFERENCE:
00513                             /* ignore */
00514                             break;
00515        
00516                      case LDAP_RES_SEARCH_RESULT:
00517                             /* just remove, no error checking (TODO?) */
00518                             msgid = ldap_msgid( res );
00519                             ldap_parse_result( ld, res, &rc, NULL, NULL, NULL, NULL, 1 );
00520                             res = NULL;
00521 
00522                             /* linear search, bah */
00523                             for ( j = 0; j < i; j++ ) {
00524                                    if ( msgids[ j ] == msgid ) {
00525                                           msgids[ j ] = -1;
00526                                           active--;
00527 #if 0
00528                                           fprintf( stderr,
00529                                                  "<<< PID=%ld - SearchDone maxloop=%d cnt=%d active=%d msgid=%d: "
00530                                                  "base=\"%s\" scope=%s filter=\"%s\"\n",
00531                                                  (long) pid, innerloop, j, active, msgid,
00532                                                  sbase, ldap_pvt_scope2str( scope ), filter );
00533 #endif
00534                                           break;
00535                                    }
00536                             }
00537                             break;
00538 
00539                      default:
00540                             /* other messages unexpected */
00541                             fprintf( stderr,
00542                                    "### PID=%ld - Search(%d): "
00543                                    "base=\"%s\" scope=%s filter=\"%s\" "
00544                                    "attrs=%s%s. unexpected response tag=%d\n",
00545                                    (long) pid, innerloop,
00546                                    sbase, ldap_pvt_scope2str( scope ), filter,
00547                                    attrs[0], attrs[1] ? " (more...)" : "", rc );
00548                             break;
00549                      }
00550 
00551                      if ( res != NULL ) {
00552                             ldap_msgfree( res );
00553                      }
00554               } while ( i < innerloop || active > 0 );
00555 
00556        } else {
00557               for ( ; i < innerloop; i++ ) {
00558                      LDAPMessage *res = NULL;
00559 
00560                      if (swamp) {
00561                             int msgid;
00562                             rc = ldap_search_ext( ld, sbase, scope,
00563                                           filter, NULL, noattrs, NULL, NULL,
00564                                           NULL, LDAP_NO_LIMIT, &msgid );
00565                             if ( rc == LDAP_SUCCESS ) continue;
00566                             else break;
00567                      }
00568        
00569                      rc = ldap_search_ext_s( ld, sbase, scope,
00570                                    filter, attrs, noattrs, NULL, NULL,
00571                                    NULL, LDAP_NO_LIMIT, &res );
00572                      if ( res != NULL ) {
00573                             ldap_msgfree( res );
00574                      }
00575        
00576                      if ( rc ) {
00577                             int first = tester_ignore_err( rc );
00578                             /* if ignore.. */
00579                             if ( first ) {
00580                                    /* only log if first occurrence */
00581                                    if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) {
00582                                           tester_ldap_error( ld, "ldap_search_ext_s", NULL );
00583                                    }
00584                                    continue;
00585                             }
00586        
00587                             /* busy needs special handling */
00588                             snprintf( buf, sizeof( buf ),
00589                                    "base=\"%s\" filter=\"%s\"\n",
00590                                    sbase, filter );
00591                             tester_ldap_error( ld, "ldap_search_ext_s", buf );
00592                             if ( rc == LDAP_BUSY && do_retry > 0 ) {
00593                                    ldap_unbind_ext( ld, NULL, NULL );
00594                                    ld = NULL;
00595                                    do_retry--;
00596                                    goto retry;
00597                             }
00598                             break;
00599                      }
00600               }
00601        }
00602 
00603 cleanup:;
00604        if ( msgids != NULL ) {
00605               free( msgids );
00606        }
00607 
00608        if ( ldp != NULL ) {
00609               *ldp = ld;
00610 
00611        } else {
00612               fprintf( stderr, "  PID=%ld - Search done (%d).\n", (long) pid, rc );
00613 
00614               if ( ld != NULL ) {
00615                      ldap_unbind_ext( ld, NULL, NULL );
00616               }
00617        }
00618 }