Coverage Report

Created: 2026-10-19 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/net-snmp/snmplib/transports/snmpIPXDomain.c
Line
Count
Source
1
#include <net-snmp/net-snmp-config.h>
2
3
#include <net-snmp/library/snmpIPXDomain.h>
4
5
#include <stdio.h>
6
#include <sys/types.h>
7
#include <ctype.h>
8
#include <errno.h>
9
10
#ifdef HAVE_STRING_H
11
#include <string.h>
12
#else
13
#include <strings.h>
14
#endif
15
#ifdef HAVE_STDLIB_H
16
#include <stdlib.h>
17
#endif
18
#ifdef HAVE_UNISTD_H
19
#include <unistd.h>
20
#endif
21
#ifdef HAVE_SYS_SOCKET_H
22
#include <sys/socket.h>
23
#endif
24
#ifdef HAVE_NETINET_IN_H
25
#include <netinet/in.h>
26
#endif
27
28
#include <net-snmp/types.h>
29
#include <net-snmp/output_api.h>
30
#include <net-snmp/config_api.h>
31
32
#include <net-snmp/library/snmp_assert.h>
33
#include <net-snmp/library/snmp_transport.h>
34
#include <net-snmp/library/tools.h>
35
36
#define SNMP_IPX_DEFAULT_PORT 36879   /*  Specified in RFC 1420.  */
37
static netsnmp_tdomain ipxDomain;
38
39
/*
40
 * Return a string representing the address in data, or else the "far end"
41
 * address if data is NULL.  
42
 */
43
44
static char *
45
netsnmp_ipx_fmtaddr(netsnmp_transport *t, const void *data, int len)
46
0
{
47
0
    const struct sockaddr_ipx *to = NULL;
48
49
0
    if (data != NULL && len == sizeof(struct sockaddr_ipx)) {
50
0
        to = (const struct sockaddr_ipx *) data;
51
0
    } else if (t != NULL && t->data != NULL) {
52
0
        to = (const struct sockaddr_ipx *) t->data;
53
0
    }
54
0
    if (to == NULL) {
55
0
        return strdup("IPX: unknown");
56
0
    } else {
57
0
        char *tmp;
58
59
0
        if (asprintf(&tmp, "IPX: %08X:%02X%02X%02X%02X%02X%02X/%hu",
60
0
         ntohl(to->sipx_network), to->sipx_node[0],
61
0
         to->sipx_node[1], to->sipx_node[2], to->sipx_node[3],
62
0
         to->sipx_node[4], to->sipx_node[5], ntohs(to->sipx_port))
63
0
      < 0)
64
0
            tmp = NULL;
65
0
        return tmp;
66
0
    }
67
0
}
68
69
static void netsnmp_ipx_get_taddr(struct netsnmp_transport_s *t,
70
                                  void **addr, size_t *addr_len)
71
0
{
72
0
    struct sockaddr_ipx *sa = t->remote;
73
74
0
    netsnmp_assert(t->remote_length == sizeof(*sa));
75
0
    *addr_len = 12;
76
0
    if ((*addr = malloc(*addr_len))) {
77
0
        unsigned char *p = *addr;
78
79
0
        memcpy(p + 0,  &sa->sipx_network, 4);
80
0
        memcpy(p + 4,  &sa->sipx_node,    6);
81
0
        memcpy(p + 10, &sa->sipx_port,    2);
82
0
    }
83
0
}
84
85
/*
86
 * You can write something into opaque that will subsequently get passed back 
87
 * to your send function if you like.  For instance, you might want to
88
 * remember where a PDU came from, so that you can send a reply there...  
89
 */
90
91
static int
92
netsnmp_ipx_recv(netsnmp_transport *t, void *buf, int size,
93
     void **opaque, int *olength)
