/src/net-snmp/snmplib/transports/snmpTCPDomain.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include <net-snmp/net-snmp-config.h> |
2 | | |
3 | | #include <net-snmp/types.h> |
4 | | #include "snmpIPBaseDomain.h" |
5 | | #include <net-snmp/library/snmpTCPDomain.h> |
6 | | |
7 | | #include <stdio.h> |
8 | | #include <sys/types.h> |
9 | | #include <errno.h> |
10 | | |
11 | | #ifdef HAVE_STRING_H |
12 | | #include <string.h> |
13 | | #else |
14 | | #include <strings.h> |
15 | | #endif |
16 | | #ifdef HAVE_STDLIB_H |
17 | | #include <stdlib.h> |
18 | | #endif |
19 | | #ifdef HAVE_UNISTD_H |
20 | | #include <unistd.h> |
21 | | #endif |
22 | | #ifdef HAVE_SYS_SOCKET_H |
23 | | #include <sys/socket.h> |
24 | | #endif |
25 | | #ifdef HAVE_NETINET_IN_H |
26 | | #include <netinet/in.h> |
27 | | #endif |
28 | | #ifdef HAVE_ARPA_INET_H |
29 | | #include <arpa/inet.h> |
30 | | #endif |
31 | | #ifdef HAVE_FCNTL_H |
32 | | #include <fcntl.h> |
33 | | #endif |
34 | | |
35 | | #include <net-snmp/types.h> |
36 | | #include <net-snmp/output_api.h> |
37 | | |
38 | | #include <net-snmp/library/snmp.h> |
39 | | #include <net-snmp/library/snmpIPv4BaseDomain.h> |
40 | | #include <net-snmp/library/snmpSocketBaseDomain.h> |
41 | | #include <net-snmp/library/snmpTCPBaseDomain.h> |
42 | | #include <net-snmp/library/tools.h> |
43 | | |
44 | | #ifndef NETSNMP_NO_SYSTEMD |
45 | | #include <net-snmp/library/sd-daemon.h> |
46 | | #endif |
47 | | |
48 | | /* |
49 | | * needs to be in sync with the definitions in snmplib/snmpUDPDomain.c |
50 | | * and perl/agent/agent.xs |
51 | | */ |
52 | | typedef netsnmp_indexed_addr_pair netsnmp_udp_addr_pair; |
53 | | |
54 | | const oid netsnmp_snmpTCPDomain[] = { TRANSPORT_DOMAIN_TCP_IP }; |
55 | | static netsnmp_tdomain tcpDomain; |
56 | | |
57 | | /* |
58 | | * Return a string representing the address in data, or else the "far end" |
59 | | * address if data is NULL. |
60 | | */ |
61 | | |
62 | | static char * |
63 | | netsnmp_tcp_fmtaddr(netsnmp_transport *t, const void *data, int len) |
64 | 0 | { |
65 | 0 | return netsnmp_ipv4_fmtaddr("TCP", t, data, len); |
66 | 0 | } |
67 | | |
68 | | static int |
69 | | netsnmp_tcp_accept(netsnmp_transport *t) |
70 | 0 | { |
71 | 0 | struct sockaddr *farend = NULL; |
72 | 0 | netsnmp_udp_addr_pair *addr_pair = NULL; |
73 | 0 | int newsock = -1; |
74 | 0 | socklen_t farendlen; |
75 | |
|
76 | 0 | addr_pair = malloc(sizeof(*addr_pair)); |
77 | 0 | if (addr_pair == NULL) { |
78 | | /* |
79 | | * Indicate that the acceptance of this socket failed. |
80 | | */ |
81 | 0 | DEBUGMSGTL(("netsnmp_tcp", "accept: malloc failed\n")); |
82 | 0 | return -1; |
83 | 0 | } |
84 | 0 | memset(addr_pair, 0, sizeof *addr_pair); |
85 | 0 | farend = &addr_pair->remote_addr.sa; |
86 | 0 | farendlen = sizeof(addr_pair->remote_addr.sa); |
87 | |
|
88 | 0 | if (t != NULL && t->sock >= 0) { |
89 | 0 | newsock = (int) accept(t->sock, farend, &farendlen); |
90 | |
|
91 | 0 | if (newsock < 0) { |
92 | 0 | DEBUGMSGTL(("netsnmp_tcp", "accept failed rc %d errno %d \"%s\"\n", |
93 | 0 | newsock, errno, strerror(errno))); |
94 | 0 | free(addr_pair); |
95 | 0 | return newsock; |
96 | 0 | } |
97 | | |
98 | 0 | if (t->data != NULL) { |
99 | 0 | free(t->data); |
100 | 0 | } |
101 | |
|
102 | 0 | t->data = addr_pair; |
103 | 0 | t->data_length = sizeof(netsnmp_udp_addr_pair); |
104 | 0 | DEBUGIF("netsnmp_tcp") { |
105 | 0 | char *str = netsnmp_tcp_fmtaddr(NULL, farend, farendlen); |
106 | 0 | DEBUGMSGTL(("netsnmp_tcp", "accept succeeded (from %s)\n", str)); |
107 | 0 | free(str); |
108 | 0 | } |
109 | | |
110 | | /* |
111 | | * Try to make the new socket blocking. |
112 | | */ |
113 | |
|
114 | 0 | if (netsnmp_set_non_blocking_mode(newsock, FALSE) < 0) |
115 | 0 | DEBUGMSGTL(("netsnmp_tcp", "couldn't f_getfl of fd %d\n", |
116 | 0 | newsock)); |
117 | | |
118 | | /* |
119 | | * Allow user to override the send and receive buffers. Default is |
120 | | * to use os default. Don't worry too much about errors -- |
121 | | * just plough on regardless. |
122 | | */ |
123 | 0 | netsnmp_sock_buffer_set(newsock, SO_SNDBUF, 1, 0); |
124 | 0 | netsnmp_sock_buffer_set(newsock, SO_RCVBUF, 1, 0); |
125 | |
|
126 | 0 | return newsock; |
127 | 0 | } else { |
128 | 0 | free(addr_pair); |
129 | 0 | return -1; |
130 | 0 | } |
131 | 0 | } |
132 | | |
133 | | |
134 | | |
135 | | /* |
136 | | * Open a TCP-based transport for SNMP. Local is TRUE if addr is the local |
137 | | * address to bind to (i.e. this is a server-type session); otherwise addr is |
138 | | * the remote address to send things to. |
139 | | */ |
140 | | |
141 | | netsnmp_transport * |
142 | | netsnmp_tcp_transport(const struct netsnmp_ep *ep, int local) |
143 | 0 | { |
144 | 0 | const struct sockaddr_in *addr = &ep->a.sin; |
145 | 0 | netsnmp_transport *t = NULL; |
146 | 0 | netsnmp_udp_addr_pair *addr_pair = NULL; |
147 | 0 | int rc = 0; |
148 | 0 | int socket_initialized = 0; |
149 | |
|
150 | | #ifdef NETSNMP_NO_LISTEN_SUPPORT |
151 | | if (local) |
152 | | return NULL; |
153 | | #endif /* NETSNMP_NO_LISTEN_SUPPORT */ |
154 | |
|
155 | 0 | if (addr == NULL || addr->sin_family != AF_INET) { |
156 | 0 | return NULL; |
157 | 0 | } |
158 | | |
159 | 0 | t = SNMP_MALLOC_TYPEDEF(netsnmp_transport); |
160 | 0 | if (t == NULL) { |
161 | 0 | return NULL; |
162 | 0 | } |
163 | | |
164 | 0 | t->sock = -1; |
165 | 0 | addr_pair = (netsnmp_udp_addr_pair *)malloc(sizeof(netsnmp_udp_addr_pair)); |
166 | 0 | if (addr_pair == NULL) |
167 | 0 | goto err; |
168 | 0 | memset(addr_pair, 0, sizeof *addr_pair); |
169 | 0 | t->data = addr_pair; |
170 | 0 | t->data_length = sizeof(netsnmp_udp_addr_pair); |
171 | 0 | memcpy(&(addr_pair->remote_addr), addr, sizeof(struct sockaddr_in)); |
172 | |
|
173 | 0 | t->domain = netsnmp_snmpTCPDomain; |
174 | 0 | t->domain_length = |
175 | 0 | sizeof(netsnmp_snmpTCPDomain) / sizeof(netsnmp_snmpTCPDomain[0]); |
176 | |
|
177 | 0 | #ifndef NETSNMP_NO_SYSTEMD |
178 | | /* |
179 | | * Maybe the socket was already provided by systemd... |
180 | | */ |
181 | 0 | if (local) { |
182 | 0 | t->sock = netsnmp_sd_find_inet_socket(PF_INET, SOCK_STREAM, 1, |
183 | 0 | ntohs(addr->sin_port)); |
184 | 0 | if (t->sock >= 0) |
185 | 0 | socket_initialized = 1; |
186 | 0 | } |
187 | 0 | #endif |
188 | 0 | if (!socket_initialized) |
189 | 0 | t->sock = (int) socket(PF_INET, SOCK_STREAM, 0); |
190 | 0 | if (t->sock < 0) |
191 | 0 | goto err; |
192 | | |
193 | 0 | t->flags = NETSNMP_TRANSPORT_FLAG_STREAM; |
194 | | |
195 | | /* for Linux VRF Traps we try to bind the iface if clientaddr is not set */ |
196 | 0 | if (local == 0 && ep) { |
197 | 0 | rc = netsnmp_bindtodevice(t->sock, ep->iface); |
198 | 0 | if (rc) |
199 | 0 | DEBUGMSGTL(("netsnmp_tcp", "VRF: Could not bind socket %d to %s\n", |
200 | 0 | t->sock, ep->iface)); |
201 | 0 | else |
202 | 0 | DEBUGMSGTL(("netsnmp_tcp", "VRF: Bound socket %d to %s\n", |
203 | 0 | t->sock, ep->iface)); |
204 | 0 | } |
205 | |
|
206 | 0 | if (local) { |
207 | 0 | #ifndef NETSNMP_NO_LISTEN_SUPPORT |
208 | 0 | int opt = 1; |
209 | | |
210 | | /* |
211 | | * This session is inteneded as a server, so we must bind to the given |
212 | | * IP address (which may include an interface address, or could be |
213 | | * INADDR_ANY, but will always include a port number. |
214 | | */ |
215 | |
|
216 | 0 | t->flags |= NETSNMP_TRANSPORT_FLAG_LISTEN; |
217 | 0 | t->local_length = sizeof(*addr); |
218 | 0 | t->local = netsnmp_memdup(addr, sizeof(*addr)); |
219 | 0 | if (!t->local) |
220 | 0 | goto err; |
221 | | |
222 | | /* |
223 | | * We should set SO_REUSEADDR too. |
224 | | */ |
225 | | |
226 | 0 | setsockopt(t->sock, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, |
227 | 0 | sizeof(opt)); |
228 | |
|
229 | 0 | if (!socket_initialized) { |
230 | 0 | rc = netsnmp_bindtodevice(t->sock, ep->iface); |
231 | 0 | if (rc != 0) { |
232 | 0 | DEBUGMSGTL(("netsnmp_tcpbase", |
233 | 0 | "failed to bind to iface %s: %s\n", |
234 | 0 | ep->iface, strerror(errno))); |
235 | 0 | goto err; |
236 | 0 | } |
237 | 0 | rc = bind(t->sock, (const struct sockaddr *)addr, sizeof(*addr)); |
238 | 0 | if (rc != 0) |
239 | 0 | goto err; |
240 | 0 | } |
241 | | |
242 | | /* |
243 | | * Since we are going to be letting select() tell us when connections |
244 | | * are ready to be accept()ed, we need to make the socket non-blocking |
245 | | * to avoid the race condition described in W. R. Stevens, ``Unix |
246 | | * Network Programming Volume I Second Edition'', pp. 422--4, which |
247 | | * could otherwise wedge the agent. |
248 | | */ |
249 | | |
250 | 0 | netsnmp_set_non_blocking_mode(t->sock, TRUE); |
251 | | |
252 | | /* |
253 | | * Now sit here and wait for connections to arrive. |
254 | | */ |
255 | 0 | if (!socket_initialized) { |
256 | 0 | rc = listen(t->sock, NETSNMP_STREAM_QUEUE_LEN); |
257 | 0 | if (rc != 0) |
258 | 0 | goto err; |
259 | 0 | } |
260 | | |
261 | | /* |
262 | | * no buffer size on listen socket - doesn't make sense |
263 | | */ |
264 | | #else /* NETSNMP_NO_LISTEN_SUPPORT */ |
265 | | return NULL; |
266 | | #endif /* NETSNMP_NO_LISTEN_SUPPORT */ |
267 | 0 | } else { |
268 | 0 | t->remote_length = sizeof(*addr); |
269 | 0 | t->remote = netsnmp_memdup(addr, sizeof(*addr)); |
270 | 0 | if (!t->remote) |
271 | 0 | goto err; |
272 | | |
273 | | /* |
274 | | * This is a client-type session, so attempt to connect to the far |
275 | | * end. We don't go non-blocking here because it's not obvious what |
276 | | * you'd then do if you tried to do snmp_sends before the connection |
277 | | * had completed. So this can block. |
278 | | */ |
279 | | |
280 | 0 | rc = connect(t->sock, (const struct sockaddr *)addr, sizeof(*addr)); |
281 | 0 | if (rc < 0) |
282 | 0 | goto err; |
283 | | |
284 | | /* |
285 | | * Allow user to override the send and receive buffers. Default is |
286 | | * to use os default. Don't worry too much about errors -- |
287 | | * just plough on regardless. |
288 | | */ |
289 | 0 | netsnmp_sock_buffer_set(t->sock, SO_SNDBUF, local, 0); |
290 | 0 | netsnmp_sock_buffer_set(t->sock, SO_RCVBUF, local, 0); |
291 | 0 | } |
292 | | |
293 | | /* |
294 | | * Message size is not limited by this transport (hence msgMaxSize |
295 | | * is equal to the maximum legal size of an SNMP message). |
296 | | */ |
297 | | |
298 | 0 | t->msgMaxSize = SNMP_MAX_PACKET_LEN; |
299 | 0 | t->f_recv = netsnmp_tcpbase_recv; |
300 | 0 | t->f_send = netsnmp_tcpbase_send; |
301 | 0 | t->f_close = netsnmp_socketbase_close; |
302 | 0 | t->f_accept = netsnmp_tcp_accept; |
303 | 0 | t->f_setup_session = netsnmp_ipbase_session_init; |
304 | 0 | t->f_fmtaddr = netsnmp_tcp_fmtaddr; |
305 | 0 | t->f_get_taddr = netsnmp_ipv4_get_taddr; |
306 | |
|
307 | 0 | return t; |
308 | | |
309 | 0 | err: |
310 | 0 | netsnmp_socketbase_close(t); |
311 | 0 | netsnmp_transport_free(t); |
312 | 0 | return NULL; |
313 | 0 | } |
314 | | |
315 | | |
316 | | |
317 | | netsnmp_transport * |
318 | | netsnmp_tcp_create_tstring(const char *str, int local, |
319 | | const char *default_target) |
320 | 0 | { |
321 | 0 | struct netsnmp_ep ep; |
322 | |
|
323 | 0 | if (netsnmp_sockaddr_in3(&ep, str, default_target)) { |
324 | 0 | return netsnmp_tcp_transport(&ep, local); |
325 | 0 | } else { |
326 | 0 | return NULL; |
327 | 0 | } |
328 | 0 | } |
329 | | |
330 | | |
331 | | |
332 | | netsnmp_transport * |
333 | | netsnmp_tcp_create_ostring(const void *o, size_t o_len, int local) |
334 | 0 | { |
335 | 0 | struct netsnmp_ep ep; |
336 | |
|
337 | 0 | memset(&ep, 0, sizeof(ep)); |
338 | 0 | if (netsnmp_ipv4_ostring_to_sockaddr(&ep.a.sin, o, o_len)) |
339 | 0 | return netsnmp_tcp_transport(&ep, local); |
340 | 0 | return NULL; |
341 | 0 | } |
342 | | |
343 | | |
344 | | |
345 | | void |
346 | | netsnmp_tcp_ctor(void) |
347 | 3.53k | { |
348 | 3.53k | tcpDomain.name = netsnmp_snmpTCPDomain; |
349 | 3.53k | tcpDomain.name_length = OID_LENGTH(netsnmp_snmpTCPDomain); |
350 | 3.53k | tcpDomain.prefix = calloc(2, sizeof(char *)); |
351 | 3.53k | if (!tcpDomain.prefix) { |
352 | 0 | snmp_log(LOG_ERR, "calloc() failed - out of memory\n"); |
353 | 0 | return; |
354 | 0 | } |
355 | 3.53k | tcpDomain.prefix[0] = "tcp"; |
356 | | |
357 | 3.53k | tcpDomain.f_create_from_tstring_new = netsnmp_tcp_create_tstring; |
358 | 3.53k | tcpDomain.f_create_from_ostring = netsnmp_tcp_create_ostring; |
359 | | |
360 | 3.53k | netsnmp_tdomain_register(&tcpDomain); |
361 | 3.53k | } |