Coverage Report

Created: 2024-07-27 06:09

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