94
0
{
95
0
    int        rc = -1;
96
0
    socklen_t      fromlen = sizeof(struct sockaddr);
97
0
    struct sockaddr *from;
98
99
0
    if (t && NETSNMP_IS_VALID_SOCKET(t->sock)) {
100
0
        from = (struct sockaddr *)malloc(sizeof(struct sockaddr_ipx));
101
0
        if (from == NULL) {
102
0
            *opaque = NULL;
103
0
            *olength = 0;
104
0
            return -1;
105
0
        } else {
106
0
            memset(from, 0, fromlen);
107
0
        }
108
109
0
  while (rc < 0) {
110
0
    rc = recvfrom(t->sock, buf, size, 0, from, &fromlen);
111
0
    if (rc < 0 && errno != EINTR) {
112
0
      break;
113
0
    }
114
0
  }
115
116
0
        if (rc >= 0) {
117
0
            DEBUGIF("netsnmp_ipx") {
118
0
                char *str = netsnmp_ipx_fmtaddr(NULL, from, fromlen);
119
0
                DEBUGMSGTL(("netsnmp_ipx",
120
0
                            "recvfrom fd %" NETSNMP_FMT_SKT " got %d bytes(from %s)\n",
121
0
                            t->sock, rc, str));
122
0
                free(str);
123
0
            }
124
0
        } else {
125
0
            DEBUGMSGTL(("netsnmp_ipx",
126
0
                        "recvfrom fd %" NETSNMP_FMT_SKT " err %d (\"%s\")\n",
127
0
                        t->sock, errno, strerror(errno)));
128
0
        }
129
0
        *opaque = (void *) from;
130
0
        *olength = sizeof(struct sockaddr_ipx);
131
0
    }
132
0
    return rc;
133
0
}
134
135
136
137
static int
138
netsnmp_ipx_send(netsnmp_transport *t, const void *buf, int size,
139
     void **opaque, int *olength)
140
0
{
141
0
    int rc = -1;
142
0
    const struct sockaddr *to = NULL;
143
144
0
    if (opaque != NULL && *opaque != NULL &&
145
0
  *olength == sizeof(struct sockaddr_ipx)) {
146
0
        to = (const struct sockaddr *) (*opaque);
147
0
    } else if (t != NULL && t->data != NULL &&
148
0
               t->data_length == sizeof(struct sockaddr_ipx)) {
149
0
        to = (const struct sockaddr *) (t->data);
150
0
    }
151
152
0
    if (to && t && NETSNMP_IS_VALID_SOCKET(t->sock)) {
153
0
        DEBUGIF("netsnmp_ipx") {
154
0
            char *str = netsnmp_ipx_fmtaddr(NULL, to,
155
0
                                            sizeof(struct sockaddr_ipx));
156
0
            DEBUGMSGTL(("netsnmp_ipx",
157
0
                        "send %d bytes to %s on fd %" NETSNMP_FMT_SKT "\n",
158
0
                        size, str, t->sock));
159
0
            free(str);
160
0
        }
161
0
  while (rc < 0) {
162
0
      rc = sendto(t->sock, buf, size, 0, to, sizeof(struct sockaddr));
163
0
      if (rc < 0 && errno != EINTR) {
164
0
    break;
165
0
      }
166
0
  }
167
0
    }
168
0
    return rc;
169
0
}
170
171
172
173
static int
174
netsnmp_ipx_close(netsnmp_transport *t)
175
0
{
176
0
    int rc;
177
178
0
    if (!NETSNMP_IS_VALID_SOCKET(t->sock))
179
0
        return -1;
180
181
0
#ifndef HAVE_CLOSESOCKET
182
0
    rc = close(t->sock);
183
#else
184
    rc = closesocket(t->sock);
185
#endif
186
0
    t->sock = NETSNMP_INVALID_SOCKET;
187
188
0
    return rc;
189
0
}
190
191
192
193
/*
194
 * Open a IPX-based transport for SNMP.  Local is TRUE if addr is the local
195
 * address to bind to (i.e. this is a server-type session); otherwise addr is 
196
 * the remote address to send things to.  
197
 */
