upstream.c 24.3 KB
Newer Older
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
 * Copyright 1998-2020 The OpenLDAP Foundation.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */

#include "portable.h"

#include <ac/socket.h>
#include <ac/errno.h>
#include <ac/string.h>
#include <ac/time.h>
#include <ac/unistd.h>

#include "lutil.h"
#include "slap.h"

Ondřej Kuzník's avatar
Ondřej Kuzník committed
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
static int
forward_response( Operation *op, BerElement *ber )
{
    Connection *c = op->o_client;
    BerElement *output;
    BerValue response, controls = BER_BVNULL;
    ber_tag_t tag, response_tag;
    ber_len_t len;

    response_tag = ber_skip_element( ber, &response );

    tag = ber_peek_tag( ber, &len );
    if ( tag == LDAP_TAG_CONTROLS ) {
        ber_skip_element( ber, &controls );
    }

Ondřej Kuzník's avatar
Ondřej Kuzník committed
43
    Debug( LDAP_DEBUG_TRACE, "forward_response: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
44
            "%s to client connid=%lu request msgid=%d\n",
45
46
            slap_msgtype2str( response_tag ), op->o_client_connid,
            op->o_client_msgid );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

    ldap_pvt_thread_mutex_lock( &c->c_io_mutex );
    output = c->c_pendingber;
    if ( output == NULL && (output = ber_alloc()) == NULL ) {
        ber_free( ber, 1 );
        ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
        return -1;
    }
    c->c_pendingber = output;

    ber_printf( output, "t{titOtO}", LDAP_TAG_MESSAGE,
            LDAP_TAG_MSGID, op->o_client_msgid,
            response_tag, &response,
            LDAP_TAG_CONTROLS, BER_BV_OPTIONAL( &controls ) );

    ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );

    ber_free( ber, 1 );
65
    connection_write_cb( -1, 0, c );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
66
67
68
69
70
71
72
73
    return 0;
}

static int
forward_final_response( Operation *op, BerElement *ber )
{
    int rc;

Ondřej Kuzník's avatar
Ondřej Kuzník committed
74
    Debug( LDAP_DEBUG_STATS, "forward_final_response: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
75
76
77
            "connid=%lu msgid=%d finishing up with a request for "
            "client connid=%lu\n",
            op->o_upstream_connid, op->o_upstream_msgid, op->o_client_connid );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
78
    rc = forward_response( op, ber );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
79
80
81
    CONNECTION_LOCK_DECREF(op->o_upstream);
    operation_destroy_from_upstream( op );
    CONNECTION_UNLOCK_INCREF(op->o_upstream);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
82
83
84
85
86
87
88

    return rc;
}

