]>
Commit | Line | Data |
---|---|---|
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 | 47 | static oid netsnmp_unix[] = { TRANSPORT_DOMAIN_LOCAL }; |
d72a05d4 VB |
48 | static netsnmp_tdomain unixDomain; |
49 | ||
50 | static char * | |
21a0428a | 51 | agent_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 | ||
57 | static int | |
58 | agent_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 | ||
85 | recv_error: | |
86 | free(to); | |
87 | *opaque = NULL; | |
88 | *olength = 0; | |
89 | return -1; | |
90 | } | |
91 | ||
ea51049d | 92 | #define AGENT_WRITE_TIMEOUT 2000 |
d72a05d4 | 93 | static int |
21a0428a | 94 | agent_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 | ||
137 | static int | |
138 | agent_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 | ||
150 | static int | |
151 | agent_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 | 157 | static netsnmp_transport * |
d72a05d4 VB |
158 | agent_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 | ||
211 | netsnmp_transport * | |
9dfa196f VB |
212 | #if !HAVE_NETSNMP_TDOMAIN_F_CREATE_FROM_TSTRING_NEW |
213 | agent_priv_unix_create_tstring(const char *string, int local) | |
214 | #else | |
72dc524e | 215 | agent_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 | 229 | static netsnmp_transport * |
21a0428a | 230 | agent_priv_unix_create_ostring(F_FROM_OSTRING_SIGNATURE) |
d72a05d4 VB |
231 | { |
232 | return agent_priv_unix_transport((char *)o, o_len, local); | |
233 | } | |
234 | ||
235 | void | |
236 | agent_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 |