198
199
netsnmp_transport *
200
netsnmp_ipx_transport(const struct sockaddr_ipx *addr, int local)
201
0
{
202
0
    netsnmp_transport *t = NULL;
203
0
    int             rc = 0;
204
205
#ifdef NETSNMP_NO_LISTEN_SUPPORT
206
    if (local)
207
        return NULL;
208
#endif /* NETSNMP_NO_LISTEN_SUPPORT */
209
210
0
    if (addr == NULL || addr->sipx_family != AF_IPX) {
211
0
        return NULL;
212
0
    }
213
214
0
    t = netsnmp_transport_alloc();
215
0
    if (t == NULL) {
216
0
        return NULL;
217
0
    }
218
219
0
    DEBUGIF("netsnmp_ipx") {
220
0
        char *str = netsnmp_ipx_fmtaddr(NULL, addr,
221
0
                                  sizeof(struct sockaddr_ipx));
222
0
        DEBUGMSGTL(("netsnmp_ipx", "open %s %s\n", local ? "local" : "remote",
223
0
                    str));
224
0
        free(str);
225
0
    }
226
227
0
    t->domain = netsnmpIPXDomain;
228
0
    t->domain_length = netsnmpIPXDomain_len;
229
230
0
    t->sock = socket(AF_IPX, SOCK_DGRAM, AF_IPX);
231
0
    if (!NETSNMP_IS_VALID_SOCKET(t->sock)) {
232
0
        netsnmp_transport_free(t);
233
0
        return NULL;
234
0
    }
235
236
0
    if (local) {
237
0
#ifndef NETSNMP_NO_LISTEN_SUPPORT
238
0
        t->local_length = sizeof(*addr);
239
0
        t->local = netsnmp_memdup(addr, sizeof(*addr));
240
0
        if (t->local == NULL) {
241
0
            netsnmp_ipx_close(t);
242
0
            netsnmp_transport_free(t);
243
0
            return NULL;
244
0
        }
245
246
        /*
247
         * This session is intended as a server, so we must bind on to the
248
         * given address (which may include a particular network and/or node
249
         * address, but definitely includes a port number).
250
         */
251
252
0
        rc = bind(t->sock, addr, sizeof(struct sockaddr));
253
0
        if (rc != 0) {
254
0
            netsnmp_ipx_close(t);
255
0
            netsnmp_transport_free(t);
256
0
            return NULL;
257
0
        }
258
0
        t->data = NULL;
259
0
        t->data_length = 0;
260
#else /* NETSNMP_NO_LISTEN_SUPPORT */
261
        netsnmp_ipx_close(t);
262
        netsnmp_transport_free(t);
263
        return NULL;
264
#endif /* NETSNMP_NO_LISTEN_SUPPORT */
265
0
    } else {
266
0
        t->remote_length = sizeof(*addr);
267
0
        t->remote = netsnmp_memdup(addr, sizeof(*addr));
268
0
        if (t->remote == NULL) {
269
0
            netsnmp_ipx_close(t);
270
0
            netsnmp_transport_free(t);
271
0
            return NULL;
272
0
        }
273
274
        /*
275
         * This is a client session.  Save the address in the
276
         * transport-specific data pointer for later use by snmp_ipx_send.
277
         */
278
279
0
        t->data = malloc(sizeof(struct sockaddr_ipx));
280
0
        if (t->data == NULL) {
281
0
            netsnmp_ipx_close(t);
282
0
            netsnmp_transport_free(t);
283
0
            return NULL;
284
0
        }
285
0
        memcpy(t->data, addr, sizeof(struct sockaddr_ipx));
286
0
        t->data_length = sizeof(struct sockaddr_ipx);
287
0
    }
288
289
    /*
290
     * Maximum size of an IPX PDU is 576 bytes including a 30-byte header.
291
     * Ridiculous!  
292
     */
293
294
0
    t->msgMaxSize = 576 - 30;
295
0
    t->f_recv     = netsnmp_ipx_recv;
296
0
    t->f_send     = netsnmp_ipx_send;
297
0
    t->f_close    = netsnmp_ipx_close;
298
0
    t->f_accept   = NULL;
299
0
    t->f_fmtaddr  = netsnmp_ipx_fmtaddr;
300
0
    t->f_get_taddr = netsnmp_ipx_get_taddr;
301
302
0
    return t;
303
0
}
304
305
306
307
/*
308
 * Attempt to parse a string of the form [%08x]:%12x[/%d] where the parts
309
 * are the network number, the node address and the port in that order.  
310
 */
