/src/net-snmp/snmplib/transports/snmpIPXDomain.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include <net-snmp/net-snmp-config.h> |
2 | | |
3 | | #include <net-snmp/library/snmpIPXDomain.h> |
4 | | |
5 | | #include <stdio.h> |
6 | | #include <sys/types.h> |
7 | | #include <ctype.h> |
8 | | #include <errno.h> |
9 | | |
10 | | #ifdef HAVE_STRING_H |
11 | | #include <string.h> |
12 | | #else |
13 | | #include <strings.h> |
14 | | #endif |
15 | | #ifdef HAVE_STDLIB_H |
16 | | #include <stdlib.h> |
17 | | #endif |
18 | | #ifdef HAVE_UNISTD_H |
19 | | #include <unistd.h> |
20 | | #endif |
21 | | #ifdef HAVE_SYS_SOCKET_H |
22 | | #include <sys/socket.h> |
23 | | #endif |
24 | | #ifdef HAVE_NETINET_IN_H |
25 | | #include <netinet/in.h> |
26 | | #endif |
27 | | |
28 | | #include <net-snmp/types.h> |
29 | | #include <net-snmp/output_api.h> |
30 | | #include <net-snmp/config_api.h> |
31 | | |
32 | | #include <net-snmp/library/snmp_assert.h> |
33 | | #include <net-snmp/library/snmp_transport.h> |
34 | | #include <net-snmp/library/tools.h> |
35 | | |
36 | | #define SNMP_IPX_DEFAULT_PORT 36879 /* Specified in RFC 1420. */ |
37 | | static netsnmp_tdomain ipxDomain; |
38 | | |
39 | | /* |
40 | | * Return a string representing the address in data, or else the "far end" |
41 | | * address if data is NULL. |
42 | | */ |
43 | | |
44 | | static char * |
45 | | netsnmp_ipx_fmtaddr(netsnmp_transport *t, const void *data, int len) |
46 | 0 | { |
47 | 0 | const struct sockaddr_ipx *to = NULL; |
48 | |
|
49 | 0 | if (data != NULL && len == sizeof(struct sockaddr_ipx)) { |
50 | 0 | to = (const struct sockaddr_ipx *) data; |
51 | 0 | } else if (t != NULL && t->data != NULL) { |
52 | 0 | to = (const struct sockaddr_ipx *) t->data; |
53 | 0 | } |
54 | 0 | if (to == NULL) { |
55 | 0 | return strdup("IPX: unknown"); |
56 | 0 | } else { |
57 | 0 | char *tmp; |
58 | |
|
59 | 0 | if (asprintf(&tmp, "IPX: %08X:%02X%02X%02X%02X%02X%02X/%hu", |
60 | 0 | ntohl(to->sipx_network), to->sipx_node[0], |
61 | 0 | to->sipx_node[1], to->sipx_node[2], to->sipx_node[3], |
62 | 0 | to->sipx_node[4], to->sipx_node[5], ntohs(to->sipx_port)) |
63 | 0 | < 0) |
64 | 0 | tmp = NULL; |
65 | 0 | return tmp; |
66 | 0 | } |
67 | 0 | } |
68 | | |
69 | | static void netsnmp_ipx_get_taddr(struct netsnmp_transport_s *t, |
70 | | void **addr, size_t *addr_len) |
71 | 0 | { |
72 | 0 | struct sockaddr_ipx *sa = t->remote; |
73 | |
|
74 | 0 | netsnmp_assert(t->remote_length == sizeof(*sa)); |
75 | 0 | *addr_len = 12; |
76 | 0 | if ((*addr = malloc(*addr_len))) { |
77 | 0 | unsigned char *p = *addr; |
78 | |
|
79 | 0 | memcpy(p + 0, &sa->sipx_network, 4); |
80 | 0 | memcpy(p + 4, &sa->sipx_node, 6); |
81 | 0 | memcpy(p + 10, &sa->sipx_port, 2); |
82 | 0 | } |
83 | 0 | } |
84 | | |
85 | | /* |
86 | | * You can write something into opaque that will subsequently get passed back |
87 | | * to your send function if you like. For instance, you might want to |
88 | | * remember where a PDU came from, so that you can send a reply there... |
89 | | */ |
90 | | |
91 | | static int |
92 | | netsnmp_ipx_recv(netsnmp_transport *t, void *buf, int size, |
93 | | void **opaque, int *olength) |
94 | 0 | { |
95 | 0 | int rc = -1; |
96 | 0 | socklen_t fromlen = sizeof(struct sockaddr); |
97 | 0 | struct sockaddr *from; |
98 | |
|
99 | 0 | if (t != NULL && t->sock >= 0) { |
100 | 0 | from = (struct sockaddr *)malloc(sizeof(struct sockaddr_ipx)); |
101 | 0 | if (from == NULL) { |
102 | 0 | *opaque = NULL; |
103 | 0 | *olength = 0; |
104 | 0 | return -1; |
105 | 0 | } else { |
106 | 0 | memset(from, 0, fromlen); |
107 | 0 | } |
108 | | |
109 | 0 | while (rc < 0) { |
110 | 0 | rc = recvfrom(t->sock, buf, size, 0, from, &fromlen); |
111 | 0 | if (rc < 0 && errno != EINTR) { |
112 | 0 | break; |
113 | 0 | } |
114 | 0 | } |
115 | |
|
116 | 0 | if (rc >= 0) { |
117 | 0 | DEBUGIF("netsnmp_ipx") { |
118 | 0 | char *str = netsnmp_ipx_fmtaddr(NULL, from, fromlen); |
119 | 0 | DEBUGMSGTL(("netsnmp_ipx", |
120 | 0 | "recvfrom fd %d got %d bytes(from %s)\n", |
121 | 0 | t->sock, rc, str)); |
122 | 0 | free(str); |
123 | 0 | } |
124 | 0 | } else { |
125 | 0 | DEBUGMSGTL(("netsnmp_ipx", "recvfrom fd %d err %d (\"%s\")\n", |
126 | 0 | t->sock, errno, strerror(errno))); |
127 | 0 | } |
128 | 0 | *opaque = (void *) from; |
129 | 0 | *olength = sizeof(struct sockaddr_ipx); |
130 | 0 | } |
131 | 0 | return rc; |
132 | 0 | } |
133 | | |
134 | | |
135 | | |
136 | | static int |
137 | | netsnmp_ipx_send(netsnmp_transport *t, const void *buf, int size, |
138 | | void **opaque, int *olength) |
139 | 0 | { |
140 | 0 | int rc = -1; |
141 | 0 | const struct sockaddr *to = NULL; |
142 | |
|
143 | 0 | if (opaque != NULL && *opaque != NULL && |
144 | 0 | *olength == sizeof(struct sockaddr_ipx)) { |
145 | 0 | to = (const struct sockaddr *) (*opaque); |
146 | 0 | } else if (t != NULL && t->data != NULL && |
147 | 0 | t->data_length == sizeof(struct sockaddr_ipx)) { |
148 | 0 | to = (const struct sockaddr *) (t->data); |
149 | 0 | } |
150 | |
|
151 | 0 | if (to != NULL && t != NULL && t->sock >= 0) { |
152 | 0 | DEBUGIF("netsnmp_ipx") { |
153 | 0 | char *str = netsnmp_ipx_fmtaddr(NULL, to, |
154 | 0 | sizeof(struct sockaddr_ipx)); |
155 | 0 | DEBUGMSGTL(("netsnmp_ipx", "send %d bytes from %p to %s on fd %d\n", |
156 | 0 | size, buf, str, t->sock)); |
157 | 0 | free(str); |
158 | 0 | } |
159 | 0 | while (rc < 0) { |
160 | 0 | rc = sendto(t->sock, buf, size, 0, to, sizeof(struct sockaddr)); |
161 | 0 | if (rc < 0 && errno != EINTR) { |
162 | 0 | break; |
163 | 0 | } |
164 | 0 | } |
165 | 0 | } |
166 | 0 | return rc; |
167 | 0 | } |
168 | | |
169 | | |
170 | | |
171 | | static int |
172 | | netsnmp_ipx_close(netsnmp_transport *t) |
173 | 0 | { |
174 | 0 | int rc = -1; |
175 | 0 | if (t->sock >= 0) { |
176 | 0 | #ifndef HAVE_CLOSESOCKET |
177 | 0 | rc = close(t->sock); |
178 | | #else |
179 | | rc = closesocket(t->sock); |
180 | | #endif |
181 | 0 | t->sock = -1; |
182 | 0 | } |
183 | 0 | return rc; |
184 | 0 | } |
185 | | |
186 | | |
187 | | |
188 | | /* |
189 | | * Open a IPX-based transport for SNMP. Local is TRUE if addr is the local |
190 | | * address to bind to (i.e. this is a server-type session); otherwise addr is |
191 | | * the remote address to send things to. |
192 | | */ |
193 | | |
194 | | netsnmp_transport * |
195 | | netsnmp_ipx_transport(const struct sockaddr_ipx *addr, int local) |
196 | 0 | { |
197 | 0 | netsnmp_transport *t = NULL; |
198 | 0 | int rc = 0; |
199 | |
|
200 | | #ifdef NETSNMP_NO_LISTEN_SUPPORT |
201 | | if (local) |
202 | | return NULL; |
203 | | #endif /* NETSNMP_NO_LISTEN_SUPPORT */ |
204 | |
|
205 | 0 | if (addr == NULL || addr->sipx_family != AF_IPX) { |
206 | 0 | return NULL; |
207 | 0 | } |
208 | | |
209 | 0 | t = SNMP_MALLOC_TYPEDEF(netsnmp_transport); |
210 | 0 | if (t == NULL) { |
211 | 0 | return NULL; |
212 | 0 | } |
213 | | |
214 | 0 | DEBUGIF("netsnmp_ipx") { |
215 | 0 | char *str = netsnmp_ipx_fmtaddr(NULL, addr, |
216 | 0 | sizeof(struct sockaddr_ipx)); |
217 | 0 | DEBUGMSGTL(("netsnmp_ipx", "open %s %s\n", local ? "local" : "remote", |
218 | 0 | str)); |
219 | 0 | free(str); |
220 | 0 | } |
221 | |
|
222 | 0 | t->domain = netsnmpIPXDomain; |
223 | 0 | t->domain_length = netsnmpIPXDomain_len; |
224 | |
|
225 | 0 | t->sock = socket(AF_IPX, SOCK_DGRAM, AF_IPX); |
226 | 0 | if (t->sock < 0) { |
227 | 0 | netsnmp_transport_free(t); |
228 | 0 | return NULL; |
229 | 0 | } |
230 | | |
231 | 0 | if (local) { |
232 | 0 | #ifndef NETSNMP_NO_LISTEN_SUPPORT |
233 | 0 | t->local_length = sizeof(*addr); |
234 | 0 | t->local = netsnmp_memdup(addr, sizeof(*addr)); |
235 | 0 | if (t->local == NULL) { |
236 | 0 | netsnmp_transport_free(t); |
237 | 0 | return NULL; |
238 | 0 | } |
239 | | |
240 | | /* |
241 | | * This session is inteneded as a server, so we must bind on to the |
242 | | * given address (which may include a particular network and/or node |
243 | | * address, but definitely includes a port number). |
244 | | */ |
245 | | |
246 | 0 | rc = bind(t->sock, addr, sizeof(struct sockaddr)); |
247 | 0 | if (rc != 0) { |
248 | 0 | netsnmp_ipx_close(t); |
249 | 0 | netsnmp_transport_free(t); |
250 | 0 | return NULL; |
251 | 0 | } |
252 | 0 | t->data = NULL; |
253 | 0 | t->data_length = 0; |
254 | | #else /* NETSNMP_NO_LISTEN_SUPPORT */ |
255 | | return NULL; |
256 | | #endif /* NETSNMP_NO_LISTEN_SUPPORT */ |
257 | 0 | } else { |
258 | 0 | t->remote_length = sizeof(*addr); |
259 | 0 | t->remote = netsnmp_memdup(addr, sizeof(*addr)); |
260 | 0 | if (t->remote == NULL) { |
261 | 0 | netsnmp_transport_free(t); |
262 | 0 | return NULL; |
263 | 0 | } |
264 | | |
265 | | /* |
266 | | * This is a client session. Save the address in the |
267 | | * transport-specific data pointer for later use by snmp_ipx_send. |
268 | | */ |
269 | | |
270 | 0 | t->data = malloc(sizeof(struct sockaddr_ipx)); |
271 | 0 | if (t->data == NULL) { |
272 | 0 | netsnmp_transport_free(t); |
273 | 0 | return NULL; |
274 | 0 | } |
275 | 0 | memcpy(t->data, addr, sizeof(struct sockaddr_ipx)); |
276 | 0 | t->data_length = sizeof(struct sockaddr_ipx); |
277 | 0 | } |
278 | | |
279 | | /* |
280 | | * Maximum size of an IPX PDU is 576 bytes including a 30-byte header. |
281 | | * Ridiculous! |
282 | | */ |
283 | | |
284 | 0 | t->msgMaxSize = 576 - 30; |
285 | 0 | t->f_recv = netsnmp_ipx_recv; |
286 | 0 | t->f_send = netsnmp_ipx_send; |
287 | 0 | t->f_close = netsnmp_ipx_close; |
288 | 0 | t->f_accept = NULL; |
289 | 0 | t->f_fmtaddr = netsnmp_ipx_fmtaddr; |
290 | 0 | t->f_get_taddr = netsnmp_ipx_get_taddr; |
291 | |
|
292 | 0 | return t; |
293 | 0 | } |
294 | | |
295 | | |
296 | | |
297 | | /* |
298 | | * Attempt to parse a string of the form [%08x]:%12x[/%d] where the parts |
299 | | * are the network number, the node address and the port in that order. |
300 | | */ |
301 | | |
302 | | int |
303 | | netsnmp_sockaddr_ipx2(struct sockaddr_ipx *addr, const char *peername, |
304 | | const char *default_target) |
305 | 0 | { |
306 | 0 | char *input = NULL, *def = NULL; |
307 | 0 | const char *network, *node, *port; |
308 | 0 | char *tmp; |
309 | 0 | unsigned long i; |
310 | |
|
311 | 0 | if (addr == NULL) { |
312 | 0 | return 0; |
313 | 0 | } |
314 | 0 | memset(addr, 0, sizeof(struct sockaddr_ipx)); |
315 | |
|
316 | 0 | DEBUGMSGTL(("netsnmp_sockaddr_ipx", |
317 | 0 | "addr %p, peername \"%s\" default_target \"%s\"\n", |
318 | 0 | addr, peername ? peername : "[NIL]", |
319 | 0 | default_target ? default_target : "[NIL]")); |
320 | |
|
321 | 0 | addr->sipx_family = AF_IPX; |
322 | 0 | addr->sipx_type = 4; /* Specified in RFC 1420. */ |
323 | |
|
324 | 0 | network = input = strdup(peername ? peername : ""); |
325 | 0 | tmp = strchr(input, ':'); |
326 | 0 | if (tmp != NULL) { |
327 | 0 | DEBUGMSGTL(("netsnmp_sockaddr_ipx", "Node identified\n")); |
328 | 0 | *tmp++ = '\0'; |
329 | 0 | node = tmp; |
330 | 0 | tmp = strchr(tmp, '/'); |
331 | 0 | } else { |
332 | 0 | node = NULL; |
333 | 0 | tmp = strchr(input, '/'); |
334 | 0 | } |
335 | 0 | if (tmp != NULL) { |
336 | 0 | DEBUGMSGTL(("netsnmp_sockaddr_ipx", "Port identified\n")); |
337 | 0 | *tmp++ = '\0'; |
338 | 0 | port = tmp; |
339 | 0 | } else |
340 | 0 | port = NULL; |
341 | |
|
342 | 0 | DEBUGMSGTL(("netsnmp_sockaddr_ipx", "Address: %s:%s/%s\n", |
343 | 0 | network ? network : "[NIL]", node ? node : "[NIL]", |
344 | 0 | port ? port : "[NIL]")); |
345 | |
|
346 | 0 | def = strdup(default_target ? default_target : ""); |
347 | 0 | if (network == NULL || *network == '\0') |
348 | 0 | network = def; |
349 | 0 | tmp = strchr(def, ':'); |
350 | 0 | if (tmp != NULL) { |
351 | 0 | *tmp++ = '\0'; |
352 | 0 | if (node == NULL || *node == '\0') |
353 | 0 | node = tmp; |
354 | 0 | tmp = strchr(tmp, '/'); |
355 | 0 | } else |
356 | 0 | tmp = strchr(def, '/'); |
357 | 0 | if (tmp != NULL) { |
358 | 0 | *tmp++ = '\0'; |
359 | 0 | if (port == NULL || *port == '\0') |
360 | 0 | port = tmp; |
361 | 0 | } |
362 | |
|
363 | 0 | DEBUGMSGTL(("netsnmp_sockaddr_ipx", "Address: %s:%s/%s\n", |
364 | 0 | network ? network : "[NIL]", node ? node : "[NIL]", |
365 | 0 | port ? port : "[NIL]")); |
366 | |
|
367 | 0 | if (network == NULL || *network == '\0') |
368 | 0 | network = "0"; |
369 | |
|
370 | 0 | if (node == NULL || *node == '\0') |
371 | 0 | node = "000000000000"; |
372 | |
|
373 | 0 | if (port == NULL || *port == '\0') |
374 | 0 | #define val(x) __STRING(x) |
375 | 0 | port = val(SNMP_IPX_DEFAULT_PORT); |
376 | 0 | #undef val |
377 | |
|
378 | 0 | DEBUGMSGTL(("netsnmp_sockaddr_ipx", "Address: %s:%s/%s\n", |
379 | 0 | network ? network : "[NIL]", node ? node : "[NIL]", |
380 | 0 | port ? port : "[NIL]")); |
381 | |
|
382 | 0 | if(sscanf(network, "%8lx%*c", &i) == 1) { |
383 | 0 | DEBUGMSGTL(("netsnmp_sockaddr_ipx", "network parsed okay\n")); |
384 | 0 | addr->sipx_network = htonl(i); |
385 | 0 | } else { |
386 | 0 | DEBUGMSGTL(("netsnmp_sockaddr_ipx", |
387 | 0 | "failed to parse network part of address\n")); |
388 | 0 | free(def); |
389 | 0 | free(input); |
390 | 0 | return 0; |
391 | 0 | } |
392 | | |
393 | 0 | if(sscanf(node, "%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%*c", |
394 | 0 | &addr->sipx_node[0], &addr->sipx_node[1], |
395 | 0 | &addr->sipx_node[2], &addr->sipx_node[3], |
396 | 0 | &addr->sipx_node[4], &addr->sipx_node[5]) == 6) { |
397 | 0 | DEBUGMSGTL(("netsnmp_sockaddr_ipx", "node parsed okay\n")); |
398 | 0 | } else { |
399 | 0 | DEBUGMSGTL(("netsnmp_sockaddr_ipx", |
400 | 0 | "failed to parse node part of address\n")); |
401 | 0 | free(def); |
402 | 0 | free(input); |
403 | 0 | return 0; |
404 | 0 | } |
405 | | |
406 | 0 | if(sscanf(port, "%lu%*c", &i) == 1) { |
407 | 0 | DEBUGMSGTL(("netsnmp_sockaddr_ipx", "port parsed okay\n")); |
408 | 0 | addr->sipx_port = htons(i); |
409 | 0 | } else { |
410 | 0 | DEBUGMSGTL(("netsnmp_sockaddr_ipx", |
411 | 0 | "failed to parse port part of address\n")); |
412 | 0 | free(def); |
413 | 0 | free(input); |
414 | 0 | return 0; |
415 | 0 | } |
416 | | |
417 | 0 | free(def); |
418 | 0 | free(input); |
419 | 0 | return 1; |
420 | 0 | } |
421 | | |
422 | | |
423 | | |
424 | | int |
425 | | netsnmp_sockaddr_ipx(struct sockaddr_ipx *addr, const char *peername) |
426 | 0 | { |
427 | 0 | return netsnmp_sockaddr_ipx2(addr, peername, NULL); |
428 | 0 | } |
429 | | |
430 | | |
431 | | |
432 | | netsnmp_transport * |
433 | | netsnmp_ipx_create_tstring(const char *str, int local, |
434 | | const char *default_target) |
435 | 0 | { |
436 | 0 | struct sockaddr_ipx addr; |
437 | |
|
438 | 0 | if (netsnmp_sockaddr_ipx2(&addr, str, default_target)) { |
439 | 0 | return netsnmp_ipx_transport(&addr, local); |
440 | 0 | } else { |
441 | 0 | return NULL; |
442 | 0 | } |
443 | 0 | } |
444 | | |
445 | | static int netsnmp_ipx_ostring_to_sockaddr(struct sockaddr_ipx *sa, |
446 | | const void *o, size_t o_len) |
447 | 0 | { |
448 | 0 | const char *p = o; |
449 | |
|
450 | 0 | if (o_len != 12) |
451 | 0 | return 0; |
452 | | |
453 | 0 | memset(sa, 0, sizeof(*sa)); |
454 | 0 | sa->sipx_family = AF_IPX; |
455 | 0 | memcpy(&sa->sipx_network, p + 0, 4); |
456 | 0 | memcpy(&sa->sipx_node, p + 4, 6); |
457 | 0 | memcpy(&sa->sipx_port, p + 10, 2); |
458 | 0 | return 1; |
459 | 0 | } |
460 | | |
461 | | netsnmp_transport * |
462 | | netsnmp_ipx_create_ostring(const void *o, size_t o_len, int local) |
463 | 0 | { |
464 | 0 | struct sockaddr_ipx sa; |
465 | |
|
466 | 0 | if (netsnmp_ipx_ostring_to_sockaddr(&sa, o, o_len)) |
467 | 0 | return netsnmp_ipx_transport(&sa, local); |
468 | | |
469 | 0 | return NULL; |
470 | 0 | } |
471 | | |
472 | | |
473 | | |
474 | | void |
475 | | netsnmp_ipx_ctor(void) |
476 | 3.81k | { |
477 | 3.81k | ipxDomain.name = netsnmpIPXDomain; |
478 | 3.81k | ipxDomain.name_length = netsnmpIPXDomain_len; |
479 | 3.81k | ipxDomain.prefix = calloc(2, sizeof(char *)); |
480 | 3.81k | if (!ipxDomain.prefix) { |
481 | 0 | snmp_log(LOG_ERR, "calloc() failed - out of memory\n"); |
482 | 0 | return; |
483 | 0 | } |
484 | 3.81k | ipxDomain.prefix[0] = "ipx"; |
485 | | |
486 | 3.81k | ipxDomain.f_create_from_tstring_new = netsnmp_ipx_create_tstring; |
487 | 3.81k | ipxDomain.f_create_from_ostring = netsnmp_ipx_create_ostring; |
488 | | |
489 | 3.81k | netsnmp_tdomain_register(&ipxDomain); |
490 | 3.81k | } |