Coverage Report

Created: 2025-08-11 06:35

/src/net-snmp/snmplib/snmp_transport.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Portions of this file are copyrighted by:
3
 * Copyright (c) 2016 VMware, Inc. All rights reserved.
4
 * Use is subject to license terms specified in the COPYING file
5
 * distributed with the Net-SNMP package.
6
 */
7
#include <net-snmp/net-snmp-config.h>
8
#include <net-snmp/net-snmp-features.h>
9
10
#include <net-snmp/types.h>
11
#include <net-snmp/library/snmp_transport.h>
12
13
#include <stdio.h>
14
#ifdef HAVE_STRING_H
15
#include <string.h>
16
#else
17
#include <strings.h>
18
#endif
19
#include <sys/types.h>
20
21
#ifdef HAVE_STDLIB_H
22
#include <stdlib.h>
23
#endif
24
25
#include <ctype.h>
26
27
#ifdef HAVE_UNISTD_H
28
#include <unistd.h>
29
#endif
30
31
#include <net-snmp/output_api.h>
32
#include <net-snmp/utilities.h>
33
34
#include <net-snmp/library/default_store.h>
35
36
#include <net-snmp/library/snmpUDPDomain.h>
37
#ifdef NETSNMP_TRANSPORT_TLSBASE_DOMAIN
38
#include <net-snmp/library/snmpTLSBaseDomain.h>
39
#endif
40
#ifdef NETSNMP_TRANSPORT_TLSTCP_DOMAIN
41
#include <net-snmp/library/snmpTLSTCPDomain.h>
42
#endif
43
#ifdef NETSNMP_TRANSPORT_STD_DOMAIN
44
#include <net-snmp/library/snmpSTDDomain.h>
45
#endif
46
#ifdef NETSNMP_TRANSPORT_TCP_DOMAIN
47
#include <net-snmp/library/snmpTCPDomain.h>
48
#endif
49
#ifdef NETSNMP_TRANSPORT_DTLSUDP_DOMAIN
50
#include <net-snmp/library/snmpDTLSUDPDomain.h>
51
#endif
52
#ifdef NETSNMP_TRANSPORT_SSH_DOMAIN
53
#include <net-snmp/library/snmpSSHDomain.h>
54
#endif
55
#ifdef NETSNMP_TRANSPORT_ALIAS_DOMAIN
56
#include <net-snmp/library/snmpAliasDomain.h>
57
#endif
58
#ifdef NETSNMP_TRANSPORT_IPX_DOMAIN
59
#include <net-snmp/library/snmpIPXDomain.h>
60
#endif
61
#ifdef NETSNMP_TRANSPORT_UNIX_DOMAIN
62
#include <net-snmp/library/snmpUnixDomain.h>
63
#endif
64
#ifdef NETSNMP_TRANSPORT_AAL5PVC_DOMAIN
65
#include <net-snmp/library/snmpAAL5PVCDomain.h>
66
#endif
67
#ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN
68
#include <net-snmp/library/snmpUDPIPv6Domain.h>
69
#endif
70
#ifdef NETSNMP_TRANSPORT_TCPIPV6_DOMAIN
71
#include <net-snmp/library/snmpTCPIPv6Domain.h>
72
#endif
73
#ifdef NETSNMP_TRANSPORT_UDPSHARED_DOMAIN
74
#include <net-snmp/library/snmpUDPsharedDomain.h>
75
#endif
76
#include <net-snmp/library/snmp_api.h>
77
#include <net-snmp/library/snmp_service.h>
78
#include <net-snmp/library/read_config.h>
79
80
netsnmp_feature_child_of(transport_all, libnetsnmp);
81
82
netsnmp_feature_child_of(tdomain_support, transport_all);
83
netsnmp_feature_child_of(tdomain_transport_oid, transport_all);
84
netsnmp_feature_child_of(sockaddr_size, transport_all);
85
netsnmp_feature_child_of(transport_cache, transport_all);
86
87
/*
88
 * Our list of supported transport domains.  
89
 */
90
91
static netsnmp_tdomain *domain_list = NULL;
92
93
94
95
/*
96
 * The standard SNMP domains.  
97
 */
98
99
const oid       netsnmpUDPDomain[] = { 1, 3, 6, 1, 6, 1, 1 };
100
size_t          netsnmpUDPDomain_len = OID_LENGTH(netsnmpUDPDomain);
101
const oid       netsnmpCLNSDomain[] = { 1, 3, 6, 1, 6, 1, 2 };
102
size_t          netsnmpCLNSDomain_len = OID_LENGTH(netsnmpCLNSDomain);
103
const oid       netsnmpCONSDomain[] = { 1, 3, 6, 1, 6, 1, 3 };
104
size_t          netsnmpCONSDomain_len = OID_LENGTH(netsnmpCONSDomain);
105
const oid       netsnmpDDPDomain[] = { 1, 3, 6, 1, 6, 1, 4 };
106
size_t          netsnmpDDPDomain_len = OID_LENGTH(netsnmpDDPDomain);
107
const oid       netsnmpIPXDomain[] = { 1, 3, 6, 1, 6, 1, 5 };
108
size_t          netsnmpIPXDomain_len = OID_LENGTH(netsnmpIPXDomain);
109
110
static netsnmp_container *_container = NULL;
111
112
113
static void     netsnmp_tdomain_dump(void);
114
115
116
#if !defined(NETSNMP_FEATURE_REMOVE_FILTER_SOURCE)
117
static netsnmp_container * filtered = NULL;
118
119
void netsnmp_transport_parse_filter(const char *word, char *cptr);
120
#endif /* NETSNMP_FEATURE_REMOVE_FILTER_SOURCE */
121
122
void
123
init_snmp_transport(void)
124
3.94k
{
125
3.94k
    netsnmp_ds_register_config(ASN_BOOLEAN,
126
3.94k
                               "snmp", "dontLoadHostConfig",
127
3.94k
                               NETSNMP_DS_LIBRARY_ID,
128
3.94k
                               NETSNMP_DS_LIB_DONT_LOAD_HOST_FILES);
129
3.94k
#ifndef NETSNMP_FEATURE_REMOVE_FILTER_SOURCE
130
3.94k
    register_app_config_handler("sourceFilterType",
131
3.94k
                                netsnmp_transport_parse_filterType,
132
3.94k
                                NULL, "none|whitelist|blacklist");
133
3.94k
    register_app_config_handler("sourceFilterAddress",
134
3.94k
                                netsnmp_transport_parse_filter,
135
3.94k
                                netsnmp_transport_filter_cleanup,
136
3.94k
                                "host");
137
3.94k
#endif /* NETSNMP_FEATURE_REMOVE_FILTER_SOURCE */
138
3.94k
}
139
140
void
141
shutdown_snmp_transport(void)
142
3.94k
{
143
3.94k
#ifndef NETSNMP_FEATURE_REMOVE_FILTER_SOURCE
144
3.94k
    netsnmp_transport_filter_cleanup();
145
3.94k
#endif
146
3.94k
}
147
148
/*
149
 * Make a deep copy of an netsnmp_transport.  
150
 */
