upstream.c 25.6 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
/* $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"
25
#include "lload.h"
Ondřej Kuzník's avatar
Ondřej Kuzník committed
26

Ondřej Kuzník's avatar
Ondřej Kuzník committed
27
int
28
forward_response( LloadConnection *client, LloadOperation *op, BerElement *ber )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
29
30
31
{
    BerElement *output;
    BerValue response, controls = BER_BVNULL;
32
    ber_int_t msgid;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
33
34
35
    ber_tag_t tag, response_tag;
    ber_len_t len;

36
37
38
39
40
41
42
43
44
45
    CONNECTION_LOCK(client);
    if ( op->o_client_msgid ) {
        msgid = op->o_client_msgid;
    } else {
        assert( op->o_pin_id );
        msgid = op->o_saved_msgid;
        op->o_saved_msgid = 0;
    }
    CONNECTION_UNLOCK(client);

Ondřej Kuzník's avatar
Ondřej Kuzník committed
46
47
48
49
50
51
52
    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
53
    Debug( LDAP_DEBUG_TRACE, "forward_response: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
54
            "%s to client connid=%lu request msgid=%d\n",
55
            lload_msgtype2str( response_tag ), op->o_client_connid, msgid );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
56

57
58
    ldap_pvt_thread_mutex_lock( &client->c_io_mutex );
    output = client->c_pendingber;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
59
60
    if ( output == NULL && (output = ber_alloc()) == NULL ) {
        ber_free( ber, 1 );
61
        ldap_pvt_thread_mutex_unlock( &client->c_io_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
62
63
        return -1;
    }
64
    client->c_pendingber = output;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
65
66

    ber_printf( output, "t{titOtO}", LDAP_TAG_MESSAGE,
67
            LDAP_TAG_MSGID, msgid,
Ondřej Kuzník's avatar
Ondřej Kuzník committed
68
69
70
            response_tag, &response,
            LDAP_TAG_CONTROLS, BER_BV_OPTIONAL( &controls ) );

71
    ldap_pvt_thread_mutex_unlock( &client->c_io_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
72
73

    ber_free( ber, 1 );
74
    connection_write_cb( -1, 0, client );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
75
76
77
    return 0;
}

Ondřej Kuzník's avatar
Ondřej Kuzník committed
78
int
79
80
81
82
forward_final_response(
        LloadConnection *client,
        LloadOperation *op,
        BerElement *ber )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
83
84
85
{
    int rc;

Ondřej Kuzník's avatar
Ondřej Kuzník committed
86
    Debug( LDAP_DEBUG_STATS, "forward_final_response: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
87
88
89
            "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 );
90
    rc = forward_response( client, op, ber );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
91
    CONNECTION_LOCK(op->o_upstream);
92
    op->o_res = LLOAD_OP_COMPLETED;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
93
94
95
96
    if ( !op->o_pin_id || !op->o_upstream_refcnt-- ) {
        operation_destroy_from_upstream( op );
    }
    CONNECTION_UNLOCK(op->o_upstream);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
97
98
99
100
101

    return rc;
}

static int
102
handle_unsolicited( LloadConnection *c, BerElement *ber )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
103
{
104
105
    if ( c->c_state == LLOAD_C_READY ) {
        c->c_state = LLOAD_C_CLOSING;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
106
    }
107

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

112
    CONNECTION_DESTROY(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
    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
135
 * the read callback, at that point, the connection hasn't been muted.
Ondřej Kuzník's avatar
Ondřej Kuzník committed
136
137
138
139
 *
 * 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
140
141
142
143
144
 *
 * 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
145
146
 */
