~ubuntu-branches/ubuntu/dapper/postfix/dapper-security

« back to all changes in this revision

Viewing changes to src/global/scache_multi.c

  • Committer: Bazaar Package Importer
  • Author(s): LaMont Jones
  • Date: 2005-12-07 15:39:11 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20051207153911-hutf07z6i8ty25z5
Tags: 2.2.6-1
* New upstream.
  - the *SQL clients did not uniformly choose the database host from
    the available pool
  - raise the "policy violation" flag when a client request exceeds
    a concurrency or rate limit.
  - don't do smtpd_end_of_data_restrictions after the transaction
    failed due to, e.g., a write error.
  - two messages could get the same message ID due to a race
    condition. This time window was increased when queue file creation
    was postponed from MAIL FROM until the first accepted RCPT TO.  The
    window is closed again.
  - the queue manager did not write a per-recipient defer logfile record
    when the delivery agent crashed after the initial handshake with the
    queue manager, and before reporting the delivery status to the queue
    manager.
  - moved code around from one place to another to make REDIRECT, FILTER,
    HOLD and DISCARD access(5) table actions work in
    smtpd_end_of_data_restrictions.  PREPEND will not be fixed; it must
    be specified before the message content is received.
* Updated Italian translations.  Closes: #336925
* Swedish translations.  Closes: #339746
* Switch to libdb4.3.  Closes: #336488
* Add Replaces: mail-transport-agent.  Closes: #325624
* Merge changes from ubuntu.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*++
 
2
/* NAME
 
3
/*      scache_multi 3
 
4
/* SUMMARY
 
5
/*      multi-session cache
 
6
/* SYNOPSIS
 
7
/*      #include <scache.h>
 
8
/* DESCRIPTION
 
9
/*      SCACHE *scache_multi_create()
 
10
/* DESCRIPTION
 
11
/*      This module implements an in-memory, multi-session cache.
 
12
/*
 
13
/*      scache_multi_create() instantiates a session cache that
 
14
/*      stores multiple sessions.
 
15
/* DIAGNOSTICS
 
16
/*      Fatal error: memory allocation problem;
 
17
/*      panic: internal consistency failure.
 
18
/* SEE ALSO
 
19
/*      scache(3), generic session cache API
 
20
/* LICENSE
 
21
/* .ad
 
22
/* .fi
 
23
/*      The Secure Mailer license must be distributed with this software.
 
24
/* AUTHOR(S)
 
25
/*      Wietse Venema
 
26
/*      IBM T.J. Watson Research
 
27
/*      P.O. Box 704
 
28
/*      Yorktown Heights, NY 10598, USA
 
29
/*--*/
 
30
 
 
31
/* System library. */
 
32
 
 
33
#include <sys_defs.h>
 
34
#include <unistd.h>
 
35
#include <stddef.h>                     /* offsetof() */
 
36
#include <string.h>
 
37
 
 
38
/* Utility library. */
 
39
 
 
40
#include <msg.h>
 
41
#include <ring.h>
 
42
#include <htable.h>
 
43
#include <vstring.h>
 
44
#include <mymalloc.h>
 
45
#include <events.h>
 
46
 
 
47
/*#define msg_verbose 1*/
 
48
 
 
49
/* Global library. */
 
50
 
 
51
#include <scache.h>
 
52
 
 
53
/* Application-specific. */
 
54
 
 
55
 /*
 
56
  * SCACHE_MULTI is a derived type from the SCACHE super-class.
 
57
  * 
 
58
  * Each destination has an entry in the destination hash table, and each
 
59
  * destination->endpoint binding is kept in a circular list under its
 
60
  * destination hash table entry.
 
61
  * 
 
62
  * Likewise, each endpoint has an entry in the endpoint hash table, and each
 
63
  * endpoint->session binding is kept in a circular list under its endpoint
 
64
  * hash table entry.
 
65
  * 
 
66
  * We do not attempt to limit the number of destination or endpoint entries,
 
67
  * nor do we attempt to limit the number of sessions. Doing so would require
 
68
  * a write-through cache. Currently, the CTABLE cache module supports only
 
69
  * read-through caching.
 
70
  * 
 
71
  * This is no problem because the number of cached destinations is limited by
 
72
  * design. Sites that specify a wild-card domain pattern, and thus cache
 
73
  * every session in recent history, may be in for a surprise.
 
74
  */
 