151
netsnmp_transport *
152
netsnmp_transport_copy(const netsnmp_transport *t)
153
0
{
154
0
    netsnmp_transport *n = NULL;
155
156
0
    if (t == NULL) {
157
0
        return NULL;
158
0
    }
159
160
0
    n = SNMP_MALLOC_TYPEDEF(netsnmp_transport);
161
0
    if (n == NULL) {
162
0
        return NULL;
163
0
    }
164
165
0
    if (t->domain != NULL) {
166
0
        n->domain = t->domain;
167
0
        n->domain_length = t->domain_length;
168
0
    } else {
169
0
        n->domain = NULL;
170
0
        n->domain_length = 0;
171
0
    }
172
173
0
    if (t->local != NULL) {
174
0
        n->local = netsnmp_memdup(t->local, t->local_length);
175
0
        if (n->local == NULL) {
176
0
            netsnmp_transport_free(n);
177
0
            return NULL;
178
0
        }
179
0
        n->local_length = t->local_length;
180
0
    } else {
181
0
        n->local = NULL;
182
0
        n->local_length = 0;
183
0
    }
184
185
0
    if (t->remote != NULL) {
186
0
        n->remote = netsnmp_memdup(t->remote, t->remote_length);
187
0
        if (n->remote == NULL) {
188
0
            netsnmp_transport_free(n);
189
0
            return NULL;
190
0
        }
191
0
        n->remote_length = t->remote_length;
192
0
    } else {
193
0
        n->remote = NULL;
194
0
        n->remote_length = 0;
195
0
    }
196
197
0
    if (t->data != NULL && t->data_length > 0) {
198
0
        n->data = netsnmp_memdup(t->data, t->data_length);
199
0
        if (n->data == NULL) {
200
0
            netsnmp_transport_free(n);
201
0
            return NULL;
202
0
        }
203
0
        n->data_length = t->data_length;
204
0
    } else {
205
0
        n->data = NULL;
206
0
        n->data_length = 0;
207
0
    }
208
209
0
    n->msgMaxSize = t->msgMaxSize;
210
0
    n->f_accept = t->f_accept;
211
0
    n->f_recv = t->f_recv;
212
0
    n->f_send = t->f_send;
213
0
    n->f_close = t->f_close;
214
0
    n->f_copy = t->f_copy;
215
0
    n->f_config = t->f_config;
216
0
    n->f_fmtaddr = t->f_fmtaddr;
217
0
    n->sock = t->sock;
218
0
    n->flags = t->flags;
219
0
    n->base_transport = netsnmp_transport_copy(t->base_transport);
220
221
    /* give the transport a chance to do "special things" */
222
0
    if (t->f_copy)
223
0
        t->f_copy(t, n);
224
                
225
0
    return n;
226
0
}
227
228
229
230
void
231
netsnmp_transport_free(netsnmp_transport *t)
232
12.8k
{
233
12.8k
    if (NULL == t)
234
6.40k
        return;
235
236
6.40k
#ifndef FEATURE_REMOVE_TRANSPORT_CACHE
237
    /** don't free a transport that is currently shared */
238
6.40k
    if (netsnmp_transport_cache_remove(t) == 1)
239
0
        return;
240
6.40k
#endif
241
242
6.40k
    SNMP_FREE(t->local);
243
6.40k
    SNMP_FREE(t->remote);
244
6.40k
    SNMP_FREE(t->data);
245
6.40k
    netsnmp_transport_free(t->base_transport);
246
247
6.40k
    SNMP_FREE(t);
248
6.40k
}
249
250
/*
251
 * netsnmp_transport_peer_string
252
 *
253
 * returns string representation of peer address.
254
 *
255
 * caller is responsible for freeing the allocated string.
256
 */
257
char *
258
netsnmp_transport_peer_string(netsnmp_transport *t, const void *data, int len)
259
1.74k
{
260
1.74k
    char           *str;
261
262
1.74k
    if (NULL == t)
263
0
        return NULL;
264
265
1.74k
    if (t->f_fmtaddr != NULL)
266
0
        str = t->f_fmtaddr(t, data, len);
267
1.74k
    else
268
1.74k
        str = strdup("<UNKNOWN>");
269
270
1.74k
    return str;
271
1.74k
}
272
273
#if !defined(NETSNMP_FEATURE_REMOVE_FILTER_SOURCE)
274
static int _transport_filter_init(void)
275
1
{
276
1
    if (filtered)
277
0
        return 0;
278
279
1
    filtered = netsnmp_container_find("transport_filter:cstring");
280
1
    if (NULL == filtered) {
281
0
        NETSNMP_LOGONCE((LOG_WARNING,
282
0
                         "couldn't allocate container for transport_filter list\n"));
283
0
        return -1;
284
0
    }
285
1
    filtered->container_name = strdup("transport_filter list");
286
287
1
    return 0;
288
1
}
289
290
int
291
netsnmp_transport_filter_add(const char *addrtxt)
292
262
{
293
262
    char *tmp;
294
262
    int res;
295
296
    /*
297
     * create the container, if needed
298
     */
299
262
    if (!filtered && _transport_filter_init()) {
300
0
        snmp_log(LOG_ERR,"netsnmp_transport_filter_add %s failed\n",
301
0
                 addrtxt);
302
0
        return (-1);
303
0
    }
304
262
    tmp = strdup(addrtxt);
305
262
    if (NULL == tmp) {
306
0
        snmp_log(LOG_ERR,"netsnmp_transport_filter_add strdup failed\n");
307
0
        return(-1);
308
0
    }
309
262
    res = CONTAINER_INSERT(filtered, tmp);
310
262
    if (res)
311
72
        free(tmp);
312
262
    return res;
313
262
}
314
315
int
316
netsnmp_transport_filter_remove(const char *addrtxt)
317
0
{
318
    /*
319
     * create the container, if needed
320
     */
321
0
    if (NULL == filtered)
322
0
        return -1;
323
0
    return CONTAINER_REMOVE(filtered, addrtxt);
324
0
}
325
326
/*
327
 * netsnmp_transport_filter_check
328
 *
329
 * returns 1 if the specified address string is in the filter list
330
 */