static int
147
handle_one_response( LloadConnection *c )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
148
149
{
    BerElement *ber;
150
151
    LloadOperation *op = NULL, needle = { .o_upstream_connid = c->c_connid };
    LloadOperationHandler handler = NULL;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
152
153
    ber_tag_t tag;
    ber_len_t len;
154
    int rc = LDAP_SUCCESS;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170

    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 */
171
172
        ber_free( ber, 1 );
        return rc;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
        /*
    } 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:
                if ( op->o_tag == LDAP_REQ_BIND ) {
195
196
197
198
#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
                    if ( lload_features & LLOAD_FEATURE_VC ) {
                        handler = handle_vc_bind_response;
                    } else
199
#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
200
201
202
203
                    {
                        handler = handle_whoami_response;
                    }
                }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
204
205
206
207
208
209
210
                break;
        }
        if ( !handler ) {
            handler = forward_final_response;
        }
    }
    if ( op ) {
211
        op->o_last_response = slap_get_time();
Ondřej Kuzník's avatar
Ondřej Kuzník committed
212
        Debug( LDAP_DEBUG_STATS2, "handle_one_response: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
213
214
                "upstream connid=%lu, processing response for "
                "client connid=%lu, msgid=%d\n",
215
                c->c_connid, op->o_client_connid, op->o_client_msgid );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
216
217
    } else {
        tag = ber_peek_tag( ber, &len );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
218
219
220
        Debug( LDAP_DEBUG_STATS2, "handle_one_response: "
                "upstream connid=%lu, %s, msgid=%d not for a pending "
                "operation\n",
221
222
                c->c_connid, lload_msgtype2str( tag ),
                needle.o_upstream_msgid );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
223
224
225
    }

    if ( handler ) {
226
        LloadConnection *client;
227

Ondřej Kuzník's avatar
Ondřej Kuzník committed
228
229
        op->o_upstream_refcnt++;
        CONNECTION_UNLOCK_INCREF(c);
230

Ondřej Kuzník's avatar
Ondřej Kuzník committed
231
        ldap_pvt_thread_mutex_lock( &op->o_link_mutex );
232
233
234
        client = op->o_client;
        if ( client ) {
            CONNECTION_LOCK(client);
235
236
237
238
239
240
241
            if ( client->c_live ) {
                op->o_client_refcnt++;
                CONNECTION_UNLOCK_INCREF(client);
            } else {
                CONNECTION_UNLOCK(client);
                client = NULL;
            }
242
        }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
243
        ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
244
245

        if ( client ) {
246
            rc = handler( client, op, ber );
247
            CONNECTION_LOCK_DECREF(client);
248
249
250
251
            op->o_client_refcnt--;
            if ( !op->o_client_refcnt ) {
                operation_destroy_from_client( op );
            }
252
            CONNECTION_UNLOCK_OR_DESTROY(client);
253
254
255
256
        } else {
            ber_free( ber, 1 );
        }

Ondřej Kuzník's avatar
Ondřej Kuzník committed
257
258
        CONNECTION_LOCK_DECREF(c);
        op->o_upstream_refcnt--;
259
        if ( !client || !op->o_upstream_refcnt ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
260
261
            operation_destroy_from_upstream( op );
        }
262
    } else {
263
        assert(0);
264
        ber_free( ber, 1 );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
265
266
267
268
    }

fail:
    if ( rc ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
269
270
271
        Debug( LDAP_DEBUG_STATS, "handle_one_response: "
                "error on processing a response (%s) on upstream connection "
                "connid=%ld, tag=%lx\n",
272
                lload_msgtype2str( tag ), c->c_connid, tag );
273
        CONNECTION_DESTROY(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
274
    }
275
    /* We leave the connection locked */
Ondřej Kuzník's avatar
Ondřej Kuzník committed
276
277
278
    return rc;
}

