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