331
int
332
netsnmp_transport_filter_check(const char *addrtxt)
333
0
{
334
0
    char *addr;
335
0
    if (NULL == filtered)
336
0
        return 0;
337
0
    addr = CONTAINER_FIND(filtered, addrtxt);
338
0
    return addr ? 1 : 0;
339
0
}
340
341
void
342
netsnmp_transport_parse_filterType(const char *word, char *cptr)
343
220
{
344
220
    int type = 42;
345
220
    if (strcmp(cptr,"whitelist") == 0)
346
18
        type = 1;
347
202
    else if (strcmp(cptr,"blacklist") == 0)
348
10
        type = -1;
349
192
    else if (strcmp(cptr,"none") == 0)
350
10
        type = 0;
351
182
    else
352
182
        netsnmp_config_error("unknown source filter type: %s", cptr);
353
354
220
    if (type != 42) {
355
38
        DEBUGMSGTL(("transport:filterType", "set to %d\n", type));
356
38
        netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID,
357
38
                           NETSNMP_DS_LIB_FILTER_TYPE, type);
358
38
    }
359
220
}
360
361
void
362
netsnmp_transport_parse_filter(const char *word, char *cptr)
363
262
{
364
262
    if (netsnmp_transport_filter_add(cptr))
365
72
        netsnmp_config_error("cannot create source filter: %s", cptr);
366
262
}
367
368
void
369
netsnmp_transport_filter_cleanup(void)
370
13.5k
{
371
13.5k
    if (NULL == filtered)
372
13.5k
        return;
373
0
    CONTAINER_CLEAR(filtered, filtered->free_item, NULL);
374
0
    CONTAINER_FREE(filtered);
375
0
    filtered = NULL;
376
0
}
377
#endif /* NETSNMP_FEATURE_REMOVE_FILTER_SOURCE */
378
379
380
#ifndef NETSNMP_FEATURE_REMOVE_SOCKADDR_SIZE
381
int
382
netsnmp_sockaddr_size(const struct sockaddr *sa)
383
0
{
384
0
    if (NULL == sa)
385
0
        return 0;
386
387
0
    switch (sa->sa_family) {
388
0
        case AF_INET:
389
0
            return sizeof(struct sockaddr_in);
390
0
        break;
391
0
#ifdef NETSNMP_ENABLE_IPV6
392
0
        case AF_INET6:
393
0
            return sizeof(struct sockaddr_in6);
394
0
            break;
395
0
#endif
396
0
    }
397
398
0
    return 0;
399
0
}
400
#endif /* NETSNMP_FEATURE_REMOVE_SOCKADDR_SIZE */
401
    
402
int
403
netsnmp_transport_send(netsnmp_transport *t, const void *packet, int length,
404
                       void **opaque, int *olength)
405
0
{
406
0
    int dumpPacket, debugLength;
407
408
0
    if ((NULL == t) || (NULL == t->f_send)) {
409
0
        DEBUGMSGTL(("transport:pkt:send", "NULL transport or send function\n"));
410
0
        return SNMPERR_GENERR;
411
0
    }
412
413
0
    dumpPacket = netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
414
0
                                        NETSNMP_DS_LIB_DUMP_PACKET);
415
0
    debugLength = (SNMPERR_SUCCESS ==
416
0
                   debug_is_token_registered("transport:send"));
417
418
0
    if (dumpPacket | debugLength) {
419
0
        char *str = netsnmp_transport_peer_string(t,
420
0
                                                  opaque ? *opaque : NULL,
421
0
                                                  olength ? *olength : 0);
422
0
        if (debugLength)
423
0
            DEBUGMSGT_NC(("transport:send","%lu bytes to %s\n",
424
0
                          (unsigned long)length, str));
425
0
        if (dumpPacket)
426
0
            snmp_log(LOG_DEBUG, "\nSending %lu bytes to %s\n", 
427
0
                     (unsigned long)length, str);
428
0
        SNMP_FREE(str);
429
0
    }
430
0
    if (dumpPacket)
431
0
        xdump(packet, length, "");
432
433
0
    return t->f_send(t, packet, length, opaque, olength);
434
0
}
435
436
int
437
netsnmp_transport_recv(netsnmp_transport *t, void *packet, int length,
438
                       void **opaque, int *olength)
439
49.3k
{
440
49.3k
    int debugLength;
441
442
49.3k
    if ((NULL == t) || (NULL == t->f_recv)) {
443
0
        DEBUGMSGTL(("transport:recv", "NULL transport or recv function\n"));
444
0
        return SNMPERR_GENERR;
445
0
    }
446
447
49.3k
    length = t->f_recv(t, packet, length, opaque, olength);
448
449
49.3k
    if (length <=0)
450
47.5k
        return length; /* don't log timeouts/socket closed */
451
452
1.74k
    debugLength = (SNMPERR_SUCCESS ==
453
1.74k
                   debug_is_token_registered("transport:recv"));
454
455
1.74k
    if (debugLength) {
456
1.74k
        char *str = netsnmp_transport_peer_string(t,
457
1.74k
                                                  opaque ? *opaque : NULL,
458
1.74k
                                                  olength ? *olength : 0);
459
1.74k
        DEBUGMSGT_NC(("transport:recv","%d bytes from %s\n", length, str));
460
1.74k
        SNMP_FREE(str);
461
1.74k
    }
462
463
1.74k
    return length;
464
49.3k
}
465
466
467
468
#ifndef NETSNMP_FEATURE_REMOVE_TDOMAIN_SUPPORT
469
int
470
netsnmp_tdomain_support(const oid * in_oid,
471
                        size_t in_len,
472
                        const oid ** out_oid, size_t * out_len)