279
int
280
upstream_bind_cb( LloadConnection *c )
281
{
Ondřej Kuzník's avatar
Ondřej Kuzník committed
282
    BerElement *ber = c->c_currentber;
283
    LloadBackend *b = c->c_private;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
284
    BerValue matcheddn, message;
285
286
287
288
289
290
    ber_tag_t tag;
    ber_int_t msgid, result;

    c->c_currentber = NULL;

    if ( ber_scanf( ber, "it", &msgid, &tag ) == LBER_ERROR ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
291
292
        Debug( LDAP_DEBUG_ANY, "upstream_bind_cb: "
                "protocol violation from server\n" );
293
294
295
296
        goto fail;
    }

    if ( msgid != ( c->c_next_msgid - 1 ) || tag != LDAP_RES_BIND ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
297
298
        Debug( LDAP_DEBUG_ANY, "upstream_bind_cb: "
                "unexpected %s from server, msgid=%d\n",
299
                lload_msgtype2str( tag ), msgid );
300
301
302
        goto fail;
    }

Ondřej Kuzník's avatar
Ondřej Kuzník committed
303
    if ( ber_scanf( ber, "{emm" /* "}" */, &result, &matcheddn, &message ) ==
304
                 LBER_ERROR ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
305
306
        Debug( LDAP_DEBUG_ANY, "upstream_bind_cb: "
                "response does not conform with a bind response\n" );
307
308
309
310
        goto fail;
    }

    switch ( result ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
311
312
        case LDAP_SUCCESS: {
            c->c_pdu_cb = handle_one_response;
313
314
            c->c_state = LLOAD_C_READY;
            c->c_type = LLOAD_C_OPEN;
315
316
317
318
319
            c->c_read_timeout = NULL;
            event_add( c->c_read_event, c->c_read_timeout );
            Debug( LDAP_DEBUG_CONNS, "upstream_bind_cb: "
                    "connid=%lu finished binding, now active\n",
                    c->c_connid );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
320
321
322
323
324
325
            CONNECTION_UNLOCK_INCREF(c);
            ldap_pvt_thread_mutex_lock( &b->b_mutex );
            LDAP_CIRCLEQ_REMOVE( &b->b_preparing, c, c_next );
            b->b_active++;
            b->b_opening--;
            b->b_failed = 0;
326
327
328
329
330
331
332
            if ( b->b_last_conn ) {
                LDAP_CIRCLEQ_INSERT_AFTER(
                        &b->b_conns, b->b_last_conn, c, c_next );
            } else {
                LDAP_CIRCLEQ_INSERT_HEAD( &b->b_conns, c, c_next );
            }
            b->b_last_conn = c;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
333
334
335
336
            ldap_pvt_thread_mutex_unlock( &b->b_mutex );
            backend_retry( b );
            CONNECTION_LOCK_DECREF(c);
        } break;
337
338
339
340
341
342
343
#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
344
                    result, message.bv_val );
345
346
347
            goto fail;
    }

Ondřej Kuzník's avatar
Ondřej Kuzník committed
348
    ber_free( ber, 1 );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
349
    return LDAP_SUCCESS;
350

Ondřej Kuzník's avatar
Ondřej Kuzník committed
351
fail:
352
    ber_free( ber, 1 );
353
    CONNECTION_DESTROY(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
354
    return -1;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
355
356
}