75
typedef struct {
 
76
    SCACHE  scache[1];                  /* super-class */
 
77
    HTABLE *dest_cache;                 /* destination->endpoint bindings */
 
78
    HTABLE *endp_cache;                 /* endpoint->session bindings */
 
79
    int     sess_count;                 /* number of cached sessions */
 
80
} SCACHE_MULTI;
 
81
 
 
82
 /*
 
83
  * Storage for a destination or endpoint list head. Each list head knows its
 
84
  * own hash table entry name, so that we can remove the list when it becomes
 
85
  * empty. List items are stored in a circular list under the list head.
 
86
  */
 
87
typedef struct {
 
88
    RING    ring[1];                    /* circular list linkage */
 
89
    char   *parent_key;                 /* parent linkage: hash table */
 
90
    SCACHE_MULTI *cache;                /* parent linkage: cache */
 
91
} SCACHE_MULTI_HEAD;
 
92
 
 
93
#define RING_TO_MULTI_HEAD(p) RING_TO_APPL((p), SCACHE_MULTI_HEAD, ring)
 
94
 
 
95
 /*
 
96
  * Storage for a destination->endpoint binding. This is an element in a
 
97
  * circular list, whose list head specifies the destination.
 
98
  */
 
99
typedef struct {
 
100
    RING    ring[1];                    /* circular list linkage */
 
101
    SCACHE_MULTI_HEAD *head;            /* parent linkage: list head */
 
102
    char   *endp_label;                 /* endpoint name */
 
103
    char   *dest_prop;                  /* binding properties */
 
104
} SCACHE_MULTI_DEST;
 
105
 
 
106
#define RING_TO_MULTI_DEST(p) RING_TO_APPL((p), SCACHE_MULTI_DEST, ring)
 
107
 
 
108
static void scache_multi_expire_dest(int, char *);
 
109
 
 
110
 /*
 
111
  * Storage for an endpoint->session binding. This is an element in a
 
112
  * circular list, whose list head specifies the endpoint.
 
113
  */
 
114
typedef struct {
 
115
    RING    ring[1];                    /* circular list linkage */
 
116
    SCACHE_MULTI_HEAD *head;            /* parent linkage: list head */
 
117
    int     fd;                         /* cached session */
 
118
    char   *endp_prop;                  /* binding properties */
 
119
} SCACHE_MULTI_ENDP;
 
120
 
 
121
#define RING_TO_MULTI_ENDP(p) RING_TO_APPL((p), SCACHE_MULTI_ENDP, ring)
 
122
 
 
123
static void scache_multi_expire_endp(int, char *);
 
124
 
 
125
 /*
 
126
  * When deleting a circular list element, are we deleting the entire
 
127
  * circular list, or are we removing a single list element. We need this
 
128
  * distinction to avoid a re-entrancy problem between htable_delete() and
 
129
  * htable_free().
 
130
  */
 
131
#define BOTTOM_UP       1               /* one item */
 
132
#define TOP_DOWN        2               /* whole list */
 
133
 
 
134
/* scache_multi_drop_endp - destroy endpoint->session binding */
 
135
 
 
136
static void scache_multi_drop_endp(SCACHE_MULTI_ENDP *endp, int direction)
 