473
0
{
474
0
    netsnmp_tdomain *d = NULL;
475
476
0
    for (d = domain_list; d != NULL; d = d->next) {
477
0
        if (netsnmp_oid_equals(in_oid, in_len, d->name, d->name_length) == 0) {
478
0
            if (out_oid != NULL && out_len != NULL) {
479
0
                *out_oid = d->name;
480
0
                *out_len = d->name_length;
481
0
            }
482
0
            return 1;
483
0
        }
484
0
    }
485
0
    return 0;
486
0
}
487
#endif /* NETSNMP_FEATURE_REMOVE_TDOMAIN_SUPPORT */
488
489
490
void
491
netsnmp_tdomain_init(void)
492
3.94k
{
493
3.94k
    DEBUGMSGTL(("tdomain", "netsnmp_tdomain_init() called\n"));
494
495
/* include the configure generated list of constructor calls */
496
3.94k
#include "transports/snmp_transport_inits.h"
497
498
3.94k
    netsnmp_tdomain_dump();
499
500
501
3.94k
}
502
503
void
504
netsnmp_clear_tdomain_list(void)
505
7.14k
{
506
7.14k
    netsnmp_tdomain *list = domain_list, *next = NULL;
507
7.14k
    DEBUGMSGTL(("tdomain", "clear_tdomain_list() called\n"));
508
509
50.4k
    while (list != NULL) {
510
43.3k
  next = list->next;
511
43.3k
  SNMP_FREE(list->prefix);
512
        /* attention!! list itself is not in the heap, so we must not free it! */
513
43.3k
  list = next;
514
43.3k
    }
515
7.14k
    domain_list = NULL;
516
7.14k
}
517
518
519
static void
520
netsnmp_tdomain_dump(void)
521
3.94k
{
522
3.94k
    netsnmp_tdomain *d;
523
3.94k
    int i = 0;
524
525
3.94k
    DEBUGMSGTL(("tdomain", "domain_list -> "));
526
47.3k
    for (d = domain_list; d != NULL; d = d->next) {
527
43.3k
        DEBUGMSG(("tdomain", "{ "));
528
43.3k
        DEBUGMSGOID(("tdomain", d->name, d->name_length));
529
43.3k
        DEBUGMSG(("tdomain", ", \""));
530
126k
        for (i = 0; d->prefix[i] != NULL; i++) {
531
82.7k
            DEBUGMSG(("tdomain", "%s%s", d->prefix[i],
532
82.7k
          (d->prefix[i + 1]) ? "/" : ""));
533
82.7k
        }
534
43.3k
        DEBUGMSG(("tdomain", "\" } -> "));
535
43.3k
    }
536
3.94k
    DEBUGMSG(("tdomain", "[NIL]\n"));
537
3.94k
}
538
539
540
541
int
542
netsnmp_tdomain_register(netsnmp_tdomain *n)
543
43.3k
{
544
43.3k
    netsnmp_tdomain **prevNext = &domain_list, *d;
545
546
43.3k
    if (n != NULL) {
547
260k
        for (d = domain_list; d != NULL; d = d->next) {
548
216k
            if (netsnmp_oid_equals(n->name, n->name_length,
549
216k
                                d->name, d->name_length) == 0) {
550
                /*
551
                 * Already registered.  
552
                 */
553
0
                return 0;
554
0
            }
555
216k
            prevNext = &(d->next);
556
216k
        }
557
43.3k
        n->next = NULL;
558
43.3k
        *prevNext = n;
559
43.3k
        return 1;
560
43.3k
    } else {
561
0
        return 0;
562
0
    }
563
43.3k
}
564
565
566
567
netsnmp_feature_child_of(tdomain_unregister, netsnmp_unused);
568
#ifndef NETSNMP_FEATURE_REMOVE_TDOMAIN_UNREGISTER
569
int
570
netsnmp_tdomain_unregister(netsnmp_tdomain *n)
571
0
{
572
0
    netsnmp_tdomain **prevNext = &domain_list, *d;
573
574
0
    if (n != NULL) {
575
0
        for (d = domain_list; d != NULL; d = d->next) {
576
0
            if (netsnmp_oid_equals(n->name, n->name_length,
577
0
                                d->name, d->name_length) == 0) {
578
0
                *prevNext = n->next;
579
0
    SNMP_FREE(n->prefix);
580
0
                return 1;
581
0
            }
582
0
            prevNext = &(d->next);
583
0
        }
584
0
        return 0;
585
0
    } else {
586
0
        return 0;
587
0
    }
588
0
}
589
#endif /* NETSNMP_FEATURE_REMOVE_TDOMAIN_UNREGISTER */
590
591
592
static netsnmp_tdomain *
593
find_tdomain(const char* spec)
594
3.20k
{
595
3.20k
    netsnmp_tdomain *d;
596
25.6k
    for (d = domain_list; d != NULL; d = d->next) {
597
25.6k
        int i;
598
80.0k
        for (i = 0; d->prefix[i] != NULL; i++)
599
57.6k
            if (strcasecmp(d->prefix[i], spec) == 0) {
600
3.20k
                DEBUGMSGTL(("tdomain",
601
3.20k
                            "Found domain \"%s\" from specifier \"%s\"\n",
602
3.20k
                            d->prefix[0], spec));
603
3.20k
                return d;
604
3.20k
            }
605
25.6k
    }
606
0
    DEBUGMSGTL(("tdomain", "Found no domain from specifier \"%s\"\n", spec));
607
0
    return NULL;
608
3.20k
}
609
610
static int
611
netsnmp_is_fqdn(const char *thename)
612
3.20k
{
613
3.20k
    if (!thename)
614
0
        return 0;
615
3.20k
    while(*thename) {
616
0
        if (*thename != '.' && !isupper((unsigned char)*thename) &&
617
0
            !islower((unsigned char)*thename) &&
618
0
            !isdigit((unsigned char)*thename) && *thename != '-') {
619
0
            return 0;
620
0
        }
621
0
        thename++;
622
0
    }
623
3.20k
    return 1;
624
3.20k
}
625
626
/*
627
 * Locate the appropriate transport domain and call the create function for
628
 * it.
629
 */
630
netsnmp_transport *
631
netsnmp_tdomain_transport_tspec(netsnmp_tdomain_spec *tspec)
632
3.20k
{
633
3.20k
    const char *application, *str, *default_domain, *default_target, *source;
634
3.20k
    int local;
635
3.20k
    netsnmp_tdomain    *match = NULL;
636
3.20k
    const char         *addr = NULL;
637
3.20k
    const char * const *spec = NULL;
638
3.20k
    int                 any_found = 0;
639
3.20k
    char buf[SNMP_MAXPATH];
640
3.20k
    const char **lspec = NULL;
641
3.20k
    char *tokenized_domain = NULL;
642
643
3.20k
    application = tspec->application;
644
3.20k
    str = tspec->target;
645
3.20k
    local = tspec->flags & NETSNMP_TSPEC_LOCAL;
646
3.20k
    default_domain = tspec->default_domain;
647
3.20k
    default_target = tspec->default_target;
648
3.20k
    source = tspec->source;
649
    /** transport_config = tspec->transport_config; not used yet */
650
651
3.20k
    DEBUGMSGTL(("tdomain",
652
3.20k
                "tdomain_transport_spec(\"%s\", \"%s\", %d, \"%s\", \"%s\", \"%s\")\n",
653
3.20k
                application, str ? str : "[NIL]", local,
654
3.20k
                default_domain ? default_domain : "[NIL]",
655
3.20k
                default_target ? default_target : "[NIL]",
656
3.20k
                source ? source : "[NIL]"));
657
658
    /* see if we can load a host-name specific set of conf files */
659
3.20k
    if (!netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
660
3.20k
                                NETSNMP_DS_LIB_DONT_LOAD_HOST_FILES) &&
661
3.20k
        netsnmp_is_fqdn(str)) {
662
3.20k
        static int have_added_handler = 0;
663
3.20k
        char *newhost;
664
3.20k
        struct config_line *config_handlers;
665
3.20k
        struct config_files file_names;
666
3.20k
        char *prev_hostname;
667
668
        /* register a "transport" specifier */
669
3.20k
        if (!have_added_handler) {
670
1
            have_added_handler = 1;
671
1
            netsnmp_ds_register_config(ASN_OCTET_STR,
672
1
                                       "snmp", "transport",
673
1
                                       NETSNMP_DS_LIBRARY_ID,
674
1
                                       NETSNMP_DS_LIB_HOSTNAME);
675
1
        }
676
677
        /* we save on specific setting that we don't allow to change
678
           from one transport creation to the next; ie, we don't want
679
           the "transport" specifier to be a default.  It should be a
680
           single invocation use only */
681
3.20k
        prev_hostname = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
682
3.20k
                                              NETSNMP_DS_LIB_HOSTNAME);
