Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/libs/ldap/libldap/result.c
4394 views
1
/* result.c - wait for an ldap result */
2
/* $OpenLDAP$ */
3
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4
*
5
* Copyright 1998-2024 The OpenLDAP Foundation.
6
* All rights reserved.
7
*
8
* Redistribution and use in source and binary forms, with or without
9
* modification, are permitted only as authorized by the OpenLDAP
10
* Public License.
11
*
12
* A copy of this license is available in the file LICENSE in the
13
* top-level directory of the distribution or, alternatively, at
14
* <http://www.OpenLDAP.org/license.html>.
15
*/
16
/* Portions Copyright (c) 1990 Regents of the University of Michigan.
17
* All rights reserved.
18
*/
19
/* This notice applies to changes, created by or for Novell, Inc.,
20
* to preexisting works for which notices appear elsewhere in this file.
21
*
22
* Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
23
*
24
* THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
25
* USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION
26
* 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT
27
* HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE
28
* TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS
29
* WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC
30
* LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE
31
* PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
32
*---
33
* Modification to OpenLDAP source by Novell, Inc.
34
* April 2000 sfs Add code to process V3 referrals and search results
35
*---
36
* Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License
37
* can be found in the file "build/LICENSE-2.0.1" in this distribution
38
* of OpenLDAP Software.
39
*/
40
41
/*
42
* LDAPv3 (RFC 4511)
43
* LDAPResult ::= SEQUENCE {
44
* resultCode ENUMERATED { ... },
45
* matchedDN LDAPDN,
46
* diagnosticMessage LDAPString,
47
* referral [3] Referral OPTIONAL
48
* }
49
* Referral ::= SEQUENCE OF LDAPURL (one or more)
50
* LDAPURL ::= LDAPString (limited to URL chars)
51
*/
52
53
#include "portable.h"
54
55
#include <stdio.h>
56
57
#include <ac/stdlib.h>
58
59
#include <ac/errno.h>
60
#include <ac/socket.h>
61
#include <ac/string.h>
62
#include <ac/time.h>
63
#include <ac/unistd.h>
64
65
#include "ldap-int.h"
66
#include "ldap_log.h"
67
#include "lutil.h"
68
69
static int ldap_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid ));
70
static int ldap_mark_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid ));
71
static int wait4msg LDAP_P(( LDAP *ld, ber_int_t msgid, int all, struct timeval *timeout,
72
LDAPMessage **result ));
73
static ber_tag_t try_read1msg LDAP_P(( LDAP *ld, ber_int_t msgid,
74
int all, LDAPConn *lc, LDAPMessage **result ));
75
static ber_tag_t build_result_ber LDAP_P(( LDAP *ld, BerElement **bp, LDAPRequest *lr ));
76
static void merge_error_info LDAP_P(( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr ));
77
static LDAPMessage * chkResponseList LDAP_P(( LDAP *ld, int msgid, int all));
78
79
#define LDAP_MSG_X_KEEP_LOOKING (-2)
80
81
82
/*
83
* ldap_result - wait for an ldap result response to a message from the
84
* ldap server. If msgid is LDAP_RES_ANY (-1), any message will be
85
* accepted. If msgid is LDAP_RES_UNSOLICITED (0), any unsolicited
86
* message is accepted. Otherwise ldap_result will wait for a response
87
* with msgid. If all is LDAP_MSG_ONE (0) the first message with id
88
* msgid will be accepted, otherwise, ldap_result will wait for all
89
* responses with id msgid and then return a pointer to the entire list
90
* of messages. In general, this is only useful for search responses,
91
* which can be of three message types (zero or more entries, zero or
92
* search references, followed by an ldap result). An extension to
93
* LDAPv3 allows partial extended responses to be returned in response
94
* to any request. The type of the first message received is returned.
95
* When waiting, any messages that have been abandoned/discarded are
96
* discarded.
97
*
98
* Example:
99
* ldap_result( s, msgid, all, timeout, result )
100
*/
101
int
102
ldap_result(
103
LDAP *ld,
104
int msgid,
105
int all,
106
struct timeval *timeout,
107
LDAPMessage **result )
108
{
109
int rc;
110
111
assert( ld != NULL );
112
assert( result != NULL );
113
114
Debug2( LDAP_DEBUG_TRACE, "ldap_result ld %p msgid %d\n", (void *)ld, msgid );
115
116
if (ld->ld_errno == LDAP_LOCAL_ERROR || ld->ld_errno == LDAP_SERVER_DOWN)
117
return -1;
118
119
LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
120
rc = wait4msg( ld, msgid, all, timeout, result );
121
LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
122
123
return rc;
124
}
125
126
/* protected by res_mutex */
127
static LDAPMessage *
128
chkResponseList(
129
LDAP *ld,
130
int msgid,
131
int all)
132
{
133
LDAPMessage *lm, **lastlm, *nextlm;
134
int cnt = 0;
135
136
/*
137
* Look through the list of responses we have received on
138
* this association and see if the response we're interested in
139
* is there. If it is, return it. If not, call wait4msg() to
140
* wait until it arrives or timeout occurs.
141
*/
142
143
LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
144
145
Debug3( LDAP_DEBUG_TRACE,
146
"ldap_chkResponseList ld %p msgid %d all %d\n",
147
(void *)ld, msgid, all );
148
149
lastlm = &ld->ld_responses;
150
for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) {
151
nextlm = lm->lm_next;
152
++cnt;
153
154
if ( ldap_abandoned( ld, lm->lm_msgid ) ) {
155
Debug2( LDAP_DEBUG_ANY,
156
"response list msg abandoned, "
157
"msgid %d message type %s\n",
158
lm->lm_msgid, ldap_int_msgtype2str( lm->lm_msgtype ) );
159
160
switch ( lm->lm_msgtype ) {
161
case LDAP_RES_SEARCH_ENTRY:
162
case LDAP_RES_SEARCH_REFERENCE:
163
case LDAP_RES_INTERMEDIATE:
164
break;
165
166
default:
167
/* there's no need to keep the id
168
* in the abandoned list any longer */
169
ldap_mark_abandoned( ld, lm->lm_msgid );
170
break;
171
}
172
173
/* Remove this entry from list */
174
*lastlm = nextlm;
175
176
ldap_msgfree( lm );
177
178
continue;
179
}
180
181
if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) {
182
LDAPMessage *tmp;
183
184
if ( all == LDAP_MSG_ONE ||
185
all == LDAP_MSG_RECEIVED ||
186
msgid == LDAP_RES_UNSOLICITED )
187
{
188
break;
189
}
190
191
tmp = lm->lm_chain_tail;
192
if ( tmp->lm_msgtype == LDAP_RES_SEARCH_ENTRY ||
193
tmp->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ||
194
tmp->lm_msgtype == LDAP_RES_INTERMEDIATE )
195
{
196
tmp = NULL;
197
}
198
199
if ( tmp == NULL ) {
200
lm = NULL;
201
}
202
203
break;
204
}
205
lastlm = &lm->lm_next;
206
}
207
208
if ( lm != NULL ) {
209
/* Found an entry, remove it from the list */
210
if ( all == LDAP_MSG_ONE && lm->lm_chain != NULL ) {
211
*lastlm = lm->lm_chain;
212
lm->lm_chain->lm_next = lm->lm_next;
213
lm->lm_chain->lm_chain_tail = ( lm->lm_chain_tail != lm ) ? lm->lm_chain_tail : lm->lm_chain;
214
lm->lm_chain = NULL;
215
lm->lm_chain_tail = NULL;
216
} else {
217
*lastlm = lm->lm_next;
218
}
219
lm->lm_next = NULL;
220
}
221
222
#ifdef LDAP_DEBUG
223
if ( lm == NULL) {
224
Debug1( LDAP_DEBUG_TRACE,
225
"ldap_chkResponseList returns ld %p NULL\n", (void *)ld );
226
} else {
227
Debug3( LDAP_DEBUG_TRACE,
228
"ldap_chkResponseList returns ld %p msgid %d, type 0x%02lx\n",
229
(void *)ld, lm->lm_msgid, (unsigned long)lm->lm_msgtype );
230
}
231
#endif
232
233
return lm;
234
}
235
236
/* protected by res_mutex */
237
static int
238
wait4msg(
239
LDAP *ld,
240
ber_int_t msgid,
241
int all,
242
struct timeval *timeout,
243
LDAPMessage **result )
244
{
245
int rc;
246
struct timeval tv = { 0 },
247
tv0 = { 0 },
248
start_time_tv = { 0 },
249
*tvp = NULL;
250
LDAPConn *lc;
251
252
assert( ld != NULL );
253
assert( result != NULL );
254
255
LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
256
257
if ( timeout == NULL && ld->ld_options.ldo_tm_api.tv_sec >= 0 ) {
258
tv = ld->ld_options.ldo_tm_api;
259
timeout = &tv;
260
}
261
262
#ifdef LDAP_DEBUG
263
if ( timeout == NULL ) {
264
Debug2( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (infinite timeout)\n",
265
(void *)ld, msgid );
266
} else {
267
Debug3( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (timeout %ld usec)\n",
268
(void *)ld, msgid, (long)timeout->tv_sec * 1000000 + timeout->tv_usec );
269
}
270
#endif /* LDAP_DEBUG */
271
272
if ( timeout != NULL && timeout->tv_sec != -1 ) {
273
tv0 = *timeout;
274
tv = *timeout;
275
tvp = &tv;
276
#ifdef HAVE_GETTIMEOFDAY
277
gettimeofday( &start_time_tv, NULL );
278
#else /* ! HAVE_GETTIMEOFDAY */
279
start_time_tv.tv_sec = time( NULL );
280
start_time_tv.tv_usec = 0;
281
#endif /* ! HAVE_GETTIMEOFDAY */
282
}
283
284
rc = LDAP_MSG_X_KEEP_LOOKING;
285
while ( rc == LDAP_MSG_X_KEEP_LOOKING ) {
286
#ifdef LDAP_DEBUG
287
if ( ldap_debug & LDAP_DEBUG_TRACE ) {
288
Debug3( LDAP_DEBUG_TRACE, "wait4msg continue ld %p msgid %d all %d\n",
289
(void *)ld, msgid, all );
290
ldap_dump_connection( ld, ld->ld_conns, 1 );
291
LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
292
ldap_dump_requests_and_responses( ld );
293
LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
294
}
295
#endif /* LDAP_DEBUG */
296
297
if ( ( *result = chkResponseList( ld, msgid, all ) ) != NULL ) {
298
rc = (*result)->lm_msgtype;
299
300
} else {
301
int lc_ready = 0;
302
303
LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
304
for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
305
if ( ber_sockbuf_ctrl( lc->lconn_sb,
306
LBER_SB_OPT_DATA_READY, NULL ) )
307
{
308
lc_ready = 2; /* ready at ber level, not socket level */
309
break;
310
}
311
}
312
313
if ( !lc_ready ) {
314
int err;
315
rc = ldap_int_select( ld, tvp );
316
if ( rc == -1 ) {
317
err = sock_errno();
318
#ifdef LDAP_DEBUG
319
Debug1( LDAP_DEBUG_TRACE,
320
"ldap_int_select returned -1: errno %d\n",
321
err );
322
#endif
323
}
324
325
if ( rc == 0 || ( rc == -1 && (
326
!LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART)
327
|| err != EINTR ) ) )
328
{
329
ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
330
LDAP_TIMEOUT);
331
LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
332
return( rc );
333
}
334
335
if ( rc == -1 ) {
336
rc = LDAP_MSG_X_KEEP_LOOKING; /* select interrupted: loop */
337
338
} else {
339
lc_ready = 1;
340
}
341
}
342
if ( lc_ready ) {
343
LDAPConn *lnext;
344
int serviced = 0;
345
rc = LDAP_MSG_X_KEEP_LOOKING;
346
LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
347
if ( ld->ld_requests != NULL ) {
348
TAvlnode *node = ldap_tavl_end( ld->ld_requests, TAVL_DIR_RIGHT );
349
LDAPRequest *lr;
350
351
assert( node != NULL );
352
lr = node->avl_data;
353
if ( lr->lr_status == LDAP_REQST_WRITING &&
354
ldap_is_write_ready( ld, lr->lr_conn->lconn_sb ) ) {
355
serviced = 1;
356
ldap_int_flush_request( ld, lr );
357
}
358
}
359
for ( lc = ld->ld_conns;
360
rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL;
361
lc = lnext )
362
{
363
if ( lc->lconn_status == LDAP_CONNST_CONNECTED &&
364
ldap_is_read_ready( ld, lc->lconn_sb ) )
365
{
366
serviced = 1;
367
/* Don't let it get freed out from under us */
368
++lc->lconn_refcnt;
369
rc = try_read1msg( ld, msgid, all, lc, result );
370
lnext = lc->lconn_next;
371
372
/* Only take locks if we're really freeing */
373
if ( lc->lconn_refcnt <= 1 ) {
374
ldap_free_connection( ld, lc, 0, 1 );
375
} else {
376
--lc->lconn_refcnt;
377
}
378
} else {
379
lnext = lc->lconn_next;
380
}
381
}
382
LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
383
/* Quit looping if no one handled any socket events */
384
if (!serviced && lc_ready == 1)
385
rc = -1;
386
}
387
LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
388
}
389
390
if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) {
391
struct timeval curr_time_tv = { 0 },
392
delta_time_tv = { 0 };
393
394
#ifdef HAVE_GETTIMEOFDAY
395
gettimeofday( &curr_time_tv, NULL );
396
#else /* ! HAVE_GETTIMEOFDAY */
397
curr_time_tv.tv_sec = time( NULL );
398
curr_time_tv.tv_usec = 0;
399
#endif /* ! HAVE_GETTIMEOFDAY */
400
401
/* delta_time = tmp_time - start_time */
402
delta_time_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec;
403
delta_time_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec;
404
if ( delta_time_tv.tv_usec < 0 ) {
405
delta_time_tv.tv_sec--;
406
delta_time_tv.tv_usec += 1000000;
407
}
408
409
/* tv0 < delta_time ? */
410
if ( ( tv0.tv_sec < delta_time_tv.tv_sec ) ||
411
( ( tv0.tv_sec == delta_time_tv.tv_sec ) && ( tv0.tv_usec < delta_time_tv.tv_usec ) ) )
412
{
413
rc = 0; /* timed out */
414
ld->ld_errno = LDAP_TIMEOUT;
415
break;
416
}
417
418
/* tv0 -= delta_time */
419
tv0.tv_sec -= delta_time_tv.tv_sec;
420
tv0.tv_usec -= delta_time_tv.tv_usec;
421
if ( tv0.tv_usec < 0 ) {
422
tv0.tv_sec--;
423
tv0.tv_usec += 1000000;
424
}
425
426
tv.tv_sec = tv0.tv_sec;
427
tv.tv_usec = tv0.tv_usec;
428
429
Debug3( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld s %ld us to go\n",
430
(void *)ld, (long) tv.tv_sec, (long) tv.tv_usec );
431
432
start_time_tv.tv_sec = curr_time_tv.tv_sec;
433
start_time_tv.tv_usec = curr_time_tv.tv_usec;
434
}
435
}
436
437
return( rc );
438
}
439
440
441
/* protected by res_mutex, conn_mutex and req_mutex */
442
static ber_tag_t
443
try_read1msg(
444
LDAP *ld,
445
ber_int_t msgid,
446
int all,
447
LDAPConn *lc,
448
LDAPMessage **result )
449
{
450
BerElement *ber;
451
LDAPMessage *newmsg, *l, *prev;
452
ber_int_t id;
453
ber_tag_t tag;
454
ber_len_t len;
455
int foundit = 0;
456
LDAPRequest *lr, *tmplr, dummy_lr = { 0 };
457
BerElement tmpber;
458
int rc, refer_cnt, hadref, simple_request, err;
459
ber_int_t lderr = -1;
460
461
#ifdef LDAP_CONNECTIONLESS
462
LDAPMessage *tmp = NULL, *chain_head = NULL;
463
int moremsgs = 0, isv2 = 0;
464
#endif
465
466
assert( ld != NULL );
467
assert( lc != NULL );
468
469
LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
470
LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
471
LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
472
473
Debug3( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n",
474
(void *)ld, msgid, all );
475
476
retry:
477
if ( lc->lconn_ber == NULL ) {
478
lc->lconn_ber = ldap_alloc_ber_with_options( ld );
479
480
if ( lc->lconn_ber == NULL ) {
481
return -1;
482
}
483
}
484
485
ber = lc->lconn_ber;
486
assert( LBER_VALID (ber) );
487
488
/* get the next message */
489
sock_errset(0);
490
#ifdef LDAP_CONNECTIONLESS
491
if ( LDAP_IS_UDP(ld) ) {
492
struct sockaddr_storage from;
493
if ( ber_int_sb_read( lc->lconn_sb, &from, sizeof(struct sockaddr_storage) ) < 0 )
494
goto fail;
495
if ( ld->ld_options.ldo_version == LDAP_VERSION2 ) isv2 = 1;
496
}
497
nextresp3:
498
#endif
499
tag = ber_get_next( lc->lconn_sb, &len, ber );
500
switch ( tag ) {
501
case LDAP_TAG_MESSAGE:
502
/*
503
* We read a complete message.
504
* The connection should no longer need this ber.
505
*/
506
lc->lconn_ber = NULL;
507
break;
508
509
default:
510
/*
511
* We read a BerElement that isn't LDAP or the stream has desync'd.
512
* In either case, anything we read from now on is probably garbage,
513
* just drop the connection.
514
*/
515
ber_free( ber, 1 );
516
lc->lconn_ber = NULL;
517
/* FALLTHRU */
518
519
case LBER_DEFAULT:
520
fail:
521
err = sock_errno();
522
#ifdef LDAP_DEBUG
523
Debug1( LDAP_DEBUG_CONNS,
524
"ber_get_next failed, errno=%d.\n", err );
525
#endif
526
if ( err == EWOULDBLOCK ) return LDAP_MSG_X_KEEP_LOOKING;
527
if ( err == EAGAIN ) return LDAP_MSG_X_KEEP_LOOKING;
528
ld->ld_errno = LDAP_SERVER_DOWN;
529
if ( !LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_KEEPCONN )) {
530
--lc->lconn_refcnt;
531
}
532
lc->lconn_status = 0;
533
return -1;
534
}
535
536
/* message id */
537
if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
538
ber_free( ber, 1 );
539
ld->ld_errno = LDAP_DECODING_ERROR;
540
return( -1 );
541
}
542
543
/* id == 0 iff unsolicited notification message (RFC 4511) */
544
545
/* id < 0 is invalid, just toss it. FIXME: should we disconnect? */
546
if ( id < 0 ) {
547
goto retry_ber;
548
}
549
550
/* if it's been abandoned, toss it */
551
if ( id > 0 ) {
552
if ( ldap_abandoned( ld, id ) ) {
553
/* the message type */
554
tag = ber_peek_tag( ber, &len );
555
switch ( tag ) {
556
case LDAP_RES_SEARCH_ENTRY:
557
case LDAP_RES_SEARCH_REFERENCE:
558
case LDAP_RES_INTERMEDIATE:
559
case LBER_ERROR:
560
break;
561
562
default:
563
/* there's no need to keep the id
564
* in the abandoned list any longer */
565
ldap_mark_abandoned( ld, id );
566
break;
567
}
568
569
Debug3( LDAP_DEBUG_ANY,
570
"abandoned/discarded ld %p msgid %d message type %s\n",
571
(void *)ld, id, ldap_int_msgtype2str( tag ) );
572
573
retry_ber:
574
ber_free( ber, 1 );
575
if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
576
goto retry;
577
}
578
return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
579
}
580
581
lr = ldap_find_request_by_msgid( ld, id );
582
if ( lr == NULL ) {
583
const char *msg = "unknown";
584
585
/* the message type */
586
tag = ber_peek_tag( ber, &len );
587
switch ( tag ) {
588
case LBER_ERROR:
589
break;
590
591
default:
592
msg = ldap_int_msgtype2str( tag );
593
break;
594
}
595
596
Debug3( LDAP_DEBUG_ANY,
597
"no request for response on ld %p msgid %d message type %s (tossing)\n",
598
(void *)ld, id, msg );
599
600
goto retry_ber;
601
}
602
603
#ifdef LDAP_CONNECTIONLESS
604
if ( LDAP_IS_UDP(ld) && isv2 ) {
605
ber_scanf(ber, "x{");
606
}
607
nextresp2:
608
;
609
#endif
610
}
611
612
/* the message type */
613
tag = ber_peek_tag( ber, &len );
614
if ( tag == LBER_ERROR ) {
615
ld->ld_errno = LDAP_DECODING_ERROR;
616
ber_free( ber, 1 );
617
return( -1 );
618
}
619
620
Debug3( LDAP_DEBUG_TRACE,
621
"read1msg: ld %p msgid %d message type %s\n",
622
(void *)ld, id, ldap_int_msgtype2str( tag ) );
623
624
if ( id == 0 ) {
625
/* unsolicited notification message (RFC 4511) */
626
if ( tag != LDAP_RES_EXTENDED ) {
627
/* toss it */
628
goto retry_ber;
629
630
/* strictly speaking, it's an error; from RFC 4511:
631
632
4.4. Unsolicited Notification
633
634
An unsolicited notification is an LDAPMessage sent from the server to
635
the client that is not in response to any LDAPMessage received by the
636
server. It is used to signal an extraordinary condition in the
637
server or in the LDAP session between the client and the server. The
638
notification is of an advisory nature, and the server will not expect
639
any response to be returned from the client.
640
641
The unsolicited notification is structured as an LDAPMessage in which
642
the messageID is zero and protocolOp is set to the extendedResp
643
choice using the ExtendedResponse type (See Section 4.12). The
644
responseName field of the ExtendedResponse always contains an LDAPOID
645
that is unique for this notification.
646
647
* however, since unsolicited responses
648
* are of advisory nature, better
649
* toss it, right now
650
*/
651
652
#if 0
653
ld->ld_errno = LDAP_DECODING_ERROR;
654
ber_free( ber, 1 );
655
return( -1 );
656
#endif
657
}
658
659
lr = &dummy_lr;
660
}
661
662
id = lr->lr_origid;
663
refer_cnt = 0;
664
hadref = simple_request = 0;
665
rc = LDAP_MSG_X_KEEP_LOOKING; /* default is to keep looking (no response found) */
666
lr->lr_res_msgtype = tag;
667
668
/*
669
* Check for V3 search reference
670
*/
671
if ( tag == LDAP_RES_SEARCH_REFERENCE ) {
672
if ( ld->ld_version > LDAP_VERSION2 ) {
673
/* This is a V3 search reference */
674
if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
675
lr->lr_parent != NULL )
676
{
677
char **refs = NULL;
678
tmpber = *ber;
679
680
/* Get the referral list */
681
if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) {
682
rc = LDAP_DECODING_ERROR;
683
684
} else {
685
/* Note: refs array is freed by ldap_chase_v3referrals */
686
refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
687
1, &lr->lr_res_error, &hadref );
688
if ( refer_cnt > 0 ) {
689
/* successfully chased reference */
690
/* If haven't got end search, set chasing referrals */
691
if ( lr->lr_status != LDAP_REQST_COMPLETED ) {
692
lr->lr_status = LDAP_REQST_CHASINGREFS;
693
Debug1( LDAP_DEBUG_TRACE,
694
"read1msg: search ref chased, "
695
"mark request chasing refs, "
696
"id = %d\n",
697
lr->lr_msgid );
698
}
699
}
700
}
701
}
702
}
703
704
} else if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) {
705
/* All results that just return a status, i.e. don't return data
706
* go through the following code. This code also chases V2 referrals
707
* and checks if all referrals have been chased.
708
*/
709
char *lr_res_error = NULL;
710
711
tmpber = *ber; /* struct copy */
712
if ( ber_scanf( &tmpber, "{eAA", &lderr,
713
&lr->lr_res_matched, &lr_res_error )
714
!= LBER_ERROR )
715
{
716
if ( lr_res_error != NULL ) {
717
if ( lr->lr_res_error != NULL ) {
718
(void)ldap_append_referral( ld, &lr->lr_res_error, lr_res_error );
719
LDAP_FREE( (char *)lr_res_error );
720
721
} else {
722
lr->lr_res_error = lr_res_error;
723
}
724
lr_res_error = NULL;
725
}
726
727
/* Do we need to check for referrals? */
728
if ( tag != LDAP_RES_BIND &&
729
( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
730
lr->lr_parent != NULL ))
731
{
732
char **refs = NULL;
733
ber_len_t len;
734
735
/* Check if V3 referral */
736
if ( ber_peek_tag( &tmpber, &len ) == LDAP_TAG_REFERRAL ) {
737
if ( ld->ld_version > LDAP_VERSION2 ) {
738
/* Get the referral list */
739
if ( ber_scanf( &tmpber, "{v}", &refs) == LBER_ERROR) {
740
rc = LDAP_DECODING_ERROR;
741
lr->lr_status = LDAP_REQST_COMPLETED;
742
Debug2( LDAP_DEBUG_TRACE,
743
"read1msg: referral decode error, "
744
"mark request completed, ld %p msgid %d\n",
745
(void *)ld, lr->lr_msgid );
746
747
} else {
748
/* Chase the referral
749
* refs array is freed by ldap_chase_v3referrals
750
*/
751
refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
752
0, &lr->lr_res_error, &hadref );
753
lr->lr_status = LDAP_REQST_COMPLETED;
754
Debug3( LDAP_DEBUG_TRACE,
755
"read1msg: referral %s chased, "
756
"mark request completed, ld %p msgid %d\n",
757
refer_cnt > 0 ? "" : "not",
758
(void *)ld, lr->lr_msgid);
759
if ( refer_cnt < 0 ) {
760
refer_cnt = 0;
761
}
762
}
763
}
764
} else {
765
switch ( lderr ) {
766
case LDAP_SUCCESS:
767
case LDAP_COMPARE_TRUE:
768
case LDAP_COMPARE_FALSE:
769
break;
770
771
default:
772
if ( lr->lr_res_error == NULL ) {
773
break;
774
}
775
776
/* pedantic, should never happen */
777
if ( lr->lr_res_error[ 0 ] == '\0' ) {
778
LDAP_FREE( lr->lr_res_error );
779
lr->lr_res_error = NULL;
780
break;
781
}
782
783
/* V2 referrals are in error string */
784
refer_cnt = ldap_chase_referrals( ld, lr,
785
&lr->lr_res_error, -1, &hadref );
786
lr->lr_status = LDAP_REQST_COMPLETED;
787
Debug1( LDAP_DEBUG_TRACE,
788
"read1msg: V2 referral chased, "
789
"mark request completed, id = %d\n",
790
lr->lr_msgid );
791
break;
792
}
793
}
794
}
795
796
/* save errno, message, and matched string */
797
if ( !hadref || lr->lr_res_error == NULL ) {
798
lr->lr_res_errno =
799
lderr == LDAP_PARTIAL_RESULTS
800
? LDAP_SUCCESS : lderr;
801
802
} else if ( ld->ld_errno != LDAP_SUCCESS ) {
803
lr->lr_res_errno = ld->ld_errno;
804
805
} else {
806
lr->lr_res_errno = LDAP_PARTIAL_RESULTS;
807
}
808
}
809
810
/* in any case, don't leave any lr_res_error 'round */
811
if ( lr_res_error ) {
812
LDAP_FREE( lr_res_error );
813
}
814
815
Debug2( LDAP_DEBUG_TRACE,
816
"read1msg: ld %p %d new referrals\n",
817
(void *)ld, refer_cnt );
818
819
if ( refer_cnt != 0 ) { /* chasing referrals */
820
ber_free( ber, 1 );
821
ber = NULL;
822
if ( refer_cnt < 0 ) {
823
ldap_return_request( ld, lr, 0 );
824
return( -1 ); /* fatal error */
825
}
826
lr->lr_res_errno = LDAP_SUCCESS; /* successfully chased referral */
827
if ( lr->lr_res_matched ) {
828
LDAP_FREE( lr->lr_res_matched );
829
lr->lr_res_matched = NULL;
830
}
831
832
} else {
833
if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
834
/* request without any referrals */
835
simple_request = ( hadref ? 0 : 1 );
836
837
} else {
838
/* request with referrals or child request */
839
ber_free( ber, 1 );
840
ber = NULL;
841
}
842
843
lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */
844
Debug2( LDAP_DEBUG_TRACE,
845
"read1msg: mark request completed, ld %p msgid %d\n",
846
(void *)ld, lr->lr_msgid );
847
tmplr = lr;
848
while ( lr->lr_parent != NULL ) {
849
merge_error_info( ld, lr->lr_parent, lr );
850
851
lr = lr->lr_parent;
852
if ( --lr->lr_outrefcnt > 0 ) {
853
break; /* not completely done yet */
854
}
855
}
856
/* ITS#6744: Original lr was refcounted when we retrieved it,
857
* must release it now that we're working with the parent
858
*/
859
if ( tmplr->lr_parent ) {
860
ldap_return_request( ld, tmplr, 0 );
861
}
862
863
/* Check if all requests are finished, lr is now parent */
864
tmplr = lr;
865
if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) {
866
for ( tmplr = lr->lr_child;
867
tmplr != NULL;
868
tmplr = tmplr->lr_refnext )
869
{
870
if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break;
871
}
872
}
873
874
/* This is the parent request if the request has referrals */
875
if ( lr->lr_outrefcnt <= 0 &&
876
lr->lr_parent == NULL &&
877
tmplr == NULL )
878
{
879
id = lr->lr_msgid;
880
tag = lr->lr_res_msgtype;
881
Debug2( LDAP_DEBUG_TRACE, "request done: ld %p msgid %d\n",
882
(void *)ld, id );
883
Debug3( LDAP_DEBUG_TRACE,
884
"res_errno: %d, res_error: <%s>, "
885
"res_matched: <%s>\n",
886
lr->lr_res_errno,
887
lr->lr_res_error ? lr->lr_res_error : "",
888
lr->lr_res_matched ? lr->lr_res_matched : "" );
889
if ( !simple_request ) {
890
ber_free( ber, 1 );
891
ber = NULL;
892
if ( build_result_ber( ld, &ber, lr )
893
== LBER_ERROR )
894
{
895
rc = -1; /* fatal error */
896
}
897
}
898
899
if ( lr != &dummy_lr ) {
900
ldap_return_request( ld, lr, 1 );
901
}
902
lr = NULL;
903
}
904
905
/*
906
* RFC 4511 unsolicited (id == 0) responses
907
* shouldn't necessarily end the connection
908
*/
909
if ( lc != NULL && id != 0 &&
910
!LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_KEEPCONN )) {
911
--lc->lconn_refcnt;
912
lc = NULL;
913
}
914
}
915
}
916
917
if ( lr != NULL ) {
918
if ( lr != &dummy_lr ) {
919
ldap_return_request( ld, lr, 0 );
920
}
921
lr = NULL;
922
}
923
924
if ( ber == NULL ) {
925
return( rc );
926
}
927
928
/* try to handle unsolicited responses as appropriate */
929
if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
930
int is_nod = 0;
931
932
tag = ber_peek_tag( &tmpber, &len );
933
934
/* we have a res oid */
935
if ( tag == LDAP_TAG_EXOP_RES_OID ) {
936
static struct berval bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
937
struct berval resoid = BER_BVNULL;
938
939
if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
940
ld->ld_errno = LDAP_DECODING_ERROR;
941
ber_free( ber, 1 );
942
return -1;
943
}
944
945
assert( !BER_BVISEMPTY( &resoid ) );
946
947
is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;
948
949
tag = ber_peek_tag( &tmpber, &len );
950
}
951
952
#if 0 /* don't need right now */
953
/* we have res data */
954
if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
955
struct berval resdata;
956
957
if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
958
ld->ld_errno = LDAP_DECODING_ERROR;
959
ber_free( ber, 0 );
960
return ld->ld_errno;
961
}
962
963
/* use it... */
964
}
965
#endif
966
967
/* handle RFC 4511 "Notice of Disconnection" locally */
968
969
if ( is_nod ) {
970
if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
971
ld->ld_errno = LDAP_DECODING_ERROR;
972
ber_free( ber, 1 );
973
return -1;
974
}
975
976
/* get rid of the connection... */
977
if ( lc != NULL &&
978
!LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_KEEPCONN )) {
979
--lc->lconn_refcnt;
980
}
981
982
/* need to return -1, because otherwise
983
* a valid result is expected */
984
ld->ld_errno = lderr;
985
return -1;
986
}
987
}
988
989
/* make a new ldap message */
990
newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) );
991
if ( newmsg == NULL ) {
992
ld->ld_errno = LDAP_NO_MEMORY;
993
return( -1 );
994
}
995
newmsg->lm_msgid = (int)id;
996
newmsg->lm_msgtype = tag;
997
newmsg->lm_ber = ber;
998
newmsg->lm_chain_tail = newmsg;
999
1000
#ifdef LDAP_CONNECTIONLESS
1001
/* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798
1002
* the responses are all a sequence wrapped in one message. In
1003
* LDAPv3 each response is in its own message. The datagram must
1004
* end with a SearchResult. We can't just parse each response in
1005
* separate calls to try_read1msg because the header info is only
1006
* present at the beginning of the datagram, not at the beginning
1007
* of each response. So parse all the responses at once and queue
1008
* them up, then pull off the first response to return to the
1009
* caller when all parsing is complete.
1010
*/
1011
if ( LDAP_IS_UDP(ld) ) {
1012
/* If not a result, look for more */
1013
if ( tag != LDAP_RES_SEARCH_RESULT ) {
1014
int ok = 0;
1015
moremsgs = 1;
1016
if (isv2) {
1017
/* LDAPv2: dup the current ber, skip past the current
1018
* response, and see if there are any more after it.
1019
*/
1020
ber = ber_dup( ber );
1021
ber_scanf( ber, "x" );
1022
if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) {
1023
/* There's more - dup the ber buffer so they can all be
1024
* individually freed by ldap_msgfree.
1025
*/
1026
struct berval bv;
1027
ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len );
1028
bv.bv_val = LDAP_MALLOC( len );
1029
if ( bv.bv_val ) {
1030
ok = 1;
1031
ber_read( ber, bv.bv_val, len );
1032
bv.bv_len = len;
1033
ber_init2( ber, &bv, ld->ld_lberoptions );
1034
}
1035
}
1036
} else {
1037
/* LDAPv3: Just allocate a new ber. Since this is a buffered
1038
* datagram, if the sockbuf is readable we still have data
1039
* to parse.
1040
*/
1041
ber = ldap_alloc_ber_with_options( ld );
1042
if ( ber == NULL ) {
1043
ld->ld_errno = LDAP_NO_MEMORY;
1044
return -1;
1045
}
1046
1047
if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1;
1048
}
1049
/* set up response chain */
1050
if ( tmp == NULL ) {
1051
newmsg->lm_next = ld->ld_responses;
1052
ld->ld_responses = newmsg;
1053
chain_head = newmsg;
1054
} else {
1055
tmp->lm_chain = newmsg;
1056
}
1057
chain_head->lm_chain_tail = newmsg;
1058
tmp = newmsg;
1059
/* "ok" means there's more to parse */
1060
if ( ok ) {
1061
if ( isv2 ) {
1062
goto nextresp2;
1063
1064
} else {
1065
goto nextresp3;
1066
}
1067
} else {
1068
/* got to end of datagram without a SearchResult. Free
1069
* our dup'd ber, but leave any buffer alone. For v2 case,
1070
* the previous response is still using this buffer. For v3,
1071
* the new ber has no buffer to free yet.
1072
*/
1073
ber_free( ber, 0 );
1074
return -1;
1075
}
1076
} else if ( moremsgs ) {
1077
/* got search result, and we had multiple responses in 1 datagram.
1078
* stick the result onto the end of the chain, and then pull the
1079
* first response off the head of the chain.
1080
*/
1081
tmp->lm_chain = newmsg;
1082
chain_head->lm_chain_tail = newmsg;
1083
*result = chkResponseList( ld, msgid, all );
1084
ld->ld_errno = LDAP_SUCCESS;
1085
return( (*result)->lm_msgtype );
1086
}
1087
}
1088
#endif /* LDAP_CONNECTIONLESS */
1089
1090
/* is this the one we're looking for? */
1091
if ( msgid == LDAP_RES_ANY || id == msgid ) {
1092
if ( all == LDAP_MSG_ONE
1093
|| ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT
1094
&& newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY
1095
&& newmsg->lm_msgtype != LDAP_RES_INTERMEDIATE
1096
&& newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) )
1097
{
1098
*result = newmsg;
1099
ld->ld_errno = LDAP_SUCCESS;
1100
return( tag );
1101
1102
} else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
1103
foundit = 1; /* return the chain later */
1104
}
1105
}
1106
1107
/*
1108
* if not, we must add it to the list of responses. if
1109
* the msgid is already there, it must be part of an existing
1110
* search response.
1111
*/
1112
1113
prev = NULL;
1114
for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
1115
if ( l->lm_msgid == newmsg->lm_msgid ) {
1116
break;
1117
}
1118
prev = l;
1119
}
1120
1121
/* not part of an existing search response */
1122
if ( l == NULL ) {
1123
if ( foundit ) {
1124
*result = newmsg;
1125
goto exit;
1126
}
1127
1128
newmsg->lm_next = ld->ld_responses;
1129
ld->ld_responses = newmsg;
1130
goto exit;
1131
}
1132
1133
Debug3( LDAP_DEBUG_TRACE, "adding response ld %p msgid %d type %ld:\n",
1134
(void *)ld, newmsg->lm_msgid, (long) newmsg->lm_msgtype );
1135
1136
/* part of a search response - add to end of list of entries */
1137
l->lm_chain_tail->lm_chain = newmsg;
1138
l->lm_chain_tail = newmsg;
1139
1140
/* return the whole chain if that's what we were looking for */
1141
if ( foundit ) {
1142
if ( prev == NULL ) {
1143
ld->ld_responses = l->lm_next;
1144
} else {
1145
prev->lm_next = l->lm_next;
1146
}
1147
*result = l;
1148
}
1149
1150
exit:
1151
if ( foundit ) {
1152
ld->ld_errno = LDAP_SUCCESS;
1153
return( tag );
1154
}
1155
if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
1156
goto retry;
1157
}
1158
return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
1159
}
1160
1161
1162
static ber_tag_t
1163
build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr )
1164
{
1165
ber_len_t len;
1166
ber_tag_t tag;
1167
ber_int_t along;
1168
BerElement *ber;
1169
1170
*bp = NULL;
1171
ber = ldap_alloc_ber_with_options( ld );
1172
1173
if( ber == NULL ) {
1174
ld->ld_errno = LDAP_NO_MEMORY;
1175
return LBER_ERROR;
1176
}
1177
1178
if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
1179
lr->lr_res_msgtype, lr->lr_res_errno,
1180
lr->lr_res_matched ? lr->lr_res_matched : "",
1181
lr->lr_res_error ? lr->lr_res_error : "" ) == -1 )
1182
{
1183
ld->ld_errno = LDAP_ENCODING_ERROR;
1184
ber_free( ber, 1 );
1185
return( LBER_ERROR );
1186
}
1187
1188
ber_reset( ber, 1 );
1189
1190
if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
1191
ld->ld_errno = LDAP_DECODING_ERROR;
1192
ber_free( ber, 1 );
1193
return( LBER_ERROR );
1194
}
1195
1196
if ( ber_get_enum( ber, &along ) == LBER_ERROR ) {
1197
ld->ld_errno = LDAP_DECODING_ERROR;
1198
ber_free( ber, 1 );
1199
return( LBER_ERROR );
1200
}
1201
1202
tag = ber_peek_tag( ber, &len );
1203
1204
if ( tag == LBER_ERROR ) {
1205
ld->ld_errno = LDAP_DECODING_ERROR;
1206
ber_free( ber, 1 );
1207
return( LBER_ERROR );
1208
}
1209
1210
*bp = ber;
1211
return tag;
1212
}
1213
1214
1215
/*
1216
* Merge error information in "lr" with "parentr" error code and string.
1217
*/
1218
static void
1219
merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
1220
{
1221
if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
1222
parentr->lr_res_errno = lr->lr_res_errno;
1223
if ( lr->lr_res_error != NULL ) {
1224
(void)ldap_append_referral( ld, &parentr->lr_res_error,
1225
lr->lr_res_error );
1226
}
1227
1228
} else if ( lr->lr_res_errno != LDAP_SUCCESS &&
1229
parentr->lr_res_errno == LDAP_SUCCESS )
1230
{
1231
parentr->lr_res_errno = lr->lr_res_errno;
1232
if ( parentr->lr_res_error != NULL ) {
1233
LDAP_FREE( parentr->lr_res_error );
1234
}
1235
parentr->lr_res_error = lr->lr_res_error;
1236
lr->lr_res_error = NULL;
1237
if ( LDAP_NAME_ERROR( lr->lr_res_errno ) ) {
1238
if ( parentr->lr_res_matched != NULL ) {
1239
LDAP_FREE( parentr->lr_res_matched );
1240
}
1241
parentr->lr_res_matched = lr->lr_res_matched;
1242
lr->lr_res_matched = NULL;
1243
}
1244
}
1245
1246
Debug1( LDAP_DEBUG_TRACE, "merged parent (id %d) error info: ",
1247
parentr->lr_msgid );
1248
Debug3( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n",
1249
parentr->lr_res_errno,
1250
parentr->lr_res_error ? parentr->lr_res_error : "",
1251
parentr->lr_res_matched ? parentr->lr_res_matched : "" );
1252
}
1253
1254
1255
1256
int
1257
ldap_msgtype( LDAPMessage *lm )
1258
{
1259
assert( lm != NULL );
1260
return ( lm != NULL ) ? (int)lm->lm_msgtype : -1;
1261
}
1262
1263
1264
int
1265
ldap_msgid( LDAPMessage *lm )
1266
{
1267
assert( lm != NULL );
1268
1269
return ( lm != NULL ) ? lm->lm_msgid : -1;
1270
}
1271
1272
1273
const char *
1274
ldap_int_msgtype2str( ber_tag_t tag )
1275
{
1276
switch( tag ) {
1277
case LDAP_RES_ADD: return "add";
1278
case LDAP_RES_BIND: return "bind";
1279
case LDAP_RES_COMPARE: return "compare";
1280
case LDAP_RES_DELETE: return "delete";
1281
case LDAP_RES_EXTENDED: return "extended-result";
1282
case LDAP_RES_INTERMEDIATE: return "intermediate";
1283
case LDAP_RES_MODIFY: return "modify";
1284
case LDAP_RES_RENAME: return "rename";
1285
case LDAP_RES_SEARCH_ENTRY: return "search-entry";
1286
case LDAP_RES_SEARCH_REFERENCE: return "search-reference";
1287
case LDAP_RES_SEARCH_RESULT: return "search-result";
1288
}
1289
return "unknown";
1290
}
1291
1292
int
1293
ldap_msgfree( LDAPMessage *lm )
1294
{
1295
LDAPMessage *next;
1296
int type = 0;
1297
1298
Debug0( LDAP_DEBUG_TRACE, "ldap_msgfree\n" );
1299
1300
for ( ; lm != NULL; lm = next ) {
1301
next = lm->lm_chain;
1302
type = lm->lm_msgtype;
1303
ber_free( lm->lm_ber, 1 );
1304
LDAP_FREE( (char *) lm );
1305
}
1306
1307
return type;
1308
}
1309
1310
/*
1311
* ldap_msgdelete - delete a message. It returns:
1312
* 0 if the entire message was deleted
1313
* -1 if the message was not found, or only part of it was found
1314
*/
1315
int
1316
ldap_msgdelete( LDAP *ld, int msgid )
1317
{
1318
LDAPMessage *lm, *prev;
1319
int rc = 0;
1320
1321
assert( ld != NULL );
1322
1323
Debug2( LDAP_DEBUG_TRACE, "ldap_msgdelete ld=%p msgid=%d\n",
1324
(void *)ld, msgid );
1325
1326
LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
1327
prev = NULL;
1328
for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
1329
if ( lm->lm_msgid == msgid ) {
1330
break;
1331
}
1332
prev = lm;
1333
}
1334
1335
if ( lm == NULL ) {
1336
rc = -1;
1337
1338
} else {
1339
if ( prev == NULL ) {
1340
ld->ld_responses = lm->lm_next;
1341
} else {
1342
prev->lm_next = lm->lm_next;
1343
}
1344
}
1345
LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
1346
if ( lm ) {
1347
switch ( ldap_msgfree( lm ) ) {
1348
case LDAP_RES_SEARCH_ENTRY:
1349
case LDAP_RES_SEARCH_REFERENCE:
1350
case LDAP_RES_INTERMEDIATE:
1351
rc = -1;
1352
break;
1353
1354
default:
1355
break;
1356
}
1357
}
1358
1359
return rc;
1360
}
1361
1362
1363
/*
1364
* ldap_abandoned
1365
*
1366
* return the location of the message id in the array of abandoned
1367
* message ids, or -1
1368
*/
1369
static int
1370
ldap_abandoned( LDAP *ld, ber_int_t msgid )
1371
{
1372
int ret, idx;
1373
assert( msgid >= 0 );
1374
1375
LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex );
1376
ret = ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &idx );
1377
LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
1378
return ret;
1379
}
1380
1381
/*
1382
* ldap_mark_abandoned
1383
*/
1384
static int
1385
ldap_mark_abandoned( LDAP *ld, ber_int_t msgid )
1386
{
1387
int ret, idx;
1388
1389
assert( msgid >= 0 );
1390
LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex );
1391
ret = ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &idx );
1392
if (ret <= 0) { /* error or already deleted by another thread */
1393
LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
1394
return ret;
1395
}
1396
/* still in abandoned array, so delete */
1397
ret = ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,
1398
msgid, idx );
1399
LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
1400
return ret;
1401
}
1402
1403