311
312
int
313
netsnmp_sockaddr_ipx2(struct sockaddr_ipx *addr, const char *peername,
314
          const char *default_target)
315
0
{
316
0
    char           *input = NULL, *def = NULL;
317
0
    const char     *network, *node, *port;
318
0
    char           *tmp;
319
0
    unsigned long   i;
320
321
0
    if (addr == NULL) {
322
0
        return 0;
323
0
    }
324
0
    memset(addr, 0, sizeof(struct sockaddr_ipx));
325
326
0
    DEBUGMSGTL(("netsnmp_sockaddr_ipx",
327
0
    "addr %p, peername \"%s\" default_target \"%s\"\n",
328
0
                addr, peername ? peername : "[NIL]",
329
0
    default_target ? default_target : "[NIL]"));
330
331
0
    addr->sipx_family = AF_IPX;
332
0
    addr->sipx_type = 4;        /*  Specified in RFC 1420.  */
333
334
0
    network = input = strdup(peername ? peername : "");
335
0
    tmp = strchr(input, ':');
336
0
    if (tmp != NULL) {
337
0
        DEBUGMSGTL(("netsnmp_sockaddr_ipx", "Node identified\n"));
338
0
        *tmp++ = '\0';
339
0
  node = tmp;
340
0
        tmp = strchr(tmp, '/');
341
0
    } else {
342
0
        node = NULL;
343
0
        tmp = strchr(input, '/');
344
0
    }
345
0
    if (tmp != NULL) {
346
0
        DEBUGMSGTL(("netsnmp_sockaddr_ipx", "Port identified\n"));
347
0
        *tmp++ = '\0';
348
0
        port = tmp;
349
0
    } else
350
0
        port = NULL;
351
352
0
    DEBUGMSGTL(("netsnmp_sockaddr_ipx", "Address: %s:%s/%s\n",
353
0
                network ? network : "[NIL]", node ? node : "[NIL]",
354
0
                port ? port : "[NIL]"));
355
356
0
    def = strdup(default_target ? default_target : "");
357
0
    if (network == NULL || *network == '\0')
358
0
        network = def;
359
0
    tmp = strchr(def, ':');
360
0
    if (tmp != NULL) {
361
0
        *tmp++ = '\0';
362
0
  if (node == NULL || *node == '\0')
363
0
            node = tmp;
364
0
        tmp = strchr(tmp, '/');
365
0
    } else
366
0
        tmp = strchr(def, '/');
367
0
    if (tmp != NULL) {
368
0
        *tmp++ = '\0';
369
0
        if (port == NULL || *port == '\0')
370
0
            port = tmp;
371
0
    }
372
373
0
    DEBUGMSGTL(("netsnmp_sockaddr_ipx", "Address: %s:%s/%s\n",
374
0
                network ? network : "[NIL]", node ? node : "[NIL]",
375
0
                port ? port : "[NIL]"));
376
377
0
    if (network == NULL || *network == '\0')
378
0
        network = "0";
379
380
0
    if (node == NULL || *node == '\0')
381
0
        node = "000000000000";
382
383
0
    if (port == NULL || *port == '\0')
384
0
#define val(x) __STRING(x)
385
0
        port = val(SNMP_IPX_DEFAULT_PORT);
386
0
#undef val
387
388
0
    DEBUGMSGTL(("netsnmp_sockaddr_ipx", "Address: %s:%s/%s\n",
389
0
                network ? network : "[NIL]", node ? node : "[NIL]",
390
0
                port ? port : "[NIL]"));
391
392
0
    if(sscanf(network, "%8lx%*c", &i) == 1) {
393
0
        DEBUGMSGTL(("netsnmp_sockaddr_ipx", "network parsed okay\n"));
394
0
        addr->sipx_network = htonl(i);
395
0
    } else {
396
0
        DEBUGMSGTL(("netsnmp_sockaddr_ipx",
397
0
                    "failed to parse network part of address\n"));
398
0
        free(def);
399
0
        free(input);
400
0
        return 0;
401
0
    }
402
403
0
    if(sscanf(node, "%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%*c",
404
0
              &addr->sipx_node[0], &addr->sipx_node[1],
405
0
              &addr->sipx_node[2], &addr->sipx_node[3],
406
0
              &addr->sipx_node[4], &addr->sipx_node[5]) == 6) {
407
0
        DEBUGMSGTL(("netsnmp_sockaddr_ipx", "node parsed okay\n"));
408
0
    } else {
409
0
        DEBUGMSGTL(("netsnmp_sockaddr_ipx",
410
0
                    "failed to parse node part of address\n"));
411
0
        free(def);
412
0
        free(input);
413
0
        return 0;
414
0
    }
415
416
0
    if(sscanf(port, "%lu%*c", &i) == 1) {
417
0
        DEBUGMSGTL(("netsnmp_sockaddr_ipx", "port parsed okay\n"));
418
0
        addr->sipx_port = htons(i);
419
0
    } else {
420
0
        DEBUGMSGTL(("netsnmp_sockaddr_ipx",
421
0
                    "failed to parse port part of address\n"));
422
0
        free(def);
423
0
        free(input);
424
0
        return 0;
425
0
    }
426
427
0
    free(def);
428
0
    free(input);
429
0
    return 1;
430
0
}
431
432
433
434
int
435
netsnmp_sockaddr_ipx(struct sockaddr_ipx *addr, const char *peername)
436
0
{
437
0
    return netsnmp_sockaddr_ipx2(addr, peername, NULL);
438
0
}
439
440
441
442
netsnmp_transport *
443
netsnmp_ipx_create_tstring(const char *str, int local,
444
         const char *default_target)
