]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/daemon/agent_priv.c
d0c5d32164bd066ad402b5f21a17259c1fcbc820
[thirdparty/lldpd.git] / src / daemon / agent_priv.c
1 /* -*- mode: c; c-file-style: "openbsd" -*- */
2 /*
3 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
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
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
23 #include "lldpd.h"
24
25 #include <unistd.h>
26 #include <errno.h>
27 #include <poll.h>
28
29 #ifdef ENABLE_PRIVSEP
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>
34 #include <net-snmp/library/snmpUnixDomain.h>
35
36 static oid netsnmp_unix[] = { TRANSPORT_DOMAIN_LOCAL };
37 static netsnmp_tdomain unixDomain;
38
39 static char *
40 agent_priv_unix_fmtaddr(netsnmp_transport *t, void *data, int len)
41 {
42 /* We don't bother to implement the full function */
43 return strdup("Local Unix socket with privilege separation: unknown");
44 }
45
46 static int
47 agent_priv_unix_recv(netsnmp_transport *t, void *buf, int size,
48 void **opaque, int *olength)
49 {
50 int rc = -1;
51 socklen_t tolen = sizeof(struct sockaddr_un);
52 struct sockaddr *to = NULL;
53
54 if (t == NULL || t->sock < 0)
55 goto recv_error;
56 to = (struct sockaddr *)calloc(1, sizeof(struct sockaddr_un));
57 if (to == NULL)
58 goto recv_error;
59 if (getsockname(t->sock, to, &tolen) != 0)
60 goto recv_error;
61 while (rc < 0) {
62 rc = recv(t->sock, buf, size, 0);
63 /* TODO: handle the (unlikely) case where we get EAGAIN or EWOULDBLOCK */
64 if (rc < 0 && errno != EINTR) {
65 log_warn("snmp", "unable to receive from fd %d",
66 t->sock);
67 goto recv_error;
68 }
69 }
70 *opaque = (void*)to;
71 *olength = sizeof(struct sockaddr_un);
72 return rc;
73
74 recv_error:
75 free(to);
76 *opaque = NULL;
77 *olength = 0;
78 return -1;
79 }
80
81 #define AGENT_WRITE_TIMEOUT 2000
82 static int
83 agent_priv_unix_send(netsnmp_transport *t, void *buf, int size,
84 void **opaque, int *olength)
85 {
86 int rc = -1;
87
88 if (t != NULL && t->sock >= 0) {
89 struct pollfd sagentx = {
90 .fd = t->sock,
91 .events = POLLOUT | POLLERR | POLLHUP
92 };
93 while (rc < 0) {
94 rc = poll(&sagentx, 1, AGENT_WRITE_TIMEOUT);
95 if (rc == 0) {
96 log_warnx("snmp",
97 "timeout while communicating with the master agent");
98 rc = -1;
99 break;
100 }
101 if (rc > 0) {
102 /* We can either write or have an error somewhere */
103 rc = send(t->sock, buf, size, 0);
104 if (rc < 0) {
105 if (errno == EAGAIN ||
106 errno == EWOULDBLOCK ||
107 errno == EINTR)
108 /* Let's retry */
109 continue;
110 log_warn("snmp",
111 "error while sending to master agent");
112 break;
113 }
114 } else {
115 if (errno != EINTR) {
116 log_warn("snmp",
117 "error while attempting to send to master agent");
118 break;
119 }
120 continue;
121 }
122 }
123 }
124 return rc;
125 }
126
127 static int
128 agent_priv_unix_close(netsnmp_transport *t)
129 {
130 int rc = 0;
131
132 if (t->sock >= 0) {
133 rc = close(t->sock);
134 t->sock = -1;
135 return rc;
136 }
137 return -1;
138 }
139
140 static int
141 agent_priv_unix_accept(netsnmp_transport *t)
142 {
143 log_warnx("snmp", "should not have been called");
144 return -1;
145 }
146
147 static netsnmp_transport *
148 agent_priv_unix_transport(const char *string, int len, int local)
149 {
150 struct sockaddr_un addr = {
151 .sun_family = AF_UNIX
152 };
153 netsnmp_transport *t = NULL;
154
155 if (local) {
156 log_warnx("snmp", "should not have been called for local transport");
157 return NULL;
158 }
159 if (!string)
160 return NULL;
161 if (len >= sizeof(addr.sun_path) ||
162 strlcpy(addr.sun_path, string, sizeof(addr.sun_path)) >= sizeof(addr.sun_path)) {
163 log_warnx("snmp", "path too long for Unix domain transport");
164 return NULL;
165 }
166
167 if ((t = (netsnmp_transport *)
168 calloc(1, sizeof(netsnmp_transport))) == NULL)
169 return NULL;
170
171 t->domain = netsnmp_unix;
172 t->domain_length =
173 sizeof(netsnmp_unix) / sizeof(netsnmp_unix[0]);
174
175 if ((t->sock = priv_snmp_socket(&addr)) < 0) {
176 netsnmp_transport_free(t);
177 return NULL;
178 }
179
180 t->flags = NETSNMP_TRANSPORT_FLAG_STREAM;
181
182 if ((t->remote = (u_char *)
183 calloc(1, strlen(addr.sun_path) + 1)) == NULL) {
184 agent_priv_unix_close(t);
185 netsnmp_transport_free(t);
186 return NULL;
187 }
188 memcpy(t->remote, addr.sun_path, strlen(addr.sun_path));
189 t->remote_length = strlen(addr.sun_path);
190
191 t->msgMaxSize = 0x7fffffff;
192 t->f_recv = agent_priv_unix_recv;
193 t->f_send = agent_priv_unix_send;
194 t->f_close = agent_priv_unix_close;
195 t->f_accept = agent_priv_unix_accept;
196 t->f_fmtaddr = agent_priv_unix_fmtaddr;
197
198 return t;
199 }
200
201 netsnmp_transport *
202 #if !HAVE_NETSNMP_TDOMAIN_F_CREATE_FROM_TSTRING_NEW
203 agent_priv_unix_create_tstring(const char *string, int local)
204 #else
205 agent_priv_unix_create_tstring(const char *string, int local, const char *default_target)
206 #endif
207 {
208 #if HAVE_NETSNMP_TDOMAIN_F_CREATE_FROM_TSTRING_NEW
209 if ((!string || *string == '\0') && default_target &&
210 *default_target != '\0') {
211 string = default_target;
212 }
213 #endif
214 if (!string)
215 return NULL;
216 return agent_priv_unix_transport(string, strlen(string), local);
217 }
218
219 static netsnmp_transport *
220 agent_priv_unix_create_ostring(const u_char * o, size_t o_len, int local)
221 {
222 return agent_priv_unix_transport((char *)o, o_len, local);
223 }
224
225 const char *
226 agent_default_agentx_socket()
227 {
228 return NETSNMP_AGENTX_SOCKET;
229 }
230
231 void
232 agent_priv_register_domain()
233 {
234 unixDomain.name = netsnmp_unix;
235 unixDomain.name_length = sizeof(netsnmp_unix) / sizeof(oid);
236 unixDomain.prefix = (const char**)calloc(2, sizeof(char *));
237 unixDomain.prefix[0] = "unix";
238 #if !HAVE_NETSNMP_TDOMAIN_F_CREATE_FROM_TSTRING_NEW
239 unixDomain.f_create_from_tstring = agent_priv_unix_create_tstring;
240 #else
241 unixDomain.f_create_from_tstring_new = agent_priv_unix_create_tstring;
242 #endif
243 unixDomain.f_create_from_ostring = agent_priv_unix_create_ostring;
244 netsnmp_tdomain_register(&unixDomain);
245 }
246 #endif