357
358
void *
upstream_bind( void *ctx, void *arg )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
359
{
360
    LloadConnection *c = arg;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
361
    BerElement *ber;
362
    ber_int_t msgid;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
363

364
    CONNECTION_LOCK(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
365
366
    c->c_pdu_cb = upstream_bind_cb;
    CONNECTION_UNLOCK_INCREF(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
367

Ondřej Kuzník's avatar
Ondřej Kuzník committed
368
369
370
371
372
    ldap_pvt_thread_mutex_lock( &c->c_io_mutex );
    ber = c->c_pendingber;
    if ( ber == NULL && (ber = ber_alloc()) == NULL ) {
        ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
        CONNECTION_LOCK_DESTROY(c);
373
        return NULL;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
374
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
375
    c->c_pendingber = ber;
376
377
    msgid = c->c_next_msgid++;

378
    if ( bindconf.sb_method == LDAP_AUTH_SIMPLE ) {
379
        /* simple bind */
380
        ber_printf( ber, "{it{iOtON}}",
381
                msgid, LDAP_REQ_BIND, LDAP_VERSION3,
382
383
                &bindconf.sb_binddn, LDAP_AUTH_SIMPLE,
                &bindconf.sb_cred );
384
385
386

#ifdef HAVE_CYRUS_SASL
    } else {
387
388
389
        BerValue cred = BER_BVNULL;
        ber_printf( ber, "{it{iOt{OON}N}}",
                msgid, LDAP_REQ_BIND, LDAP_VERSION3,
390
391
                &bindconf.sb_binddn, LDAP_AUTH_SASL,
                &bindconf.sb_saslmech, BER_BV_OPTIONAL( &cred ) );
392
393
394
395
#endif /* HAVE_CYRUS_SASL */
    }
    ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );

396
    connection_write_cb( -1, 0, c );
397

398
    CONNECTION_LOCK_DECREF(c);
399
400
    c->c_read_timeout = lload_timeout_net;
    event_add( c->c_read_event, c->c_read_timeout );
401
    CONNECTION_UNLOCK_OR_DESTROY(c);
402

403
404
405
    return NULL;
}

Ondřej Kuzník's avatar
Ondřej Kuzník committed
406
407
408
409
/*
 * The backend is already locked when entering the function.
 */
static int
410
upstream_finish( LloadConnection *c )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
411
{
412
    LloadBackend *b = c->c_private;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
    int is_bindconn = 0, rc = 0;

    c->c_pdu_cb = handle_one_response;

    /* 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 */
    if (
#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
            !(lload_features & LLOAD_FEATURE_VC) &&
#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
            b->b_active && b->b_numbindconns ) {
        if ( !b->b_bindavail ) {
            is_bindconn = 1;
        } else if ( b->b_active >= b->b_numconns &&
                b->b_bindavail < b->b_numbindconns ) {
            is_bindconn = 1;
        }
    }

    if ( is_bindconn ) {
        LDAP_CIRCLEQ_REMOVE( &b->b_preparing, c, c_next );
436
437
        c->c_state = LLOAD_C_READY;
        c->c_type = LLOAD_C_BIND;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
438
439
440
        b->b_bindavail++;
        b->b_opening--;
        b->b_failed = 0;
441
442
443
444
445
446
447
        if ( b->b_last_bindconn ) {
            LDAP_CIRCLEQ_INSERT_AFTER(
                    &b->b_bindconns, b->b_last_bindconn, c, c_next );
        } else {
            LDAP_CIRCLEQ_INSERT_HEAD( &b->b_bindconns, c, c_next );
        }
        b->b_last_bindconn = c;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
448
449
    } else if ( bindconf.sb_method == LDAP_AUTH_NONE ) {
        LDAP_CIRCLEQ_REMOVE( &b->b_preparing, c, c_next );
450
451
        c->c_state = LLOAD_C_READY;
        c->c_type = LLOAD_C_OPEN;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
452
453
454
        b->b_active++;
        b->b_opening--;
        b->b_failed = 0;
455
456
457
458
459
460
        if ( b->b_last_conn ) {
            LDAP_CIRCLEQ_INSERT_AFTER( &b->b_conns, b->b_last_conn, c, c_next );
        } else {
            LDAP_CIRCLEQ_INSERT_HEAD( &b->b_conns, c, c_next );
        }
        b->b_last_conn = c;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
461
462
463
464
465
466
467
468
469
470
471
472
    } else {
        rc = 1;
        ldap_pvt_thread_pool_submit( &connection_pool, upstream_bind, c );
    }

    Debug( LDAP_DEBUG_CONNS, "upstream_finish: "
            "%sconnection connid=%lu is%s ready for use\n",
            is_bindconn ? "bind " : "", c->c_connid, rc ? " almost" : "" );

    return rc;
}

Ondřej Kuzník's avatar
Ondřej Kuzník committed
473
474
475
static void
upstream_tls_handshake_cb( evutil_socket_t s, short what, void *arg )
{
476
477
    LloadConnection *c = arg;
    LloadBackend *b;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
478
479
480
481
482
483
484
485
486
487
488
    int rc = LDAP_SUCCESS;

    CONNECTION_LOCK(c);
    if ( what & EV_TIMEOUT ) {
        Debug( LDAP_DEBUG_CONNS, "upstream_tls_handshake_cb: "
                "connid=%lu, timeout reached, destroying\n",
                c->c_connid );
        goto fail;
    }
    b = c->c_private;

489
    rc = ldap_pvt_tls_connect( lload_tls_backend_ld, c->c_sb, b->b_host );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
    if ( rc < 0 ) {
        goto fail;
    }

    if ( rc == 0 ) {
        struct event_base *base = event_get_base( c->c_read_event );

        /*
         * We're finished, replace the callbacks
         *
         * This is deadlock-safe, since both share the same base - the one
         * that's just running us.
         */
        event_del( c->c_read_event );
        event_del( c->c_write_event );

506
        c->c_read_timeout = NULL;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
507
508
        event_assign( c->c_read_event, base, c->c_fd, EV_READ|EV_PERSIST,
                connection_read_cb, c );
509
        event_add( c->c_read_event, c->c_read_timeout );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545

        event_assign( c->c_write_event, base, c->c_fd, EV_WRITE,
                connection_write_cb, c );
        Debug( LDAP_DEBUG_CONNS, "upstream_tls_handshake_cb: "
                "connid=%lu finished\n",
                c->c_connid );
        c->c_is_tls = LLOAD_TLS_ESTABLISHED;

        CONNECTION_UNLOCK_INCREF(c);
        ldap_pvt_thread_mutex_lock( &b->b_mutex );
        CONNECTION_LOCK_DECREF(c);

        rc = upstream_finish( c );

        ldap_pvt_thread_mutex_unlock( &b->b_mutex );

        if ( rc == LDAP_SUCCESS ) {
            backend_retry( b );
        }
    } else if ( ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_NEEDS_WRITE, NULL ) ) {
        event_add( c->c_write_event, lload_write_timeout );
        Debug( LDAP_DEBUG_CONNS, "upstream_tls_handshake_cb: "
                "connid=%lu need write rc=%d\n",
                c->c_connid, rc );
    }
    CONNECTION_UNLOCK_OR_DESTROY(c);
    return;

fail:
    Debug( LDAP_DEBUG_CONNS, "upstream_tls_handshake_cb: "
            "connid=%lu failed rc=%d\n",
            c->c_connid, rc );
    CONNECTION_DESTROY(c);
}

static int
546
upstream_starttls( LloadConnection *c )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
{
    BerValue matcheddn, message, responseOid,
             startTLSOid = BER_BVC(LDAP_EXOP_START_TLS);
    BerElement *ber = c->c_currentber;
    struct event_base *base;
    ber_int_t msgid, result;
    ber_tag_t tag;

    c->c_currentber = NULL;

    if ( ber_scanf( ber, "it", &msgid, &tag ) == LBER_ERROR ) {
        Debug( LDAP_DEBUG_ANY, "upstream_starttls: "
                "protocol violation from server\n" );
        goto fail;
    }

    if ( msgid != ( c->c_next_msgid - 1 ) || tag != LDAP_RES_EXTENDED ) {
        Debug( LDAP_DEBUG_ANY, "upstream_starttls: "
                "unexpected %s from server, msgid=%d\n",
566
                lload_msgtype2str( tag ), msgid );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
        goto fail;
    }

    if ( ber_scanf( ber, "{emm}", &result, &matcheddn, &message ) ==
                 LBER_ERROR ) {
        Debug( LDAP_DEBUG_ANY, "upstream_starttls: "
                "protocol violation on StartTLS response\n" );
        goto fail;
    }

    if ( (tag = ber_get_tag( ber )) != LBER_DEFAULT ) {
        if ( tag != LDAP_TAG_EXOP_RES_OID ||
                ber_scanf( ber, "{m}", &responseOid ) == LBER_DEFAULT ) {
            Debug( LDAP_DEBUG_ANY, "upstream_starttls: "
                    "protocol violation on StartTLS response\n" );
            goto fail;
        }

        if ( ber_bvcmp( &responseOid, &startTLSOid ) ) {
            Debug( LDAP_DEBUG_ANY, "upstream_starttls: "
                    "oid=%s not a StartTLS response\n",
                    responseOid.bv_val );
            goto fail;
        }
    }

    if ( result != LDAP_SUCCESS ) {
594
        LloadBackend *b = c->c_private;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
        int rc;

        Debug( LDAP_DEBUG_STATS, "upstream_starttls: "
                "server doesn't support StartTLS rc=%d message='%s'%s\n",
                result, message.bv_val,
                (c->c_is_tls == LLOAD_STARTTLS_OPTIONAL) ? ", ignored" : "" );
        if ( c->c_is_tls != LLOAD_STARTTLS_OPTIONAL ) {
            goto fail;
        }
        c->c_is_tls = LLOAD_CLEARTEXT;

        ber_free( ber, 1 );

        CONNECTION_UNLOCK_INCREF(c);
        ldap_pvt_thread_mutex_lock( &b->b_mutex );
        CONNECTION_LOCK_DECREF(c);

        rc = upstream_finish( c );

        ldap_pvt_thread_mutex_unlock( &b->b_mutex );

        if ( rc == LDAP_SUCCESS ) {
            backend_retry( b );
        }

        CONNECTION_UNLOCK_OR_DESTROY(c);
        return rc;
    }

    base = event_get_base( c->c_read_event );

    event_del( c->c_read_event );
    event_del( c->c_write_event );

629
    c->c_read_timeout = lload_timeout_net;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
630
631
632
633
634
    event_assign( c->c_read_event, base, c->c_fd, EV_READ|EV_PERSIST,
            upstream_tls_handshake_cb, c );
    event_assign( c->c_write_event, base, c->c_fd, EV_WRITE,
            upstream_tls_handshake_cb, c );

635
    event_add( c->c_read_event, c->c_read_timeout );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
636
637
638
639
640
641
642
643
644
645
646
647
648
    event_add( c->c_write_event, lload_write_timeout );

    CONNECTION_UNLOCK(c);

    ber_free( ber, 1 );
    return -1;

fail:
    ber_free( ber, 1 );
    CONNECTION_DESTROY(c);
    return -1;
}

649
650
651
/*
 * We must already hold b->b_mutex when called.
 */
652
653
LloadConnection *
upstream_init( ber_socket_t s, LloadBackend *b )
654
{
655
656
    LloadConnection *c;
    struct event_base *base = lload_get_base( s );
657
    struct event *event;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
658
    int flags, rc = -1;
659
660
661

    assert( b != NULL );

Ondřej Kuzník's avatar
Ondřej Kuzník committed
662
    flags = (b->b_proto == LDAP_PROTO_IPC) ? CONN_IS_IPC : 0;
663
    if ( (c = lload_connection_init( s, b->b_host, flags )) == NULL ) {
664
665
666
        return NULL;
    }

667
    c->c_private = b;
668
669
    c->c_is_tls = b->b_tls;
    c->c_pdu_cb = handle_one_response;
670

Ondřej Kuzník's avatar
Ondřej Kuzník committed
671
    LDAP_CIRCLEQ_INSERT_HEAD( &b->b_preparing, c, c_next );
672
    c->c_type = LLOAD_C_PREPARING;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
673

674
675
676
677
678
    {
        ber_len_t max = sockbuf_max_incoming_upstream;
        ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_SET_MAX_INCOMING, &max );
    }

679
680
681
682
683
684
685
686
687
    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
688
    if ( !event ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
689
690
        Debug( LDAP_DEBUG_ANY, "upstream_init: "
                "Write event could not be allocated\n" );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
691
692
        goto fail;
    }
693
    /* We only add the write event when we have data pending */
Ondřej Kuzník's avatar
Ondřej Kuzník committed
694
695
    c->c_write_event = event;

Ondřej Kuzník's avatar
Ondřej Kuzník committed
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
    if ( c->c_is_tls == LLOAD_CLEARTEXT ) {
        rc = upstream_finish( c );
        if ( rc < 0 ) {
            goto fail;
        }
    } else if ( c->c_is_tls == LLOAD_LDAPS ) {
        event_assign( c->c_read_event, base, s, EV_READ|EV_PERSIST,
                upstream_tls_handshake_cb, c );
        event_assign( c->c_write_event, base, s, EV_WRITE,
                upstream_tls_handshake_cb, c );
        event_add( c->c_write_event, lload_write_timeout );
    } else if ( c->c_is_tls == LLOAD_STARTTLS ||
            c->c_is_tls == LLOAD_STARTTLS_OPTIONAL ) {
        BerElement *output;

        ldap_pvt_thread_mutex_lock( &c->c_io_mutex );
        if ( (output = c->c_pendingber = ber_alloc()) == NULL ) {
            ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
            goto fail;
        }
        ber_printf( output, "t{tit{ts}}", LDAP_TAG_MESSAGE,
                LDAP_TAG_MSGID, c->c_next_msgid++,
                LDAP_REQ_EXTENDED,
                LDAP_TAG_EXOP_REQ_OID, LDAP_EXOP_START_TLS );
        ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
721

Ondřej Kuzník's avatar
Ondřej Kuzník committed
722
723
724
725
726
        c->c_pdu_cb = upstream_starttls;
        CONNECTION_UNLOCK_INCREF(c);
        connection_write_cb( s, 0, c );
        CONNECTION_LOCK_DECREF(c);
    }
727
    event_add( c->c_read_event, c->c_read_timeout );
728

Ondřej Kuzník's avatar
Ondřej Kuzník committed
729
730
731
732
733
734
735
736
    c->c_destroy = upstream_destroy;
    CONNECTION_UNLOCK_OR_DESTROY(c);

    /* has upstream_finish() finished? */
    if ( rc == LDAP_SUCCESS ) {
        ldap_pvt_thread_mutex_unlock( &b->b_mutex );
        backend_retry( b );
        ldap_pvt_thread_mutex_lock( &b->b_mutex );
737
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
738
739

    return c;
740

Ondřej Kuzník's avatar
Ondřej Kuzník committed
741
742
743
744
745
746
747
748
749
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 );
    }