445
0
{
446
0
    struct sockaddr_ipx addr;
447
448
0
    if (netsnmp_sockaddr_ipx2(&addr, str, default_target)) {
449
0
        return netsnmp_ipx_transport(&addr, local);
450
0
    } else {
451
0
        return NULL;
452
0
    }
453
0
}
454
455
static int netsnmp_ipx_ostring_to_sockaddr(struct sockaddr_ipx *sa,
456
                                           const void *o, size_t o_len)
457
0
{
458
0
    const char *p = o;
459
460
0
    if (o_len != 12)
461
0
        return 0;
462
463
0
    memset(sa, 0, sizeof(*sa));
464
0
    sa->sipx_family = AF_IPX;
465
0
    memcpy(&sa->sipx_network, p + 0, 4);
466
0
    memcpy(&sa->sipx_node,    p + 4, 6);
467
0
    memcpy(&sa->sipx_port,    p + 10, 2);
468
0
    return 1;
469
0
}
470
471
netsnmp_transport *
472
netsnmp_ipx_create_ostring(const void *o, size_t o_len, int local)
473
0
{
474
0
    struct sockaddr_ipx sa;
475
476
0
    if (netsnmp_ipx_ostring_to_sockaddr(&sa, o, o_len))
477
0
        return netsnmp_ipx_transport(&sa, local);
478
479
0
    return NULL;
480
0
}
481
482
483
484
void
485
netsnmp_ipx_ctor(void)
486
4.35k
{
487
4.35k
    ipxDomain.name = netsnmpIPXDomain;
488
4.35k
    ipxDomain.name_length = netsnmpIPXDomain_len;
489
4.35k
    ipxDomain.prefix = calloc(2, sizeof(char *));
490
4.35k
    if (!ipxDomain.prefix) {
491
0
        snmp_log(LOG_ERR, "calloc() failed - out of memory\n");
492
0
        return;
493
0
    }
494
4.35k
    ipxDomain.prefix[0] = "ipx";
495
496
4.35k
    ipxDomain.f_create_from_tstring_new = netsnmp_ipx_create_tstring;
497
4.35k
    ipxDomain.f_create_from_ostring     = netsnmp_ipx_create_ostring;
498
499
4.35k
    netsnmp_tdomain_register(&ipxDomain);
500
4.35k
}