static int
handle_bind_response( Operation *op, BerElement *ber )
{
Ondřej Kuzník's avatar
Ondřej Kuzník committed
89
    Connection *client = op->o_client, *upstream = op->o_upstream;
90
    BerValue response;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
91
    BerElement *copy;
92
    ber_int_t result;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
93
    ber_tag_t tag;
94
    int rc = LDAP_SUCCESS;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
95

96
    if ( (copy = ber_alloc()) == NULL ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
97
98
99
100
        rc = -1;
        goto done;
    }

101
102
103
104
105
106
    tag = ber_peek_element( ber, &response );
    assert( tag == LDAP_RES_BIND );

    ber_init2( copy, &response, 0 );

    tag = ber_get_enum( copy, &result );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
107
108
109
110
111
112
113
    ber_free( copy, 0 );

    if ( tag == LBER_ERROR ) {
        rc = -1;
        goto done;
    }

Ondřej Kuzník's avatar
Ondřej Kuzník committed
114
115
116
117
    Debug( LDAP_DEBUG_STATS, "handle_bind_response: "
            "received response for bind request msgid=%d by client "
            "connid=%lu, result=%d\n",
            op->o_client_msgid, op->o_client_connid, result );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
118

Ondřej Kuzník's avatar
Ondřej Kuzník committed
119
120
121
122
123
124
125
    CONNECTION_LOCK(upstream);
    if ( result != LDAP_SASL_BIND_IN_PROGRESS ) {
        upstream->c_state = SLAP_C_READY;
    }
    CONNECTION_UNLOCK(upstream);

    CONNECTION_LOCK(client);
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
    if ( client->c_state == SLAP_C_BINDING ) {
        switch ( result ) {
            case LDAP_SASL_BIND_IN_PROGRESS:
                break;
            case LDAP_SUCCESS:
            default: {
                client->c_state = SLAP_C_READY;
                client->c_type = SLAP_C_OPEN;
                if ( result != LDAP_SUCCESS ) {
                    ber_memfree( client->c_auth.bv_val );
                    BER_BVZERO( &client->c_auth );
                } else if ( !ber_bvstrcasecmp(
                                    &client->c_auth, &lloadd_identity ) ) {
                    client->c_type = SLAP_C_PRIVILEGED;
                }
                if ( !BER_BVISNULL( &client->c_sasl_bind_mech ) ) {
                    ber_memfree( client->c_sasl_bind_mech.bv_val );
                    BER_BVZERO( &client->c_sasl_bind_mech );
                }
                break;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
146
147
            }
        }
148
    } else {
149
150
        assert( client->c_state == SLAP_C_INVALID ||
                client->c_state == SLAP_C_CLOSING );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
151
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
152
    CONNECTION_UNLOCK(client);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
153
154
155

done:
    if ( rc ) {
156
        operation_send_reject( op, LDAP_OTHER, "internal error", 0 );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
157

Ondřej Kuzník's avatar
Ondřej Kuzník committed
158
        ber_free( ber, 1 );
159
        return LDAP_SUCCESS;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
160
161
162
163
    }
    return forward_final_response( op, ber );
}

164
#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
Ondřej Kuzník's avatar
Ondřej Kuzník committed
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
static int
handle_vc_bind_response( Operation *op, BerElement *ber )
{
    Connection *c = op->o_client;
    BerElement *output;
    BerValue matched, diagmsg, creds = BER_BVNULL, controls = BER_BVNULL;
    ber_int_t result;
    ber_tag_t tag;
    ber_len_t len;
    int rc = 0;

    tag = ber_scanf( ber, "{emm" /* "}" */,
            &result, &matched, &diagmsg );
    if ( tag == LBER_ERROR ) {
        rc = -1;
        goto done;
    }

    tag = ber_peek_tag( ber, &len );
    if ( result == LDAP_PROTOCOL_ERROR ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
185
186
187
        Connection *upstream = op->o_upstream;
        Backend *b;

188
        CONNECTION_LOCK(upstream);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
189
        b = (Backend *)upstream->c_private;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
190
191
        Debug( LDAP_DEBUG_ANY, "handle_vc_bind_response: "
                "VC extended operation not supported on backend %s\n",
192
                b->b_uri.bv_val );
193
        CONNECTION_UNLOCK(upstream);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
194
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
195

Ondřej Kuzník's avatar
Ondřej Kuzník committed
196
197
198
199
    Debug( LDAP_DEBUG_STATS, "handle_vc_bind_response: "
            "received response for bind request msgid=%d by client "
            "connid=%lu, result=%d\n",
            op->o_client_msgid, op->o_client_connid, result );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
200

Ondřej Kuzník's avatar
Ondřej Kuzník committed
201
    CONNECTION_LOCK(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
202
203
204
205
206
207
208
209

    if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE ) {
        if ( !BER_BVISNULL( &c->c_vc_cookie ) ) {
            ber_memfree( c->c_vc_cookie.bv_val );
        }
        tag = ber_scanf( ber, "o", &c->c_vc_cookie );
        if ( tag == LBER_ERROR ) {
            rc = -1;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
210
            CONNECTION_UNLOCK_INCREF(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
211
212
213
214
215
216
217
218
219
            goto done;
        }
        tag = ber_peek_tag( ber, &len );
    }

    if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_SCREDS ) {
        tag = ber_scanf( ber, "m", &creds );
        if ( tag == LBER_ERROR ) {
            rc = -1;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
220
            CONNECTION_UNLOCK_INCREF(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
221
222
223
224
225
226
227
228
229
            goto done;
        }
        tag = ber_peek_tag( ber, &len );
    }

    if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS ) {
        tag = ber_scanf( ber, "m", &controls );
        if ( tag == LBER_ERROR ) {
            rc = -1;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
230
            CONNECTION_UNLOCK_INCREF(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
231
232
233
234
            goto done;
        }
    }

235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
    if ( c->c_state == SLAP_C_BINDING ) {
        switch ( result ) {
            case LDAP_SASL_BIND_IN_PROGRESS:
                break;
            case LDAP_SUCCESS:
            default: {
                c->c_state = SLAP_C_READY;
                c->c_type = SLAP_C_OPEN;
                if ( result != LDAP_SUCCESS ) {
                    ber_memfree( c->c_auth.bv_val );
                    BER_BVZERO( &c->c_auth );
                } else if ( !ber_bvstrcasecmp(
                                    &c->c_auth, &lloadd_identity ) ) {
                    c->c_type = SLAP_C_PRIVILEGED;
                }
                if ( !BER_BVISNULL( &c->c_vc_cookie ) ) {
                    ber_memfree( c->c_vc_cookie.bv_val );
                    BER_BVZERO( &c->c_vc_cookie );
                }
                if ( !BER_BVISNULL( &c->c_sasl_bind_mech ) ) {
                    ber_memfree( c->c_sasl_bind_mech.bv_val );
                    BER_BVZERO( &c->c_sasl_bind_mech );
                }
                break;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
259
260
            }
        }
261
    } else {
262
        assert( c->c_state == SLAP_C_INVALID || c->c_state == SLAP_C_CLOSING );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
263
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
264
    CONNECTION_UNLOCK_INCREF(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282

    ldap_pvt_thread_mutex_lock( &c->c_io_mutex );
    output = c->c_pendingber;
    if ( output == NULL && (output = ber_alloc()) == NULL ) {
        rc = -1;
        ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
        goto done;
    }
    c->c_pendingber = output;

    rc = ber_printf( output, "t{tit{eOOtO}tO}", LDAP_TAG_MESSAGE,
            LDAP_TAG_MSGID, op->o_client_msgid, LDAP_RES_BIND,
            result, &matched, &diagmsg,
            LDAP_TAG_SASL_RES_CREDS, BER_BV_OPTIONAL( &creds ),
            LDAP_TAG_CONTROLS, BER_BV_OPTIONAL( &controls ) );

    ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
    if ( rc >= 0 ) {
283
        connection_write_cb( -1, 0, c );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
284
285
286
287
        rc = 0;
    }

done:
Ondřej Kuzník's avatar
Ondřej Kuzník committed
288
289
    CONNECTION_LOCK_DECREF(c);
    operation_destroy_from_client( op );
290
    CONNECTION_UNLOCK_OR_DESTROY(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
291
292
293
    ber_free( ber, 1 );
    return rc;
}
294
#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
Ondřej Kuzník's avatar
Ondřej Kuzník committed
295
296
297
298

static int
handle_unsolicited( Connection *c, BerElement *ber )
{
299
    c->c_state = SLAP_C_CLOSING;
300

Ondřej Kuzník's avatar
Ondřej Kuzník committed
301
    Debug( LDAP_DEBUG_CONNS, "handle_unsolicited: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
302
            "teardown for upstream connection connid=%lu\n",
Ondřej Kuzník's avatar
Ondřej Kuzník committed
303
304
            c->c_connid );

305
    CONNECTION_DESTROY(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
    ber_free( ber, 1 );

    return -1;
}

/*
 * Pull c->c_currentber from the connection and try to look up the operation on
 * the upstream.
 *
 * If it's a notice of disconnection, we won't find it and need to tear down
 * the connection and tell the clients, if we can't find the operation, ignore
 * the message (either client already disconnected/abandoned it or the upstream
 * is pulling our leg).
 *
 * Some responses need special handling:
 * - Bind response
 * - VC response where the client requested a Bind (both need to update the
 *   client's bind status)
 * - search entries/referrals and intermediate responses (will not trigger
 *   operation to be removed)
 *
 * If the worker pool is overloaded, we might be called directly from
328
 * the read callback, at that point, the connection hasn't been muted.
Ondřej Kuzník's avatar
Ondřej Kuzník committed
329
330
331
332
 *
 * TODO: when the client already has data pending on write, we should mute the
 * upstream.
 * - should record the BerElement on the Op and the Op on the client
Ondřej Kuzník's avatar
Ondřej Kuzník committed
333
334
335
336
337
 *
 * The following hold on entering any of the handlers:
 * - op->o_upstream_refcnt > 0
 * - op->o_upstream->c_refcnt > 0
 * - op->o_client->c_refcnt > 0
Ondřej Kuzník's avatar
Ondřej Kuzník committed
338
339
340
341
342
 */
static int
handle_one_response( Connection *c )
{
    BerElement *ber;
343
    Operation *op = NULL, needle = { .o_upstream_connid = c->c_connid };
Ondřej Kuzník's avatar
Ondřej Kuzník committed
344
345
346
    OperationHandler handler = NULL;
    ber_tag_t tag;
    ber_len_t len;
347
    int rc = LDAP_SUCCESS;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363

    ber = c->c_currentber;
    c->c_currentber = NULL;

    tag = ber_get_int( ber, &needle.o_upstream_msgid );
    if ( tag != LDAP_TAG_MSGID ) {
        rc = -1;
        ber_free( ber, 1 );
        goto fail;
    }

    if ( needle.o_upstream_msgid == 0 ) {
        return handle_unsolicited( c, ber );
    } else if ( !( op = tavl_find(
                           c->c_ops, &needle, operation_upstream_cmp ) ) ) {
        /* Already abandoned, do nothing */
364
365
        ber_free( ber, 1 );
        return rc;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
        /*
    } else if ( op->o_response_pending ) {
        c->c_pendingop = op;
        event_del( c->c_read_event );
    */
    } else {
        /*
        op->o_response_pending = ber;
        */

        tag = ber_peek_tag( ber, &len );
        switch ( tag ) {
            case LDAP_RES_SEARCH_ENTRY:
            case LDAP_RES_SEARCH_REFERENCE:
            case LDAP_RES_INTERMEDIATE:
                handler = forward_response;
                break;
            case LDAP_RES_BIND:
                handler = handle_bind_response;
                break;
            case LDAP_RES_EXTENDED:
387
#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
Ondřej Kuzník's avatar
Ondřej Kuzník committed
388
389
390
                if ( op->o_tag == LDAP_REQ_BIND ) {
                    handler = handle_vc_bind_response;
                }
391
#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
Ondřej Kuzník's avatar
Ondřej Kuzník committed
392
393
394
395
396
397
398
                break;
        }
        if ( !handler ) {
            handler = forward_final_response;
        }
    }
    if ( op ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
399
        Debug( LDAP_DEBUG_STATS2, "handle_one_response: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
400
401
                "upstream connid=%lu, processing response for "
                "client connid=%lu, msgid=%d\n",
402
                c->c_connid, op->o_client_connid, op->o_client_msgid );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
403
404
    } else {
        tag = ber_peek_tag( ber, &len );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
405
406
407
        Debug( LDAP_DEBUG_STATS2, "handle_one_response: "
                "upstream connid=%lu, %s, msgid=%d not for a pending "
                "operation\n",
Ondřej Kuzník's avatar
Ondřej Kuzník committed
408
409
410
411
                c->c_connid, slap_msgtype2str( tag ), needle.o_upstream_msgid );
    }

    if ( handler ) {
412
413
        Connection *client;

Ondřej Kuzník's avatar
Ondřej Kuzník committed
414
415
        op->o_upstream_refcnt++;
        CONNECTION_UNLOCK_INCREF(c);
416

Ondřej Kuzník's avatar
Ondřej Kuzník committed
417
        ldap_pvt_thread_mutex_lock( &op->o_link_mutex );
418
419
420
        client = op->o_client;
        if ( client ) {
            CONNECTION_LOCK(client);
421
422
423
424
425
426
427
            if ( client->c_live ) {
                op->o_client_refcnt++;
                CONNECTION_UNLOCK_INCREF(client);
            } else {
                CONNECTION_UNLOCK(client);
                client = NULL;
            }
428
        }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
429
        ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
430
431
432
433

        if ( client ) {
            rc = handler( op, ber );
            CONNECTION_LOCK_DECREF(client);
434
435
436
437
            op->o_client_refcnt--;
            if ( !op->o_client_refcnt ) {
                operation_destroy_from_client( op );
            }
438
            CONNECTION_UNLOCK_OR_DESTROY(client);
439
440
441
442
        } else {
            ber_free( ber, 1 );
        }

Ondřej Kuzník's avatar
Ondřej Kuzník committed
443
444
        CONNECTION_LOCK_DECREF(c);
        op->o_upstream_refcnt--;
445
        if ( !client || !op->o_upstream_refcnt ) {
446
447
448
            if ( c->c_state == SLAP_C_BINDING ) {
                c->c_state = SLAP_C_READY;
            }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
449
450
            operation_destroy_from_upstream( op );
        }
451
452
    } else {
        ber_free( ber, 1 );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
453
454
455
456
    }

fail:
    if ( rc ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
457
458
459
460
        Debug( LDAP_DEBUG_STATS, "handle_one_response: "
                "error on processing a response (%s) on upstream connection "
                "connid=%ld, tag=%lx\n",
                slap_msgtype2str( tag ), c->c_connid, tag );
461
        CONNECTION_DESTROY(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
462
    }
463
    /* We leave the connection locked */
Ondřej Kuzník's avatar
Ondřej Kuzník committed
464
465
466
    return rc;
}

467
int
468
469
470
471
472
473
474
upstream_finish( Connection *c )
{
    struct event_base *base;
    struct event *event;
    evutil_socket_t s = c->c_fd;

    Debug( LDAP_DEBUG_CONNS, "upstream_finish: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
475
            "connection connid=%lu is ready for use\n",
Ondřej Kuzník's avatar
Ondřej Kuzník committed
476
            c->c_connid );
477
478
479

    base = slap_get_base( s );

480
    event = event_new( base, s, EV_READ|EV_PERSIST, connection_read_cb, c );
481
    if ( !event ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
482
483
        Debug( LDAP_DEBUG_ANY, "upstream_finish: "
                "Read event could not be allocated\n" );
484
        return -1;
485
486
487
488
489
490
491
492
493
494
    }
    event_add( event, NULL );
    if ( c->c_read_event ) {
        event_del( c->c_read_event );
        event_free( c->c_read_event );
    }
    c->c_read_event = event;

    c->c_state = SLAP_C_READY;

495
    return 0;
496
497
498
499
500
501
502
}

void
upstream_bind_cb( evutil_socket_t s, short what, void *arg )
{
    Connection *c = arg;
    BerElement *ber;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
503
    BerValue matcheddn, message;
504
505
506
507
    ber_tag_t tag;
    ber_len_t len;
    ber_int_t msgid, result;

508
    CONNECTION_LOCK(c);
509
    Debug( LDAP_DEBUG_CONNS, "upstream_bind_cb: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
510
            "connection connid=%lu ready to read\n",
511
512
513
514
            c->c_connid );

    ber = c->c_currentber;
    if ( ber == NULL && (ber = ber_alloc()) == NULL ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
515
516
        Debug( LDAP_DEBUG_ANY, "upstream_bind_cb: "
                "ber_alloc failed\n" );
517
        CONNECTION_UNLOCK(c);
518
519
        return;
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
520
    c->c_currentber = ber;
521
522
523
524
525
526

    tag = ber_get_next( c->c_sb, &len, ber );
    if ( tag != LDAP_TAG_MESSAGE ) {
        int err = sock_errno();

        if ( err != EWOULDBLOCK && err != EAGAIN ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
527
528
529
530
531
532
533
534
535
536
537
538
            if ( err || tag == LBER_ERROR ) {
                char ebuf[128];
                Debug( LDAP_DEBUG_ANY, "upstream_bind_cb: "
                        "ber_get_next on fd=%d failed errno=%d (%s)\n",
                        c->c_fd, err,
                        sock_errstr( err, ebuf, sizeof(ebuf) ) );
            } else {
                Debug( LDAP_DEBUG_STATS, "upstream_bind_cb: "
                        "ber_get_next on fd=%d connid=%lu received "
                        "a strange PDU tag=%lx\n",
                        c->c_fd, c->c_connid, tag );
            }
539
540
541
542

            c->c_currentber = NULL;
            goto fail;
        }
543
        CONNECTION_UNLOCK(c);
544
545
546
547
548
        return;
    }
    c->c_currentber = NULL;

    if ( ber_scanf( ber, "it", &msgid, &tag ) == LBER_ERROR ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
549
550
        Debug( LDAP_DEBUG_ANY, "upstream_bind_cb: "
                "protocol violation from server\n" );
551
552
553
554
        goto fail;
    }

    if ( msgid != ( c->c_next_msgid - 1 ) || tag != LDAP_RES_BIND ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
555
556
        Debug( LDAP_DEBUG_ANY, "upstream_bind_cb: "
                "unexpected %s from server, msgid=%d\n",
557
                slap_msgtype2str( tag ), msgid );
558
559
560
        goto fail;
    }

Ondřej Kuzník's avatar
Ondřej Kuzník committed
561
    if ( ber_scanf( ber, "{emm" /* "}" */, &result, &matcheddn, &message ) ==
562
                 LBER_ERROR ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
563
564
        Debug( LDAP_DEBUG_ANY, "upstream_bind_cb: "
                "response does not conform with a bind response\n" );
565
566
567
568
569
        goto fail;
    }

    switch ( result ) {
        case LDAP_SUCCESS:
570
571
572
            if ( upstream_finish( c ) ) {
                goto fail;
            }
573
574
575
576
577
578
579
580
            break;
#ifdef HAVE_CYRUS_SASL
        case LDAP_SASL_BIND_IN_PROGRESS:
            /* TODO: fallthrough until we implement SASL */
#endif /* HAVE_CYRUS_SASL */
        default:
            Debug( LDAP_DEBUG_ANY, "upstream_bind_cb: "
                    "upstream bind failed, rc=%d, message='%s'\n",
Ondřej Kuzník's avatar
Ondřej Kuzník committed
581
                    result, message.bv_val );
582
583
584
            goto fail;
    }

585
    CONNECTION_UNLOCK(c);
586

Ondřej Kuzník's avatar
Ondřej Kuzník committed
587
    ber_free( ber, 1 );
588
589
    return;

Ondřej Kuzník's avatar
Ondřej Kuzník committed
590
fail:
591
    event_del( c->c_read_event );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
592
593
594
    Debug( LDAP_DEBUG_CONNS, "upstream_bind_cb: "
            "suspended read event on dying connid=%lu\n",
            c->c_connid );
595
    ber_free( ber, 1 );
596
    CONNECTION_DESTROY(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
597
598
}

599
600
void *
upstream_bind( void *ctx, void *arg )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
601
{
602
603
604
605
    Connection *c = arg;
    Backend *b;
    BerElement *ber = ber_alloc();
    struct event_base *base;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
606
    struct event *event;
607
608
    ber_int_t msgid;
    evutil_socket_t s;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
609

610
    assert( ber );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
611

612
    CONNECTION_LOCK(c);
613
614
615
    b = c->c_private;
    s = c->c_fd;
    base = slap_get_base( s );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
616

617
    event = event_new( base, s, EV_READ|EV_PERSIST, upstream_bind_cb, c );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
618
    if ( !event ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
619
620
        Debug( LDAP_DEBUG_ANY, "upstream_bind: "
                "Read event could not be allocated\n" );
621
        CONNECTION_DESTROY(c);
622
        return NULL;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
623
624
    }
    event_add( event, NULL );
625
626
627
628
    if ( c->c_read_event ) {
        event_del( c->c_read_event );
        event_free( c->c_read_event );
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
629
630
    c->c_read_event = event;

631
632
    msgid = c->c_next_msgid++;

633
    CONNECTION_UNLOCK_INCREF(c);
634

Ondřej Kuzník's avatar
Ondřej Kuzník committed
635
    ldap_pvt_thread_mutex_lock( &b->b_mutex );
636
    if ( bindconf.sb_method == LDAP_AUTH_SIMPLE ) {
637
        /* simple bind */
638
        ber_printf( ber, "{it{iOtON}}",
639
                msgid, LDAP_REQ_BIND, LDAP_VERSION3,
640
641
                &bindconf.sb_binddn, LDAP_AUTH_SIMPLE,
                &bindconf.sb_cred );
642
643
644

#ifdef HAVE_CYRUS_SASL
    } else {
645
646
647
        BerValue cred = BER_BVNULL;
        ber_printf( ber, "{it{iOt{OON}N}}",
                msgid, LDAP_REQ_BIND, LDAP_VERSION3,
648
649
                &bindconf.sb_binddn, LDAP_AUTH_SASL,
                &bindconf.sb_saslmech, BER_BV_OPTIONAL( &cred ) );
650
651
#endif /* HAVE_CYRUS_SASL */
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
652
    ldap_pvt_thread_mutex_unlock( &b->b_mutex );
653
654
655
656
657

    ldap_pvt_thread_mutex_lock( &c->c_io_mutex );
    c->c_pendingber = ber;
    ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );

658
    connection_write_cb( -1, 0, c );
659

660
    CONNECTION_LOCK_DECREF(c);
661
    CONNECTION_UNLOCK_OR_DESTROY(c);
662

663
664
665
    return NULL;
}

666
667
668
/*
 * We must already hold b->b_mutex when called.
 */
669
670
671
672
673
674
Connection *
upstream_init( ber_socket_t s, Backend *b )
{
    Connection *c;
    struct event_base *base = slap_get_base( s );
    struct event *event;
675
    int flags, is_bindconn = 0;
676
677
678

    assert( b != NULL );

679
    flags = (b->b_tls == LLOAD_LDAPS) ? CONN_IS_TLS : 0;
680
681
682
683
    if ( (c = connection_init( s, b->b_host, flags )) == NULL ) {
        return NULL;
    }

684
    c->c_private = b;
685
686
    c->c_is_tls = b->b_tls;
    c->c_pdu_cb = handle_one_response;
687

688
689
690
691
692
    {
        ber_len_t max = sockbuf_max_incoming_upstream;
        ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_SET_MAX_INCOMING, &max );
    }

693
694
695
696
697
698
699
700
701
    event = event_new( base, s, EV_READ|EV_PERSIST, connection_read_cb, c );
    if ( !event ) {
        Debug( LDAP_DEBUG_ANY, "upstream_init: "
                "Read event could not be allocated\n" );
        goto fail;
    }
    c->c_read_event = event;

    event = event_new( base, s, EV_WRITE, connection_write_cb, c );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
702
    if ( !event ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
703
704
        Debug( LDAP_DEBUG_ANY, "upstream_init: "
                "Write event could not be allocated\n" );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
705
706
        goto fail;
    }
707
    /* We only add the write event when we have data pending */
Ondřej Kuzník's avatar
Ondřej Kuzník committed
708
709
    c->c_write_event = event;

710
711
712
713
    /* Unless we are configured to use the VC exop, consider allocating the
     * connection into the bind conn pool. Start off by allocating one for
     * general use, then one for binds, then we start filling up the general
     * connection pool, finally the bind pool */
714
715
716
717
718
    if (
#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
            !(lload_features & LLOAD_FEATURE_VC) &&
#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
            b->b_active && b->b_numbindconns ) {
719
720
721
722
723
724
725
726
        if ( !b->b_bindavail ) {
            is_bindconn = 1;
        } else if ( b->b_active >= b->b_numconns &&
                b->b_bindavail < b->b_numbindconns ) {
            is_bindconn = 1;
        }
    }

727
    if ( is_bindconn || bindconf.sb_method == LDAP_AUTH_NONE ) {
728
729
730
        if ( upstream_finish( c ) ) {
            goto fail;
        }
731
732
733
    } else {
        ldap_pvt_thread_pool_submit( &connection_pool, upstream_bind, c );
    }
734

735
    if ( is_bindconn ) {
736
        LDAP_CIRCLEQ_INSERT_HEAD( &b->b_bindconns, c, c_next );
737
738
739
        c->c_type = SLAP_C_BIND;
        b->b_bindavail++;
    } else {
740
        LDAP_CIRCLEQ_INSERT_HEAD( &b->b_conns, c, c_next );
741
742
        b->b_active++;
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
743

744
    c->c_destroy = upstream_destroy;
745
    CONNECTION_UNLOCK(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
746
    return c;
747

Ondřej Kuzník's avatar
Ondřej Kuzník committed
748
749
750
751
752
753
754
755
756
fail:
    if ( c->c_write_event ) {
        event_del( c->c_write_event );
        event_free( c->c_write_event );
    }
    if ( c->c_read_event ) {
        event_del( c->c_read_event );
        event_free( c->c_read_event );
    }
757
758
759
760
761

    c->c_state = SLAP_C_INVALID;
    CONNECTION_DESTROY(c);
    assert( c == NULL );

Ondřej Kuzník's avatar
Ondřej Kuzník committed
762
763
764
    return NULL;
}

Ondřej Kuzník's avatar
Ondřej Kuzník committed
765
void
Ondřej Kuzník's avatar
Ondřej Kuzník committed
766
767
768
upstream_destroy( Connection *c )
{
    Backend *b = c->c_private;
769
    struct event *read_event, *write_event;
770
    TAvlnode *root;
771
    long freed, executing;
772
    enum sc_state state;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
773

774
    Debug( LDAP_DEBUG_CONNS, "upstream_destroy: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
775
            "freeing connection connid=%lu\n",
776
777
            c->c_connid );

778
779
    assert( c->c_state != SLAP_C_INVALID );
    state = c->c_state;
780
    c->c_state = SLAP_C_INVALID;
781

782
783
    root = c->c_ops;
    c->c_ops = NULL;
784
    executing = c->c_n_ops_executing;
785
    c->c_n_ops_executing = 0;
786

787
788
    read_event = c->c_read_event;
    write_event = c->c_write_event;
789

790
791
    CONNECTION_UNLOCK_INCREF(c);

792
    freed = tavl_free( root, (AVL_FREE)operation_lost_upstream );
793
    assert( freed == executing );
794

795
796
797
798
799
    /*
     * Avoid a deadlock:
     * event_del will block if the event is currently executing its callback,
     * that callback might be waiting to lock c->c_mutex
     */
Ondřej Kuzník's avatar
Ondřej Kuzník committed
800
801
802
    if ( read_event ) {
        event_del( read_event );
    }
803

Ondřej Kuzník's avatar
Ondřej Kuzník committed
804
805
806
    if ( write_event ) {
        event_del( write_event );
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
807

808
809
810
811
812
813
814
815
816
817
818
819
820
    /* Remove from the backend on first pass */
    if ( state != SLAP_C_CLOSING ) {
        ldap_pvt_thread_mutex_lock( &b->b_mutex );
        if ( c->c_type == SLAP_C_BIND ) {
            LDAP_CIRCLEQ_REMOVE( &b->b_bindconns, c, c_next );
            b->b_bindavail--;
        } else {
            LDAP_CIRCLEQ_REMOVE( &b->b_conns, c, c_next );
            b->b_active--;
        }
        b->b_n_ops_executing -= executing;
        ldap_pvt_thread_mutex_unlock( &b->b_mutex );
        backend_retry( b );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
821
    }
822

823
    CONNECTION_LOCK_DECREF(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
824

Ondřej Kuzník's avatar
Ondřej Kuzník committed
825
826
827
828
829
830
831
832
833
834
    if ( c->c_read_event ) {
        event_free( c->c_read_event );
        c->c_read_event = NULL;
    }

    if ( c->c_write_event ) {
        event_free( c->c_write_event );
        c->c_write_event = NULL;
    }

Ondřej Kuzník's avatar
Ondřej Kuzník committed
835
836
837
838
839
840
841
842
843
844
845
846
847
848
    /*
     * If we attempted to destroy any operations, we might have lent a new
     * refcnt token for a thread that raced us to that, let them call us again
     * later
     */
    assert( c->c_refcnt >= 0 );
    if ( c->c_refcnt ) {
        c->c_state = SLAP_C_CLOSING;
        Debug( LDAP_DEBUG_CONNS, "upstream_destroy: "
                "connid=%lu aborting with refcnt=%d\n",
                c->c_connid, c->c_refcnt );
        CONNECTION_UNLOCK(c);
        return;
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
849
850
    connection_destroy( c );
}