Coverage Report

Created: 2026-08-13 09:19

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