683
3.20k
        if (prev_hostname)
684
0
            prev_hostname = strdup(prev_hostname);
685
686
        /* read in the hosts/STRING.conf files */
687
3.20k
        config_handlers = read_config_get_handlers("snmp");
688
3.20k
        snprintf(buf, sizeof(buf)-1, "hosts/%s", str);
689
3.20k
        file_names.fileHeader = buf;
690
3.20k
        file_names.start = config_handlers;
691
3.20k
        file_names.next = NULL;
692
3.20k
        DEBUGMSGTL(("tdomain", "checking for host specific config %s\n",
693
3.20k
                    buf));
694
3.20k
        read_config_files_of_type(EITHER_CONFIG, &file_names);
695
696
3.20k
        if (NULL !=
697
3.20k
            (newhost = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
698
3.20k
                                             NETSNMP_DS_LIB_HOSTNAME))) {
699
0
            strlcpy(buf, newhost, sizeof(buf));
700
0
            str = buf;
701
0
        }
702
703
3.20k
        netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
704
3.20k
                              NETSNMP_DS_LIB_HOSTNAME,
705
3.20k
                              prev_hostname);
706
3.20k
        SNMP_FREE(prev_hostname);
707
3.20k
    }
708
709
    /* First try - assume that there is a domain in str (domain:target) */
710
711
3.20k
    if (str != NULL) {
712
3.20k
        const char *cp;
713
3.20k
        if ((cp = strchr(str, ':')) != NULL) {
714
0
            char* mystring = (char*)malloc(cp + 1 - str);
715
0
            if (mystring == NULL)
716
0
                return NULL;
717
0
            memcpy(mystring, str, cp - str);
718
0
            mystring[cp - str] = '\0';
719
0
            addr = cp + 1;
720
721
0
            match = find_tdomain(mystring);
722
0
            free(mystring);
723
0
        }
724
3.20k
    }
725
726
    /*
727
     * Second try, if there is no domain in str (target), then try the
728
     * default domain
729
     */
730
731
3.20k
    if (match == NULL) {
732
3.20k
        addr = str;
733
3.20k
        if (addr && *addr == '/') {
734
0
            DEBUGMSGTL(("tdomain",
735
0
                        "Address starts with '/', so assume \"unix\" "
736
0
                        "domain\n"));
737
0
            match = find_tdomain("unix");
738
3.20k
        } else if (default_domain) {
739
0
            DEBUGMSGTL(("tdomain",
740
0
                        "Use user specified default domain \"%s\"\n",
741
0
                        default_domain));
742
0
            if (!strchr(default_domain, ','))
743
0
                match = find_tdomain(default_domain);
744
0
            else {
745
0
                int commas = 0;
746
0
                const char *cp = default_domain;
747
0
                char *ptr = NULL;
748
0
                tokenized_domain = strdup(default_domain);
749
0
                if (!tokenized_domain)
750
0
                    return NULL;
751
752
0
                while (*++cp) if (*cp == ',') commas++;
753
0
                lspec = calloc(commas+2, sizeof(char *));
754
0
                if (!lspec) {
755
0
                    free(tokenized_domain);
756
0
                    return NULL;
757
0
                }
758
0
                commas = 1;
759
0
                lspec[0] = strtok_r(tokenized_domain, ",", &ptr);
760
0
                while ((lspec[commas++] = strtok_r(NULL, ",", &ptr)))
761
0
                    ;
762
0
                spec = lspec;
763
0
            }
764
3.20k
        } else {
765
3.20k
            spec = netsnmp_lookup_default_domains(application);
766
3.20k
            if (spec == NULL) {
767
0
                DEBUGMSGTL(("tdomain",
768
0
                            "No default domain found, assume \"udp\"\n"));
769
0
                match = find_tdomain("udp");
770
3.20k
            } else {
771
3.20k
                const char * const * r = spec;
772
3.20k
                DEBUGMSGTL(("tdomain",
773
3.20k
                            "Use application default domains"));
774
9.60k
                while(*r) {
775
6.40k
                    DEBUGMSG(("tdomain", " \"%s\"", *r));
776
6.40k
                    ++r;
777
6.40k
                }
778
3.20k
                DEBUGMSG(("tdomain", "\n"));
779
3.20k
            }
780
3.20k
        }
781
3.20k
    }
782
783
6.40k
    for(;;) {
784
6.40k
        if (match) {
785
3.20k
            netsnmp_transport *t = NULL;
786
3.20k
            const char* addr2;
787
788
3.20k
            any_found = 1;
789
            /*
790
             * Ok, we know what domain to try, lets see what default data
791
             * should be used with it
792
             */
793
3.20k
            if (default_target != NULL)
794
0
                addr2 = default_target;
795
3.20k
            else
796
3.20k
                addr2 = netsnmp_lookup_default_target(application,
797
3.20k
                                                      match->prefix[0]);
798
3.20k
            DEBUGMSGTL(("tdomain",
799
3.20k
                        "trying domain \"%s\" address \"%s\" "
800
3.20k
                        "default address \"%s\"\n",
801
3.20k
                        match->prefix[0], addr ? addr : "[NIL]",
802
3.20k
                        addr2 ? addr2 : "[NIL]"));
803
3.20k
            if (match->f_create_from_tspec) {
804
3.20k
                netsnmp_tdomain_spec tspec_tmp;
805
3.20k
                memcpy(&tspec_tmp, tspec, sizeof(tspec_tmp));
806
                /** if we didn't have a default target but looked one up,
807
                 *  copy the spec and use the found default. */
808
3.20k
                if ((default_target == NULL) && (addr2 != NULL))
809
3.20k
                    tspec_tmp.default_target = addr2;
810
3.20k
                if (addr != tspec_tmp.target)
811
0
                    tspec_tmp.target = addr;
812
3.20k
                t = match->f_create_from_tspec(&tspec_tmp);
813
3.20k
            }
814
0
            else {
815
#if 0 /** remove warning until all transports implement tspec */
816
                NETSNMP_LOGONCE((LOG_WARNING,
817
                                 "transport domain %s uses deprecated f_create function\n",
818
                                 match->prefix[0]));
819
#endif
820
0
                if (match->f_create_from_tstring) {
821
0
                    t = match->f_create_from_tstring(addr, local);
822
0
                }
823
0
                else
824
0
                    t = match->f_create_from_tstring_new(addr, local, addr2);
825
0
            }
826
3.20k
            if (t) {
827
3.20k
                if (lspec) {
828
0
                    free(tokenized_domain);
829
0
                    free(lspec);
830
0
                }
831
3.20k
                return t;
832
3.20k
            }
833
3.20k
        }
834
3.20k
        addr = str;
835
3.20k
        if (spec && *spec)
836
3.20k
            match = find_tdomain(*spec++);
837
0
        else
838
0
            break;
839
3.20k
    }
