Coverage Report

Created: 2026-08-13 09:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/net-snmp/snmplib/transports/snmpSTDDomain.c
Line
Count
Source
1
#include <net-snmp/net-snmp-config.h>
2
3
#include <net-snmp/library/snmpSTDDomain.h>
4
5
#include <stdio.h>
6
#include <sys/types.h>
7
#include <signal.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
22
#include <net-snmp/types.h>
23
#include <net-snmp/output_api.h>
24
#include <net-snmp/library/snmp.h>
25
#include <net-snmp/library/snmp_transport.h>
26
#include <net-snmp/library/tools.h>
27
28
const oid netsnmp_snmpSTDDomain[] = { TRANSPORT_DOMAIN_STD_IP };
29
static netsnmp_tdomain stdDomain;
30
31
/*
32
 * Return a string representing the address in data, or else the "far end"
33
 * address if data is NULL.  
34
 */
35
36
static char *
37
netsnmp_std_fmtaddr(netsnmp_transport *t, const void *data, int len)
38
0
{
39
0
    DEBUGMSGTL(("domain:std","formatting addr.  data=%p\n",t->data));
40
0
    if (t->data) {
41
0
        netsnmp_std_data *data = (netsnmp_std_data*)t->data;
42
0
  char *buf;
43
44
0
        if (asprintf(&buf, "STD:%s", data->prog) < 0)
45
0
            buf = NULL;
46
0
        DEBUGMSGTL(("domain:std","  formatted:=%s\n",buf));
47
0
        return buf;
48
0
    }
49
0
    return strdup("STDInOut");
50
0
}
51
52
static void
53
netsnmp_std_get_taddr(netsnmp_transport *t, void **addr, size_t *addr_len)
54
0
{
55
0
    *addr_len = t->remote_length;
56
0
    *addr = netsnmp_memdup(t->remote, *addr_len);
57
0
}
58
59
/*
60
 * You can write something into opaque that will subsequently get passed back 
61
 * to your send function if you like.  For instance, you might want to
62
 * remember where a PDU came from, so that you can send a reply there...  
63
 */
64
65
static int
66
netsnmp_std_recv(netsnmp_transport *t, void *buf, int size,
67
     void **opaque, int *olength)
68
0
{
69
0
    int rc = -1;
70
71
0
    DEBUGMSGTL(("domain:std", "recv on sock %" NETSNMP_FMT_SKT ".  data=%p\n",
72
0
                t->sock, t->data));
73
0
    while (rc < 0) {
74
0
        rc = read(t->sock, buf, size);
75
0
        DEBUGMSGTL(("domain:std","  bytes: %d.\n", rc));
76
0
        if (rc < 0 && errno != EINTR) {
77
0
            DEBUGMSGTL(("netsnmp_std",
78
0
                        " read on fd %" NETSNMP_FMT_SKT " failed: %d (\"%s\")\n",
79
0
                        t->sock, errno, strerror(errno)));
80
0
            break;
81
0
        }
82
0
        if (rc == 0) {
83
            /* 0 input is probably bad since we selected on it */
84
0
            return -1;
85
0
        }
86
0
        DEBUGMSGTL(("netsnmp_std", "read on stdin got %d bytes\n", rc));
87
0
    }
88
89
0
    return rc;
90
0
}
91
92
93
94
static int
95
netsnmp_std_send(netsnmp_transport *t, const void *buf, int size,
96
     void **opaque, int *olength)
97
0
{
98
0
    int rc = -1;
99
100
0
    DEBUGMSGTL(("domain:std","send on sock.  data=%p\n", t->data));
101
0
    while (rc < 0) {
102
0
        if (t->data) {
103
0
            netsnmp_std_data *data = (netsnmp_std_data*)t->data;
104
0
            rc = write(data->outfd, buf, size);
105
0
        } else {
106
            /* straight to stdout */
107
0
            rc = write(1, buf, size);
108
0
        }
109
0
        if (rc < 0 && errno != EINTR) {
110
0
            break;
111
0
        }
112
0
    }
113
0
    return rc;
114
0
}
115
116
static int
117
netsnmp_std_close(netsnmp_transport *t)
118
0
{
119
0
    DEBUGMSGTL(("domain:std","close.  data=%p\n", t->data));
120
0
    if (t->data) {
121
0
        netsnmp_std_data *data = (netsnmp_std_data*)t->data;
122
123
0
        close(data->outfd);
124
0
        close(t->sock);
125
126
        /* kill the child too */
127
0
        DEBUGMSGTL(("domain:std"," killing %d\n", data->childpid));
128
0
        kill(data->childpid, SIGTERM);
129
0
        sleep(1);
130
0
        kill(data->childpid, SIGKILL);
131
        /* XXX: set an alarm to kill harder the child */
132
0
    } else {
133
        /* close stdout/in */
134
0
        close(STDOUT_FILENO);
135
0
        close(STDIN_FILENO);
136
0
    }
137
0
    return 0;
138
0
}
139
140
141
142
static NETSNMP_SOCKET
143
netsnmp_std_accept(netsnmp_transport *t)
144
0
{
145
0
    DEBUGMSGTL(("domain:std"," accept data=%p\n", t->data));
146
    /* nothing to do here */
147
0
    return NETSNMP_INVALID_SOCKET;
148
0
}
149
150
/*
151
 * Open a STDIN/STDOUT -based transport for SNMP.
152
 */
