]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/daemon/agent_priv.c
snmp: additional fix around NetSNMP 5.8+ and function pointers
[thirdparty/lldpd.git] / src / daemon / agent_priv.c
CommitLineData
4b292b55 1/* -*- mode: c; c-file-style: "openbsd" -*- */
d72a05d4
VB
2/*
3 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
4 *
51434125 5 * Permission to use, copy, modify, and/or distribute this software for any
d72a05d4
VB
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
85127205
VB
18/* Some of the code here (agent_priv_unix_*) has been adapted from code from
19 * Net-SNMP project (snmplib/snmpUnixDomain.c). Net-SNMP project is licensed
20 * using BSD and BSD-like licenses. I don't know the exact license of the file
21 * snmplib/snmpUnixDomain.c. */
22
d72a05d4
VB
23#include "lldpd.h"
24
ae8f3104 25#include <unistd.h>
d72a05d4 26#include <errno.h>
ea51049d 27#include <poll.h>
d72a05d4 28
71a7dbb3 29#ifdef ENABLE_PRIVSEP
d72a05d4
VB
30#include <net-snmp/net-snmp-config.h>
31#include <net-snmp/net-snmp-includes.h>
32#include <net-snmp/agent/net-snmp-agent-includes.h>
33#include <net-snmp/agent/snmp_vars.h>
d72a05d4
VB
34#include <net-snmp/library/snmpUnixDomain.h>
35
31ac51a8 36#ifdef ASN_PRIV_STOP
21a0428a
VB
37/* NetSNMP 5.8+ */
38#define F_SEND_SIGNATURE netsnmp_transport *t, const void *buf, int size, void **opaque, int *olength
39#define F_FMTADDR_SIGNATURE netsnmp_transport *t, const void *data, int len
40#define F_FROM_OSTRING_SIGNATURE const void* o, size_t o_len, int local
31ac51a8 41#else
21a0428a
VB
42#define F_SEND_SIGNATURE netsnmp_transport *t, void *buf, int size, void **opaque, int *olength
43#define F_FMTADDR_SIGNATURE netsnmp_transport *t, void *data, int len
44#define F_FROM_OSTRING_SIGNATURE const u_char* o, size_t o_len, int local
31ac51a8
VB
45#endif
46
8888d191 47static oid netsnmp_unix[] = { TRANSPORT_DOMAIN_LOCAL };
d72a05d4
VB
48static netsnmp_tdomain unixDomain;
49
50static char *
21a0428a 51agent_priv_unix_fmtaddr(F_FMTADDR_SIGNATURE)
d72a05d4
VB
52{
53 /* We don't bother to implement the full function */
54 return strdup("Local Unix socket with privilege separation: unknown");
55}
56
57static int
58agent_priv_unix_recv(netsnmp_transport *t, void *buf, int size,
59 void **opaque, int *olength)
60{
61 int rc = -1;
62 socklen_t tolen = sizeof(struct sockaddr_un);
63 struct sockaddr *to = NULL;
509861fe 64
d72a05d4
VB
65 if (t == NULL || t->sock < 0)
66 goto recv_error;
b0cb07f7 67 to = (struct sockaddr *)calloc(1, sizeof(struct sockaddr_un));
d72a05d4
VB
68 if (to == NULL)
69 goto recv_error;
d72a05d4
VB
70 if (getsockname(t->sock, to, &tolen) != 0)
71 goto recv_error;
72 while (rc < 0) {
73 rc = recv(t->sock, buf, size, 0);
ea51049d 74 /* TODO: handle the (unlikely) case where we get EAGAIN or EWOULDBLOCK */
d72a05d4 75 if (rc < 0 && errno != EINTR) {
6f8925be 76 log_warn("snmp", "unable to receive from fd %d",
d72a05d4
VB
77 t->sock);
78 goto recv_error;
79 }
80 }
81 *opaque = (void*)to;
82 *olength = sizeof(struct sockaddr_un);
83 return rc;
84
85recv_error:
86 free(to);
87 *opaque = NULL;
88 *olength = 0;
89 return -1;
90}
91
ea51049d 92#define AGENT_WRITE_TIMEOUT 2000
d72a05d4 93static int
21a0428a 94agent_priv_unix_send(F_SEND_SIGNATURE)
d72a05d4 95{
ea51049d 96 int rc = -1;
b93e39a1 97
d72a05d4 98 if (t != NULL && t->sock >= 0) {
ba908c4e
VB
99 struct pollfd sagentx = {
100 .fd = t->sock,
101 .events = POLLOUT | POLLERR | POLLHUP
102 };
d72a05d4 103 while (rc < 0) {
ea51049d
VB
104 rc = poll(&sagentx, 1, AGENT_WRITE_TIMEOUT);
105 if (rc == 0) {
106 log_warnx("snmp",
107 "timeout while communicating with the master agent");
108 rc = -1;
109 break;
110 }
111 if (rc > 0) {
112 /* We can either write or have an error somewhere */
113 rc = send(t->sock, buf, size, 0);
114 if (rc < 0) {
115 if (errno == EAGAIN ||
116 errno == EWOULDBLOCK ||
117 errno == EINTR)
118 /* Let's retry */
119 continue;
120 log_warn("snmp",
121 "error while sending to master agent");
122 break;
123 }
124 } else {
125 if (errno != EINTR) {
126 log_warn("snmp",
127 "error while attempting to send to master agent");
b93e39a1
ST
128 break;
129 }
ea51049d 130 continue;
d72a05d4
VB
131 }
132 }
133 }
134 return rc;
135}
136
137static int
138agent_priv_unix_close(netsnmp_transport *t)
139{
140 int rc = 0;
141
142 if (t->sock >= 0) {
143 rc = close(t->sock);
144 t->sock = -1;
145 return rc;
146 }
147 return -1;
148}
149
150static int
151agent_priv_unix_accept(netsnmp_transport *t)
152{
6f8925be 153 log_warnx("snmp", "should not have been called");
d72a05d4
VB
154 return -1;
155}
156
8888d191 157static netsnmp_transport *
d72a05d4
VB
158agent_priv_unix_transport(const char *string, int len, int local)
159{
b0cb07f7
VB
160 struct sockaddr_un addr = {
161 .sun_family = AF_UNIX
162 };
d72a05d4 163 netsnmp_transport *t = NULL;
b0cb07f7 164
d72a05d4 165 if (local) {
6f8925be 166 log_warnx("snmp", "should not have been called for local transport");
d72a05d4
VB
167 return NULL;
168 }
509861fe
VB
169 if (!string)
170 return NULL;
b0cb07f7 171 if (len >= sizeof(addr.sun_path) ||
5e36732d 172 strlcpy(addr.sun_path, string, sizeof(addr.sun_path)) >= sizeof(addr.sun_path)) {
6f8925be 173 log_warnx("snmp", "path too long for Unix domain transport");
d72a05d4
VB
174 return NULL;
175 }
176
177 if ((t = (netsnmp_transport *)
b0cb07f7 178 calloc(1, sizeof(netsnmp_transport))) == NULL)
d72a05d4
VB
179 return NULL;
180
8888d191 181 t->domain = netsnmp_unix;
d72a05d4 182 t->domain_length =
8888d191 183 sizeof(netsnmp_unix) / sizeof(netsnmp_unix[0]);
d72a05d4
VB
184
185 if ((t->sock = priv_snmp_socket(&addr)) < 0) {
186 netsnmp_transport_free(t);
187 return NULL;
188 }
189
190 t->flags = NETSNMP_TRANSPORT_FLAG_STREAM;
191
192 if ((t->remote = (u_char *)
b64a011b 193 calloc(1, strlen(addr.sun_path) + 1)) == NULL) {
d72a05d4
VB
194 agent_priv_unix_close(t);
195 netsnmp_transport_free(t);
196 return NULL;
509861fe
VB
197 }
198 memcpy(t->remote, addr.sun_path, strlen(addr.sun_path));
199 t->remote_length = strlen(addr.sun_path);
d72a05d4
VB
200
201 t->msgMaxSize = 0x7fffffff;
202 t->f_recv = agent_priv_unix_recv;
203 t->f_send = agent_priv_unix_send;
204 t->f_close = agent_priv_unix_close;
205 t->f_accept = agent_priv_unix_accept;
206 t->f_fmtaddr = agent_priv_unix_fmtaddr;
207
208 return t;
209}
210
211netsnmp_transport *
9dfa196f
VB
212#if !HAVE_NETSNMP_TDOMAIN_F_CREATE_FROM_TSTRING_NEW
213agent_priv_unix_create_tstring(const char *string, int local)
214#else
72dc524e 215agent_priv_unix_create_tstring(const char *string, int local, const char *default_target)
9dfa196f 216#endif
d72a05d4 217{
9dfa196f 218#if HAVE_NETSNMP_TDOMAIN_F_CREATE_FROM_TSTRING_NEW
d72a05d4
VB
219 if ((!string || *string == '\0') && default_target &&
220 *default_target != '\0') {
221 string = default_target;
222 }
9dfa196f
VB
223#endif
224 if (!string)
225 return NULL;
d72a05d4
VB
226 return agent_priv_unix_transport(string, strlen(string), local);
227}
228
8888d191 229static netsnmp_transport *
21a0428a 230agent_priv_unix_create_ostring(F_FROM_OSTRING_SIGNATURE)
d72a05d4
VB
231{
232 return agent_priv_unix_transport((char *)o, o_len, local);
233}
234
235void
236agent_priv_register_domain()
237{
8888d191
VB
238 unixDomain.name = netsnmp_unix;
239 unixDomain.name_length = sizeof(netsnmp_unix) / sizeof(oid);
d72a05d4
VB
240 unixDomain.prefix = (const char**)calloc(2, sizeof(char *));
241 unixDomain.prefix[0] = "unix";
9dfa196f
VB
242#if !HAVE_NETSNMP_TDOMAIN_F_CREATE_FROM_TSTRING_NEW
243 unixDomain.f_create_from_tstring = agent_priv_unix_create_tstring;
244#else
d72a05d4 245 unixDomain.f_create_from_tstring_new = agent_priv_unix_create_tstring;
9dfa196f 246#endif
d72a05d4 247 unixDomain.f_create_from_ostring = agent_priv_unix_create_ostring;
d72a05d4
VB
248 netsnmp_tdomain_register(&unixDomain);
249}
71a7dbb3 250#endif