137
{
 
138
    const char *myname = "scache_multi_drop_endp";
 
139
    SCACHE_MULTI_HEAD *head;
 
140
 
 
141
    if (msg_verbose)
 
142
        msg_info("%s: endp_prop=%s fd=%d", myname,
 
143
                 endp->endp_prop, endp->fd);
 
144
 
 
145
    /*
 
146
     * Stop the timer.
 
147
     */
 
148
    event_cancel_timer(scache_multi_expire_endp, (char *) endp);
 
149
 
 
150
    /*
 
151
     * In bottom-up mode, remove the list head from the endpoint hash when
 
152
     * the list becomes empty. Otherwise, remove the endpoint->session
 
153
     * binding from the list.
 
154
     */
 
155
    ring_detach(endp->ring);
 
156
    head = endp->head;
 
157
    head->cache->sess_count--;
 
158
    if (direction == BOTTOM_UP && ring_pred(head->ring) == head->ring)
 
159
        htable_delete(head->cache->endp_cache, head->parent_key, myfree);
 
160
 
 
161
    /*
 
162
     * Destroy the endpoint->session binding.
 
163
     */
 
164
    if (endp->fd >= 0 && close(endp->fd) != 0)
 
165
        msg_warn("%s: close(%d): %m", myname, endp->fd);
 
166
    myfree(endp->endp_prop);
 
167
 
 
168
    myfree((char *) endp);
 
169
}
 
170
 
 
171
/* scache_multi_expire_endp - event timer call-back */
 
172
 
 
173
static void scache_multi_expire_endp(int unused_event, char *context)
 
174
{
 
175
    SCACHE_MULTI_ENDP *endp = (SCACHE_MULTI_ENDP *) context;
 
176
 
 
177
    scache_multi_drop_endp(endp, BOTTOM_UP);
 
178
}
 
179
 
 
180
/* scache_multi_free_endp - hash table destructor call-back */
 
181
 
 
182
static void scache_multi_free_endp(char *ptr)
 
183
{
 
184
    SCACHE_MULTI_HEAD *head = (SCACHE_MULTI_HEAD *) ptr;
 
185
    SCACHE_MULTI_ENDP *endp;
 
186
    RING   *ring;
 
187
 
 
188
    /*
 
189
     * Delete each endpoint->session binding in the list, then delete the
 
190
     * list head. Note: this changes the list, so we must iterate carefully.
 
191
     */
 
192
    while ((ring = ring_succ(head->ring)) != head->ring) {
 
193
        endp = RING_TO_MULTI_ENDP(ring);
 
194
        scache_multi_drop_endp(endp, TOP_DOWN);
 
195
    }
 
196
    myfree((char *) head);
 
197
}
 
198
 
 
199
/* scache_multi_save_endp - save endpoint->session binding */
 
200
 
 
201
static void scache_multi_save_endp(SCACHE *scache, int ttl,
 
202
                                           const char *endp_label,
 
203
                                           const char *endp_prop, int fd)
 
204
{
 
205
    const char *myname = "scache_multi_save_endp";
 
206
    SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
 
207
    SCACHE_MULTI_HEAD *head;
 
208
    SCACHE_MULTI_ENDP *endp;
 
209
 
 
210
    if (ttl < 0)
 
211
        msg_panic("%s: bad ttl: %d", myname, ttl);
 
212
 
 
213
    /*
 
214
     * Look up or instantiate the list head with the endpoint name.
 
215
     */
 
216
    if ((head = (SCACHE_MULTI_HEAD *)
 
217
         htable_find(sp->endp_cache, endp_label)) == 0) {
 
218
        head = (SCACHE_MULTI_HEAD *) mymalloc(sizeof(*head));
 
219
        ring_init(head->ring);
 
220
        head->parent_key =
 
221
            htable_enter(sp->endp_cache, endp_label, (char *) head)->key;
 
222
        head->cache = sp;
 
223
    }
 
224
 
 
225
    /*
 
226
     * Add the endpoint->session binding to the list. There can never be a
 
227
     * duplicate, because each session must have a different file descriptor.
 
228
     */
 
229
    endp = (SCACHE_MULTI_ENDP *) mymalloc(sizeof(*endp));
 
230
    endp->head = head;
 
231
    endp->fd = fd;
 
232
    endp->endp_prop = mystrdup(endp_prop);
 
233
    ring_prepend(head->ring, endp->ring);
 
234
    sp->sess_count++;
 
235
 
 
236
    /*
 
237
     * Make sure this binding will go away eventually.
 
238
     */
 
239
    event_request_timer(scache_multi_expire_endp, (char *) endp, ttl);
 
240
 
 
241
    if (msg_verbose)
 
242
        msg_info("%s: endp_label=%s -> endp_prop=%s fd=%d",
 
243
                 myname, endp_label, endp_prop, fd);
 
244
}
 