153
154
netsnmp_transport *
155
netsnmp_std_transport(const char *instring, size_t instring_len,
156
          const char *default_target)
157
0
{
158
0
    netsnmp_transport *t;
159
160
0
    t = netsnmp_transport_alloc();
161
0
    if (t == NULL) {
162
0
        return NULL;
163
0
    }
164
165
0
    t->domain = netsnmp_snmpSTDDomain;
166
0
    t->domain_length =
167
0
        sizeof(netsnmp_snmpSTDDomain) / sizeof(netsnmp_snmpSTDDomain[0]);
168
0
    t->flags = NETSNMP_TRANSPORT_FLAG_STREAM | NETSNMP_TRANSPORT_FLAG_TUNNELED;
169
170
    /*
171
     * Message size is not limited by this transport (hence msgMaxSize
172
     * is equal to the maximum legal size of an SNMP message).  
173
     */
174
175
0
    t->msgMaxSize = SNMP_MAX_PACKET_LEN;
176
0
    t->f_recv     = netsnmp_std_recv;
177
0
    t->f_send     = netsnmp_std_send;
178
0
    t->f_close    = netsnmp_std_close;
179
0
    t->f_accept   = netsnmp_std_accept;
180
0
    t->f_fmtaddr  = netsnmp_std_fmtaddr;
181
0
    t->f_get_taddr = netsnmp_std_get_taddr;
182
183
    /*
184
     * if instring is not null length, it specifies a path to a prog
185
     * XXX: plus args
186
     */
187
0
    if (instring_len == 0 && default_target != NULL) {
188
0
  instring = default_target;
189
0
  instring_len = strlen(default_target);
190
0
    }
191
192
0
    if (instring_len != 0) {
193
0
        int infd[2], outfd[2];  /* sockets to and from the client */
194
0
        int childpid;
195
196
0
        if (pipe(infd) || pipe(outfd)) {
197
0
            snmp_log(LOG_ERR,
198
0
                     "Failed to create needed pipes for a STD transport");
199
0
            netsnmp_transport_free(t);
200
0
            return NULL;
201
0
        }
202
203
0
        childpid = fork();
204
        /* parentpid => childpid */
205
        /* infd[1]   => infd[0] */
206
        /* outfd[0]  <= outfd[1] */
207
208
0
        if (childpid) {
209
0
            netsnmp_std_data *data;
210
            
211
            /* we're in the parent */
212
0
            close(infd[0]);
213
0
            close(outfd[1]);
214
215
0
            data = SNMP_MALLOC_TYPEDEF(netsnmp_std_data);
216
0
            if (!data) {
217
0
                snmp_log(LOG_ERR, "snmpSTDDomain: malloc failed");
218
0
                netsnmp_transport_free(t);
219
0
                return NULL;
220
0
            }
221
0
            t->data = data;
222
0
            t->data_length = sizeof(netsnmp_std_data);
223
0
            t->sock = outfd[0];
224
0
            data->prog = strdup(instring);
225
0
            data->outfd = infd[1];
226
0
            data->childpid = childpid;
227
0
            DEBUGMSGTL(("domain:std","parent.  data=%p\n", t->data));
228
0
        } else {
229
            /* we're in the child */
230
231
0
            dup2(infd[0], STDIN_FILENO);
232
0
            dup2(outfd[1], STDOUT_FILENO);
233
234
            /* close all the pipes themselves */
235
0
            close(infd[0]);
236
0
            close(infd[1]);
237
0
            close(outfd[0]);
238
0
            close(outfd[1]);
239
240
            /* call exec */
241
0
            NETSNMP_IGNORE_RESULT(system(instring));
242
            /* XXX: TODO: use exec form instead; needs args */
243
            /* execv(instring, NULL); */
244
0
            exit(0);
245
246
            /* ack...  we should never ever get here */
247
0
            snmp_log(LOG_ERR, "STD transport returned after execv()\n");
248
0
        }
249
0
    }            
250
251
0
    return t;
252
0
}
253
254
netsnmp_transport *
255
netsnmp_std_create_tstring(const char *instring, int local,
256
         const char *default_target)
257
0
{
258
0
    return netsnmp_std_transport(instring, strlen(instring), default_target);
259
0
}
260
261
netsnmp_transport *
262
netsnmp_std_create_ostring(const void *o, size_t o_len, int local)
263
0
{
264
0
    return netsnmp_std_transport(o, o_len, NULL);
265
0
}
266
267
void
268
netsnmp_std_ctor(void)
269
4.25k
{
270
4.25k
    stdDomain.name = netsnmp_snmpSTDDomain;
271
4.25k
    stdDomain.name_length = OID_LENGTH(netsnmp_snmpSTDDomain);
272
4.25k
    stdDomain.prefix = calloc(2, sizeof(char *));
273
4.25k
    if (!stdDomain.prefix) {
274
0
        snmp_log(LOG_ERR, "calloc() failed - out of memory\n");
275
0
        return;
276
0
    }
277
4.25k
    stdDomain.prefix[0] = "std";
278
279
4.25k
    stdDomain.f_create_from_tstring_new = netsnmp_std_create_tstring;
280
4.25k
    stdDomain.f_create_from_ostring     = netsnmp_std_create_ostring;
281
282
4.25k
    netsnmp_tdomain_register(&stdDomain);
283
4.25k
}