750

751
    c->c_state = LLOAD_C_INVALID;
752
753
754
    CONNECTION_DESTROY(c);
    assert( c == NULL );

Ondřej Kuzník's avatar
Ondřej Kuzník committed
755
756
757
    return NULL;
}

Ondřej Kuzník's avatar
Ondřej Kuzník committed
758
void
759
upstream_destroy( LloadConnection *c )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
760
{
761
    LloadBackend *b = c->c_private;
762
    struct event *read_event, *write_event;
763
    TAvlnode *root;
764
    long freed, executing;
765
    enum sc_state state;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
766

767
    Debug( LDAP_DEBUG_CONNS, "upstream_destroy: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
768
            "freeing connection connid=%lu\n",
769
770
            c->c_connid );

771
    assert( c->c_state != LLOAD_C_INVALID );
772
    state = c->c_state;
773
    c->c_state = LLOAD_C_INVALID;
774

775
776
    root = c->c_ops;
    c->c_ops = NULL;
777
    executing = c->c_n_ops_executing;
778
    c->c_n_ops_executing = 0;
779

780
781
    read_event = c->c_read_event;
    write_event = c->c_write_event;
782

783
784
    CONNECTION_UNLOCK_INCREF(c);

785
    freed = tavl_free( root, (AVL_FREE)operation_lost_upstream );
786
    assert( freed == executing );
787

788
789
790
791
792
    /*
     * 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
793
794
795
    if ( read_event ) {
        event_del( read_event );
    }
796

Ondřej Kuzník's avatar
Ondřej Kuzník committed
797
798
799
    if ( write_event ) {
        event_del( write_event );
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
800

801
    /* Remove from the backend on first pass */
802
    if ( state != LLOAD_C_CLOSING ) {
803
        ldap_pvt_thread_mutex_lock( &b->b_mutex );
804
        if ( c->c_type == LLOAD_C_PREPARING ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
805
806
807
            LDAP_CIRCLEQ_REMOVE( &b->b_preparing, c, c_next );
            b->b_opening--;
            b->b_failed++;
808
        } else if ( c->c_type == LLOAD_C_BIND ) {
809
            if ( c == b->b_last_bindconn ) {
810
                LloadConnection *prev =
811
812
813
814
815
816
817
                        LDAP_CIRCLEQ_LOOP_PREV( &b->b_bindconns, c, c_next );
                if ( prev == c ) {
                    b->b_last_bindconn = NULL;
                } else {
                    b->b_last_bindconn = prev;
                }
            }
818
819
820
            LDAP_CIRCLEQ_REMOVE( &b->b_bindconns, c, c_next );
            b->b_bindavail--;
        } else {
821
            if ( c == b->b_last_conn ) {
822
                LloadConnection *prev =
823
824
825
826
827
828
829
                        LDAP_CIRCLEQ_LOOP_PREV( &b->b_conns, c, c_next );
                if ( prev == c ) {
                    b->b_last_conn = NULL;
                } else {
                    b->b_last_conn = prev;
                }
            }
830
831
832
833
834
835
            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
836
    }
837

838
    CONNECTION_LOCK_DECREF(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
839

Ondřej Kuzník's avatar
Ondřej Kuzník committed
840
841
842
843
844
845
846
847
848
849
    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
850
851
852
853
854
855
856
    /*
     * 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 ) {
857
        c->c_state = LLOAD_C_CLOSING;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
858
859
860
861
862
863
        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
864
865
    connection_destroy( c );
}