245
 
 
246
/* scache_multi_find_endp - look up session for named endpoint */
 
247
 
 
248
static int scache_multi_find_endp(SCACHE *scache, const char *endp_label,
 
249
                                          VSTRING *endp_prop)
 
250
{
 
251
    const char *myname = "scache_multi_find_endp";
 
252
    SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
 
253
    SCACHE_MULTI_HEAD *head;
 
254
    SCACHE_MULTI_ENDP *endp;
 
255
    RING   *ring;
 
256
    int     fd;
 
257
 
 
258
    /*
 
259
     * Look up the list head with the endpoint name.
 
260
     */
 
261
    if ((head = (SCACHE_MULTI_HEAD *)
 
262
         htable_find(sp->endp_cache, endp_label)) == 0) {
 
263
        if (msg_verbose)
 
264
            msg_info("%s: no endpoint cache: endp_label=%s",
 
265
                     myname, endp_label);
 
266
        return (-1);
 
267
    }
 
268
 
 
269
    /*
 
270
     * Use the first available session. Remove the session from the cache
 
271
     * because we're giving it to someone else.
 
272
     */
 
273
    if ((ring = ring_succ(head->ring)) != head->ring) {
 
274
        endp = RING_TO_MULTI_ENDP(ring);
 
275
        fd = endp->fd;
 
276
        endp->fd = -1;
 
277
        vstring_strcpy(endp_prop, endp->endp_prop);
 
278
        if (msg_verbose)
 
279
            msg_info("%s: found: endp_label=%s -> endp_prop=%s fd=%d",
 
280
                     myname, endp_label, endp->endp_prop, fd);
 
281
        scache_multi_drop_endp(endp, BOTTOM_UP);
 
282
        return (fd);
 
283
    }
 
284
    if (msg_verbose)
 
285
        msg_info("%s: not found: endp_label=%s", myname, endp_label);
 
286
    return (-1);
 
287
}
 
288
 
 
289
/* scache_multi_drop_dest - delete destination->endpoint binding */
 
290
 
 
291
static void scache_multi_drop_dest(SCACHE_MULTI_DEST *dest, int direction)
 
292
{
 
293
    const char *myname = "scache_multi_drop_dest";
 
294
    SCACHE_MULTI_HEAD *head;
 
295
 
 
296
    if (msg_verbose)
 
297
        msg_info("%s: dest_prop=%s endp_label=%s",
 
298
                 myname, dest->dest_prop, dest->endp_label);
 
299
 
 
300
    /*
 
301
     * Stop the timer.
 
302
     */
 
303
    event_cancel_timer(scache_multi_expire_dest, (char *) dest);
 
304
 
 
305
    /*
 
306
     * In bottom-up mode, remove the list head from the destination hash when
 
307
     * the list becomes empty. Otherwise, remove the destination->endpoint
 
308
     * binding from the list.
 
309
     */
 
310
    ring_detach(dest->ring);
 
311
    head = dest->head;
 
312
    if (direction == BOTTOM_UP && ring_pred(head->ring) == head->ring)
 
313
        htable_delete(head->cache->dest_cache, head->parent_key, myfree);
 
314
 
 
315
    /*
 
316
     * Destroy the destination->endpoint binding.
 
317
     */
 
318
    myfree(dest->dest_prop);
 
319
    myfree(dest->endp_label);
 
320
 
 
321
    myfree((char *) dest);
 
322
}
 