840
0
    if (!any_found)
841
0
        snmp_log(LOG_ERR, "No support for any checked transport domain\n");
842
0
    if (lspec) {
843
0
        free(tokenized_domain);
844
0
        free(lspec);
845
0
    }
846
0
    return NULL;
847
3.20k
}
848
849
netsnmp_transport *
850
netsnmp_tdomain_transport_full(const char *application,
851
                               const char *str, int local,
852
                               const char *default_domain,
853
                               const char *default_target)
854
3.20k
{
855
3.20k
    netsnmp_tdomain_spec tspec;
856
3.20k
    memset(&tspec, 0x0, sizeof(tspec));
857
3.20k
    tspec.application = application;
858
3.20k
    tspec.target = str;
859
3.20k
    if (local)
860
3.20k
        tspec.flags |= NETSNMP_TSPEC_LOCAL;
861
3.20k
    tspec.default_domain = default_domain;
862
3.20k
    tspec.default_target = default_target;
863
3.20k
    tspec.source = NULL;
864
3.20k
    tspec.transport_config = NULL;
865
3.20k
    return netsnmp_tdomain_transport_tspec(&tspec);
866
3.20k
}
867
868
netsnmp_transport *
869
netsnmp_tdomain_transport(const char *str, int local,
870
        const char *default_domain)
871
0
{
872
0
    netsnmp_tdomain_spec tspec;
873
0
    memset(&tspec, 0x0, sizeof(tspec));
874
0
    tspec.application = "snmp";
875
0
    tspec.target = str;
876
0
    if (local)
877
0
        tspec.flags |= NETSNMP_TSPEC_LOCAL;
878
0
    tspec.default_domain = default_domain;
879
0
    tspec.default_target = NULL;
880
0
    tspec.source = NULL;
881
0
    tspec.transport_config = NULL;
882
0
    return netsnmp_tdomain_transport_tspec(&tspec);
883
0
}
884
885
#ifndef NETSNMP_FEATURE_REMOVE_TDOMAIN_TRANSPORT_OID
886
/*
887
 * The format of @dom and @o follows the TDomain and TAddress textual
888
 * conventions from RFC 2579. For the actual TAddress format definitions,
889
 * see e.g. SnmpUDPAddress in RFC 1906.
890
 */
891
netsnmp_transport *
892
netsnmp_tdomain_transport_oid(const oid * dom,
893
                              size_t dom_len,
894
                              const u_char * o, size_t o_len, int local)
895
0
{
896
0
    netsnmp_tdomain *d;
897
0
    int             i;
898
899
0
    DEBUGMSGTL(("tdomain", "domain \""));
900
0
    DEBUGMSGOID(("tdomain", dom, dom_len));
901
0
    DEBUGMSG(("tdomain", "\"\n"));
902
903
0
    for (d = domain_list; d != NULL; d = d->next) {
904
0
        for (i = 0; d->prefix[i] != NULL; i++) {
905
0
            if (netsnmp_oid_equals(dom, dom_len, d->name, d->name_length) ==
906
0
                0) {
907
0
                return d->f_create_from_ostring(o, o_len, local);
908
0
            }
909
0
        }
910
0
    }
911
912
0
    snmp_log(LOG_ERR, "No support for requested transport domain\n");
913
0
    return NULL;
914
0
}
915
#endif /* NETSNMP_FEATURE_REMOVE_TDOMAIN_TRANSPORT_OID */
916
917
netsnmp_transport*
918
netsnmp_transport_open(const char* application, const char* str, int local)
919
0
{
920
0
    return netsnmp_tdomain_transport_full(application, str, local, NULL, NULL);
921
0
}
922
923
netsnmp_transport*
924
netsnmp_transport_open_server(const char* application, const char* str)
925
3.20k
{
926
3.20k
    return netsnmp_tdomain_transport_full(application, str, 1, NULL, NULL);
927
3.20k
}
928
929
netsnmp_transport*
930
netsnmp_transport_open_client(const char* application, const char* str)
931
0
{
932
0
    return netsnmp_tdomain_transport_full(application, str, 0, NULL, NULL);
933
0
}
934
935
/** adds a transport to a linked list of transports.
936
    Returns 1 on failure, 0 on success */
937
int
938
netsnmp_transport_add_to_list(netsnmp_transport_list **transport_list,
939
                              netsnmp_transport *transport)
940
3.20k
{
941
3.20k
    netsnmp_transport_list *newptr =
942
3.20k
        SNMP_MALLOC_TYPEDEF(netsnmp_transport_list);
943
944
3.20k
    if (!newptr)
945
0
        return 1;
946
947
3.20k
    newptr->next = *transport_list;
948
3.20k
    newptr->transport = transport;
949
950
3.20k
    *transport_list = newptr;
951
952
3.20k
    return 0;
953
3.20k
}
954
955
956
/**  removes a transport from a linked list of transports.
957
     Returns 1 on failure, 0 on success */
958
int
959
netsnmp_transport_remove_from_list(netsnmp_transport_list **transport_list,
960
                                   netsnmp_transport *transport)
961
3.20k
{
962
3.20k
    netsnmp_transport_list *ptr = *transport_list, *lastptr = NULL;
963
964
3.20k
    while (ptr && ptr->transport != transport) {
965
0
        lastptr = ptr;
966
0
        ptr = ptr->next;
967
0
    }
968
969
3.20k
    if (!ptr)
970
0
        return 1;
971
972
3.20k
    if (lastptr)
973
0
        lastptr->next = ptr->next;
974
3.20k
    else
975
3.20k
        *transport_list = ptr->next;
976
977
3.20k
    SNMP_FREE(ptr);
978
979
3.20k
    return 0;
980
3.20k
}
981
982
int
983
netsnmp_transport_config_compare(const void *p, const void *q)
984
0
{
985
0
    const netsnmp_transport_config *left = p, *right = q;
986
987
0
    return strcmp(left->key, right->key);
988
0
}
989
990
netsnmp_transport_config *
991
netsnmp_transport_create_config(const char *key, const char *value)
992
54
{
993
54
    netsnmp_transport_config *entry =
994
54
        SNMP_MALLOC_TYPEDEF(netsnmp_transport_config);
995
54
    if (!entry)
996
0
        return NULL;
997
54
    entry->key = strdup(key);
998
54
    entry->value = strdup(value);
999
54
    if (!entry->key || !entry->value) {
1000
0
        free(entry->key);
1001
0
        free(entry->value);
1002
0
        free(entry);
1003
0
        return NULL;
1004
0
    }
1005
54
    return entry;
1006
54
}
1007
1008
#ifndef FEATURE_REMOVE_TRANSPORT_CACHE
1009
1010
/* *************************************************************************
1011
 * transport caching by address family, type and use
1012
 */
1013
typedef struct trans_cache_s {
1014
    netsnmp_transport *t;
1015
    int af;
1016
    int type;
1017
    int local;
1018
    netsnmp_sockaddr_storage bind_addr;
1019
    int count; /* number of times this transport has been returned */
1020
} trans_cache;
1021
1022
static void _tc_free_item(void *tc, void *context);
1023
static int _tc_compare(const void *p, const void *q);
1024
1025
/** initialize transport cache */
1026
static int
1027
_tc_init(void)
1028
0
{
1029
0
    DEBUGMSGTL(("transport:cache:init", "%p\n", _container));
1030
1031
    /** prevent double init */
1032
0
    if (NULL != _container)
1033
0
        return 0;
1034
1035
0
    _container = netsnmp_container_find("trans_cache:binary_array");
1036
0
    if (NULL == _container) {
1037
0
        snmp_log(LOG_ERR, "failed to allocate trans_cache container\n");
1038
0
        return 1;
1039
0
    }
1040
1041
0
    _container->container_name = strdup("trans_cache");
1042
0
    _container->free_item = _tc_free_item;
1043
0
    _container->compare = _tc_compare;
1044
1045
0
    return 0;
1046
0
}
1047
1048
/*
1049
 * container compare function
1050
 *
1051
 * sort by af, type, local
1052
 */
1053
static int
1054
_tc_compare(const void *p, const void *q)
1055
0
{
1056
0
    const trans_cache *lhs = p, *rhs = q;
1057
1058
0
    netsnmp_assert((lhs != NULL) && (rhs != NULL));
1059
1060
0
    DEBUGMSGTL(("9:transport:cache:compare", "%p/%p\n", lhs, rhs));
1061
1062
0
   if (lhs->af < rhs->af)
1063
0
        return -1;
1064
0
    else if (lhs->af > rhs->af)
1065
0
        return 1;
1066
1067
0
    if (lhs->type < rhs->type)
1068
0
        return -1;
1069
0
    else if (lhs->type > rhs->type)
1070
0
        return 1;
1071
1072
0
    if (lhs->local < rhs->local)
1073
0
        return -1;
1074
0
    else if (lhs->local > rhs->local)
1075
0
        return 1;
1076
1077
0
    if (AF_INET == lhs->af) {
1078
0
        const struct sockaddr_in *lha = &lhs->bind_addr.sin,
1079
0
            *rha = &rhs->bind_addr.sin;
1080
0
        if (lha->sin_addr.s_addr < rha->sin_addr.s_addr)
1081
0
            return -1;
1082
0
        else if (lha->sin_addr.s_addr > rha->sin_addr.s_addr)
1083
0
            return 1;
1084
1085
0
        if (lha->sin_port < rha->sin_port)
1086
0
            return -1;
1087
0
        else if (lha->sin_port > rha->sin_port)
1088
0
            return 1;
1089
0
    }
1090
0
#ifdef NETSNMP_ENABLE_IPV6
1091
0
    else if (AF_INET6 == lhs->af) {
1092
0
        const struct sockaddr_in6 *lha = &lhs->bind_addr.sin6,
1093
0
            *rha = &rhs->bind_addr.sin6;
1094
0
        int rc = memcmp(lha->sin6_addr.s6_addr, rha->sin6_addr.s6_addr,
1095
0
                        sizeof(rha->sin6_addr.s6_addr));
1096
0
        if (rc)
1097
0
            return rc;
1098
1099
0
        if (lha->sin6_port < rha->sin6_port)
1100
0
            return -1;
1101
0
        else if (lha->sin6_port > rha->sin6_port)
1102
0
            return 1;
1103
1104
0
        if (lha->sin6_flowinfo < rha->sin6_flowinfo)
1105
0
            return -1;
1106
0
        else if (lha->sin6_flowinfo > rha->sin6_flowinfo)
1107
0
            return 1;
1108
1109
0
        if (lha->sin6_scope_id < rha->sin6_scope_id)
1110
0
            return -1;
1111
0
        else if (lha->sin6_scope_id > rha->sin6_scope_id)
1112
0
            return 1;
1113
0
    }
1114
0
#endif
1115
0
    return 0;
1116
0
}
1117
1118
static void
1119
_tc_free(trans_cache *tc)
1120
0
{
1121
0
    if (NULL == tc)
1122
0
        return;
1123
1124
0
    DEBUGMSGTL(("transport:cache:free", "%p %d/%d/%d/%p %d\n", tc, tc->af,
1125
0
                tc->type, tc->local, tc->t, tc->count));
1126
0
    netsnmp_transport_free(tc->t);
1127
0
    memset(tc, 0x0, sizeof(*tc));
1128
0
    free(tc);
1129
0
}
1130
1131
static void
1132
_tc_free_item(void *tc, void *context)
1133
0
{
1134
0
    _tc_free(tc);
1135
0
}
1136
1137
static void
1138
_tc_remove(trans_cache *tc)
1139
0
{
1140
0
    if (NULL == tc || NULL == _container)
1141
0
        return;
1142
1143
0
    DEBUGMSGTL(("transport:cache:remove", "%p\n", tc));
1144
1145
0
    CONTAINER_REMOVE(_container, tc);
1146
0
}
1147
1148
static trans_cache *
1149
_tc_create(int af, int type, int local, const netsnmp_sockaddr_storage *addr,
1150
           unsigned addr_size, netsnmp_transport *t)
1151
0
{
1152
0
    trans_cache *tc = SNMP_MALLOC_TYPEDEF(trans_cache);
1153
1154
0
    if (NULL == tc) {
1155
0
        snmp_log(LOG_ERR, "failed to allocate trans_cache\n");
1156
0
        return NULL;
1157
0
    }
1158
0
    DEBUGMSGTL(("transport:cache:create", "%p\n", tc));
1159
0
    tc->af = af;
1160
0
    tc->type = type;
1161
0
    tc->local = local;
1162
0
    tc->t = t;
1163
0
    if (addr)
1164
0
        memcpy(&tc->bind_addr, addr, addr_size);
1165
    /** we only understand ipv6 and ipv6 sockaddrs in compare */
1166
0
    if (AF_INET != tc->af && AF_INET6 != tc->af)
1167
0
        NETSNMP_LOGONCE((LOG_WARNING, "transport cache not tested for af %d\n",
1168
0
                         tc->af));
1169
0
    return tc;
1170
0
}
1171
1172
static trans_cache *
1173
_tc_add(int af, int type, int local, const netsnmp_sockaddr_storage *addr,
1174
        unsigned addr_size, netsnmp_transport *t)