323
 
 
324
/* scache_multi_expire_dest - event timer call-back */
 
325
 
 
326
static void scache_multi_expire_dest(int unused_event, char *context)
 
327
{
 
328
    SCACHE_MULTI_DEST *dest = (SCACHE_MULTI_DEST *) context;
 
329
 
 
330
    scache_multi_drop_dest(dest, BOTTOM_UP);
 
331
}
 
332
 
 
333
/* scache_multi_free_dest - hash table destructor call-back */
 
334
 
 
335
static void scache_multi_free_dest(char *ptr)
 
336
{
 
337
    SCACHE_MULTI_HEAD *head = (SCACHE_MULTI_HEAD *) ptr;
 
338
    SCACHE_MULTI_DEST *dest;
 
339
    RING   *ring;
 
340
 
 
341
    /*
 
342
     * Delete each destination->endpoint binding in the list, then delete the
 
343
     * list head. Note: this changes the list, so we must iterate carefully.
 
344
     */
 
345
    while ((ring = ring_succ(head->ring)) != head->ring) {
 
346
        dest = RING_TO_MULTI_DEST(ring);
 
347
        scache_multi_drop_dest(dest, TOP_DOWN);
 
348
    }
 
349
    myfree((char *) head);
 
350
}
 
351
 
 
352
/* scache_multi_save_dest - save destination->endpoint binding */
 
353
 
 
354
static void scache_multi_save_dest(SCACHE *scache, int ttl,
 
355
                                           const char *dest_label,
 
356
                                           const char *dest_prop,
 
357
                                           const char *endp_label)
 
358
{
 
359
    const char *myname = "scache_multi_save_dest";
 
360
    SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
 
361
    SCACHE_MULTI_HEAD *head;
 
362
    SCACHE_MULTI_DEST *dest;
 
363
    RING   *ring;
 
364
    int     refresh = 0;
 
365
 
 
366
    if (ttl < 0)
 
367
        msg_panic("%s: bad ttl: %d", myname, ttl);
 
368
 
 
369
    /*
 
370
     * Look up or instantiate the list head with the destination name.
 
371
     */
 
372
    if ((head = (SCACHE_MULTI_HEAD *)
 
373
         htable_find(sp->dest_cache, dest_label)) == 0) {
 
374
        head = (SCACHE_MULTI_HEAD *) mymalloc(sizeof(*head));
 
375
        ring_init(head->ring);
 
376
        head->parent_key =
 
377
            htable_enter(sp->dest_cache, dest_label, (char *) head)->key;
 
378
        head->cache = sp;
 
379
    }
 
380
 
 
381
    /*
 
382
     * Look up or instantiate the destination->endpoint binding. Update the
 
383
     * expiration time if this destination->endpoint binding already exists.
 
384
     */
 
385
    RING_FOREACH(ring, head->ring) {
 
386
        dest = RING_TO_MULTI_DEST(ring);
 
387
        if (strcmp(dest->endp_label, endp_label) == 0
 
388
            && strcmp(dest->dest_prop, dest_prop) == 0) {
 
389
            refresh = 1;
 
390
            break;
 
391
        }
 
392
    }
 
393
    if (refresh == 0) {
 
394
        dest = (SCACHE_MULTI_DEST *) mymalloc(sizeof(*dest));
 
395
        dest->head = head;
 
396
        dest->endp_label = mystrdup(endp_label);
 
397
        dest->dest_prop = mystrdup(dest_prop);
 
398
        ring_prepend(head->ring, dest->ring);
 
399
    }
 
400
 
 
401
    /*
 
402
     * Make sure this binding will go away eventually.
 
403
     */
 
404
    event_request_timer(scache_multi_expire_dest, (char *) dest, ttl);
 
405
 
 
406
    if (msg_verbose)
 
407
        msg_info("%s: dest_label=%s -> dest_prop=%s endp_label=%s%s",
 
408
                 myname, dest_label, dest_prop, endp_label,
 
409
                 refresh ? " (refreshed)" : "");
 
410
}
 