1175
0
{
1176
0
    trans_cache *tc;
1177
0
    int rc;
1178
1179
0
    DEBUGMSGTL(("transport:cache:add", "%d/%d/%d/%p\n", af, type, local, t));
1180
1181
0
    if (NULL == _container) {
1182
0
        _tc_init();
1183
0
        if (NULL == _container)
1184
0
            return NULL;
1185
0
    }
1186
1187
0
    tc = _tc_create(af, type, local, addr, addr_size, t);
1188
0
    if (NULL == tc) {
1189
0
        DEBUGMSGTL(("transport:cache:add",
1190
0
                    "could not create transport cache\n"));
1191
0
        return NULL;
1192
0
    }
1193
1194
0
    rc = CONTAINER_INSERT(_container, tc);
1195
0
    if (rc) {
1196
0
        DEBUGMSGTL(("transport:cache:add", "container insert failed\n"));
1197
0
        _tc_free(tc);
1198
0
        return NULL;
1199
0
    }
1200
1201
0
    return tc;
1202
0
}
1203
1204
trans_cache *
1205
_tc_find(int af, int type, int local, const netsnmp_sockaddr_storage *addr,
1206
         unsigned addr_size)
1207
0
{
1208
0
    trans_cache tc, *rtn;
1209
1210
0
    DEBUGMSGTL(("transport:cache:find", "%d/%d/%d\n", af, type, local));
1211
1212
0
    if (NULL == _container)
1213
0
        return NULL;
1214
1215
0
    memset(&tc, 0x00, sizeof(tc));
1216
0
    tc.af = af;
1217
0
    tc.type = type;
1218
0
    tc.local = local;
1219
0
    if (addr)
1220
0
        memcpy(&tc.bind_addr, addr, addr_size);
1221
1222
0
    rtn = CONTAINER_FIND(_container, &tc);
1223
0
    DEBUGMSGTL(("transport:cache:find", "%p\n", rtn));
1224
0
    return rtn;
1225
0
}
1226
1227
trans_cache *
1228
_tc_find_transport(netsnmp_transport *t)
1229
6.40k
{
1230
    /*
1231
     * we shouldn't really have that many transports, so instead of
1232
     * using an additional key, just iterate over the whole container.
1233
     */
1234
6.40k
    netsnmp_iterator  *itr;
1235
6.40k
    trans_cache *tc;
1236
1237
6.40k
    DEBUGMSGTL(("transport:cache:find_transport", "%p\n", t));
1238
1239
6.40k
    if (NULL == _container)
1240
6.40k
        return NULL;
1241
1242
0
    itr = CONTAINER_ITERATOR(_container);
1243
0
    if (NULL == itr) {
1244
0
        snmp_log(LOG_ERR, "could not get iterator for transport cache\n");
1245
0
        return NULL;
1246
0
    }
1247
1248
0
    tc = ITERATOR_FIRST(itr);
1249
0
    for( ; tc; tc = ITERATOR_NEXT(itr))
1250
0
        if (tc->t == t)
1251
0
            break;
1252
0
    ITERATOR_RELEASE(itr);
1253
1254
0
    DEBUGMSGT(("transport:cache:find_transport","found %p\n", tc));
1255
1256
0
    return tc;
1257
0
}
1258
1259
int
1260
netsnmp_transport_cache_remove(netsnmp_transport *t)
1261
6.40k
{
1262
6.40k
    trans_cache *tc;
1263
1264
6.40k
    DEBUGMSGTL(("transport:cache:close", "%p\n", t));
1265
1266
6.40k
    if (NULL == t)
1267
0
        return 0;
1268
1269
    /** transport in cache? */
1270
6.40k
    tc = _tc_find_transport(t);
1271
6.40k
    if (NULL == tc) {
1272
6.40k
        DEBUGMSGTL(("transport:cache:close", "%p not found in cache\n", t));
1273
6.40k
        return 0;
1274
6.40k
    }
1275
1276
0
    --tc->count;
1277
1278
    /** still in use? */
1279
0
    if (tc->count > 0) {
1280
0
        DEBUGMSGTL(("transport:cache:close", "still %d user(s) of %p\n",
1281
0
                    tc->count, t));
1282
0
        return 1;
1283
0
    }
1284
1285
    /** unbalanced get/close? */
1286
0
    if (tc->count < 0)
1287
0
        snmp_log(LOG_WARNING, "transport cache get/close mismatch\n");
1288
1289
0
    _tc_remove(tc);
1290
0
    _tc_free(tc); /* also does close */
1291
1292
0
    return 0;
1293
0
}
1294
1295
/*
1296
 * netsnmp_transport_get: get a (possibly duplicate, cached) transport
1297
 */
1298
netsnmp_transport *
1299
netsnmp_transport_cache_get(int af, int type, int local,
1300
                            const netsnmp_sockaddr_storage *bind_addr,
1301
                            unsigned addr_size)
1302
0
{
1303
0
    trans_cache       *tc;
1304
0
    netsnmp_transport *t;
1305
1306
0
    DEBUGMSGTL(("transport:cache:get", "%d/%d/%d\n", af, type, local));
1307
1308
0
#define USE_CACHE 1
1309
1310
0
#ifdef USE_CACHE
1311
    /** check for existing transport */
1312
0
    tc = _tc_find(af, type, local, bind_addr, addr_size);
1313
0
    if (tc) {
1314
0
        DEBUGMSGTL(("transport:cache:get", "using existing transport %p\n",
1315
0
                    tc->t));
1316
0
        ++tc->count;
1317
0
        return tc->t;
1318
0
    }
1319
0
#endif
1320
    /** get transport */
1321
0
    t = NULL; /* _transport(af, type, 0);*/
1322
0
    if (NULL == t) {
1323
0
        snmp_log(LOG_ERR, "could not get new transport for %d/%d/%d\n", af,
1324
0
                 type, local);
1325
0
        return NULL;
1326
0
    }
1327
0
    DEBUGMSGTL(("transport:cache:get", "new transport %p\n", t));
1328
1329
0
#ifdef USE_CACHE
1330
    /** create transport cache for new transport */
1331
0
    tc = _tc_add(af, type, local, bind_addr, addr_size, t);
1332
0
    if (NULL == tc) {
1333
0
        DEBUGMSGTL(("transport:cache:get", "could not create transport cache entry\n"));
1334
        /*
1335
         * We have a transport, just no cache for it. Let's continue on and
1336
         * hope for the best.
1337
         */
1338
0
        return t;
1339
0
    }
1340
0
    tc->count = 1;
1341
0
#endif
1342
1343
0
    return t;
1344
0
}
1345
1346
int
1347
netsnmp_transport_cache_save(int af, int type, int local,
1348
                             const netsnmp_sockaddr_storage *addr,
1349
                             unsigned addr_size,
1350
                             netsnmp_transport *t)
1351
0
{
1352
0
    if (NULL == t)
1353
0
        return 1;
1354
1355
0
    if (NULL == _tc_add(af, type, local, addr, addr_size, t))
1356
0
        return 1;
1357
1358
0
    return 0;
1359
0
}
1360
#endif /* FEATURE_REMOVE_TRANSPORT_CACHE */