411
 
 
412
/* scache_multi_find_dest - look up session for named destination */
 
413
 
 
414
static int scache_multi_find_dest(SCACHE *scache, const char *dest_label,
 
415
                                          VSTRING *dest_prop,
 
416
                                          VSTRING *endp_prop)
 
417
{
 
418
    const char *myname = "scache_multi_find_dest";
 
419
    SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
 
420
    SCACHE_MULTI_HEAD *head;
 
421
    SCACHE_MULTI_DEST *dest;
 
422
    RING   *ring;
 
423
    int     fd;
 
424
 
 
425
    /*
 
426
     * Look up the list head with the destination name.
 
427
     */
 
428
    if ((head = (SCACHE_MULTI_HEAD *)
 
429
         htable_find(sp->dest_cache, dest_label)) == 0) {
 
430
        if (msg_verbose)
 
431
            msg_info("%s: no destination cache: dest_label=%s",
 
432
                     myname, dest_label);
 
433
        return (-1);
 
434
    }
 
435
 
 
436
    /*
 
437
     * Search endpoints for the first available session.
 
438
     */
 
439
    RING_FOREACH(ring, head->ring) {
 
440
        dest = RING_TO_MULTI_DEST(ring);
 
441
        fd = scache_multi_find_endp(scache, dest->endp_label, endp_prop);
 
442
        if (fd >= 0) {
 
443
            vstring_strcpy(dest_prop, dest->dest_prop);
 
444
            return (fd);
 
445
        }
 
446
    }
 
447
    if (msg_verbose)
 
448
        msg_info("%s: not found: dest_label=%s", myname, dest_label);
 
449
    return (-1);
 
450
}
 
451
 
 
452
/* scache_multi_size - size of multi-element cache object */
 
453
 
 
454
static void scache_multi_size(SCACHE *scache, SCACHE_SIZE *size)
 
455
{
 
456
    SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
 
457
 
 
458
    size->dest_count = sp->dest_cache->used;
 
459
    size->endp_count = sp->endp_cache->used;
 
460
    size->sess_count = sp->sess_count;
 
461
}
 
462
 
 
463
/* scache_multi_free - destroy multi-element cache object */
 
464
 
 
465
static void scache_multi_free(SCACHE *scache)
 
466
{
 
467
    SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
 
468
 
 
469
    htable_free(sp->dest_cache, scache_multi_free_dest);
 
470
    htable_free(sp->endp_cache, scache_multi_free_endp);
 
471
 
 
472
    myfree((char *) sp);
 
473
}
 
474
 
 
475
/* scache_multi_create - initialize */
 
476
 
 
477
SCACHE *scache_multi_create(void)
 
478
{
 
479
    SCACHE_MULTI *sp = (SCACHE_MULTI *) mymalloc(sizeof(*sp));
 
480
 
 
481
    sp->scache->save_endp = scache_multi_save_endp;
 
482
    sp->scache->find_endp = scache_multi_find_endp;
 
483
    sp->scache->save_dest = scache_multi_save_dest;
 
484
    sp->scache->find_dest = scache_multi_find_dest;
 
485
    sp->scache->size = scache_multi_size;
 
486
    sp->scache->free = scache_multi_free;
 
487
 
 
488
    sp->dest_cache = htable_create(1);
 
489
    sp->endp_cache = htable_create(1);
 
490
    sp->sess_count = 0;
 
491
 
 
492
    return (sp->scache);
 
493
}