/src/net-snmp/snmplib/transports/snmpIPXDomain.c
Line | Count | Source |
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 && NETSNMP_IS_VALID_SOCKET(t->sock)) { |
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 %" NETSNMP_FMT_SKT " 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", |
126 | 0 | "recvfrom fd %" NETSNMP_FMT_SKT " err %d (\"%s\")\n", |
127 | 0 | t->sock, errno, strerror(errno))); |
128 | 0 | } |
129 | 0 | *opaque = (void *) from; |
130 | 0 | *olength = sizeof(struct sockaddr_ipx); |
131 | 0 | } |
132 | 0 | return rc; |
133 | 0 | } |
134 | | |
135 | | |
136 | | |
137 | | static int |
138 | | netsnmp_ipx_send(netsnmp_transport *t, const void *buf, int size, |
139 | | void **opaque, int *olength) |
140 | 0 | { |
141 | 0 | int rc = -1; |
142 | 0 | const struct sockaddr *to = NULL; |
143 | |
|
144 | 0 | if (opaque != NULL && *opaque != NULL && |
145 | 0 | *olength == sizeof(struct sockaddr_ipx)) { |
146 | 0 | to = (const struct sockaddr *) (*opaque); |
147 | 0 | } else if (t != NULL && t->data != NULL && |
148 | 0 | t->data_length == sizeof(struct sockaddr_ipx)) { |
149 | 0 | to = (const struct sockaddr *) (t->data); |
150 | 0 | } |
151 | |
|
152 | 0 | if (to && t && NETSNMP_IS_VALID_SOCKET(t->sock)) { |
153 | 0 | DEBUGIF("netsnmp_ipx") { |
154 | 0 | char *str = netsnmp_ipx_fmtaddr(NULL, to, |
155 | 0 | sizeof(struct sockaddr_ipx)); |
156 | 0 | DEBUGMSGTL(("netsnmp_ipx", |
157 | 0 | "send %d bytes to %s on fd %" NETSNMP_FMT_SKT "\n", |
158 | 0 | size, str, t->sock)); |
159 | 0 | free(str); |
160 | 0 | } |
161 | 0 | while (rc < 0) { |
162 | 0 | rc = sendto(t->sock, buf, size, 0, to, sizeof(struct sockaddr)); |
163 | 0 | if (rc < 0 && errno != EINTR) { |
164 | 0 | break; |
165 | 0 | } |
166 | 0 | } |
167 | 0 | } |
168 | 0 | return rc; |
169 | 0 | } |
170 | | |
171 | | |
172 | | |
173 | | static int |
174 | | netsnmp_ipx_close(netsnmp_transport *t) |
175 | 0 | { |
176 | 0 | int rc; |
177 | |
|
178 | 0 | if (!NETSNMP_IS_VALID_SOCKET(t->sock)) |
179 | 0 | return -1; |
180 | | |
181 | 0 | #ifndef HAVE_CLOSESOCKET |
182 | 0 | rc = close(t->sock); |
183 | | #else |
184 | | rc = closesocket(t->sock); |
185 | | #endif |
186 | 0 | t->sock = NETSNMP_INVALID_SOCKET; |
187 | |
|
188 | 0 | return rc; |
189 | 0 | } |
190 | | |
191 | | |
192 | | |
193 | | /* |
194 | | * Open a IPX-based transport for SNMP. Local is TRUE if addr is the local |
195 | | * address to bind to (i.e. this is a server-type session); otherwise addr is |
196 | | * the remote address to send things to. |
197 | | */ |
198 | | |
199 | | netsnmp_transport * |
200 | | netsnmp_ipx_transport(const struct sockaddr_ipx *addr, int local) |
201 | 0 | { |
202 | 0 | netsnmp_transport *t = NULL; |
203 | 0 | int rc = 0; |
204 | |
|
205 | | #ifdef NETSNMP_NO_LISTEN_SUPPORT |
206 | | if (local) |
207 | | return NULL; |
208 | | #endif /* NETSNMP_NO_LISTEN_SUPPORT */ |
209 | |
|
210 | 0 | if (addr == NULL || addr->sipx_family != AF_IPX) { |
211 | 0 | return NULL; |
212 | 0 | } |
213 | | |
214 | 0 | t = netsnmp_transport_alloc(); |
215 | 0 | if (t == NULL) { |
216 | 0 | return NULL; |
217 | 0 | } |
218 | | |
219 | 0 | DEBUGIF("netsnmp_ipx") { |
220 | 0 | char *str = netsnmp_ipx_fmtaddr(NULL, addr, |
221 | 0 | sizeof(struct sockaddr_ipx)); |
222 | 0 | DEBUGMSGTL(("netsnmp_ipx", "open %s %s\n", local ? "local" : "remote", |
223 | 0 | str)); |
224 | 0 | free(str); |
225 | 0 | } |
226 | |
|
227 | 0 | t->domain = netsnmpIPXDomain; |
228 | 0 | t->domain_length = netsnmpIPXDomain_len; |
229 | |
|
230 | 0 | t->sock = socket(AF_IPX, SOCK_DGRAM, AF_IPX); |
231 | 0 | if (!NETSNMP_IS_VALID_SOCKET(t->sock)) { |
232 | 0 | netsnmp_transport_free(t); |
233 | 0 | return NULL; |
234 | 0 | } |
235 | | |
236 | 0 | if (local) { |
237 | 0 | #ifndef NETSNMP_NO_LISTEN_SUPPORT |
238 | 0 | t->local_length = sizeof(*addr); |
239 | 0 | t->local = netsnmp_memdup(addr, sizeof(*addr)); |
240 | 0 | if (t->local == NULL) { |
241 | 0 | netsnmp_ipx_close(t); |
242 | 0 | netsnmp_transport_free(t); |
243 | 0 | return NULL; |
244 | 0 | } |
245 | | |
246 | | /* |
247 | | * This session is intended as a server, so we must bind on to the |
248 | | * given address (which may include a particular network and/or node |
249 | | * address, but definitely includes a port number). |
250 | | */ |
251 | | |
252 | 0 | rc = bind(t->sock, addr, sizeof(struct sockaddr)); |
253 | 0 | if (rc != 0) { |
254 | 0 | netsnmp_ipx_close(t); |
255 | 0 | netsnmp_transport_free(t); |
256 | 0 | return NULL; |
257 | 0 | } |
258 | 0 | t->data = NULL; |
259 | 0 | t->data_length = 0; |
260 | | #else /* NETSNMP_NO_LISTEN_SUPPORT */ |
261 | | netsnmp_ipx_close(t); |
262 | | netsnmp_transport_free(t); |
263 | | return NULL; |
264 | | #endif /* NETSNMP_NO_LISTEN_SUPPORT */ |
265 | 0 | } else { |
266 | 0 | t->remote_length = sizeof(*addr); |
267 | 0 | t->remote = netsnmp_memdup(addr, sizeof(*addr)); |
268 | 0 | if (t->remote == NULL) { |
269 | 0 | netsnmp_ipx_close(t); |
270 | 0 | netsnmp_transport_free(t); |
271 | 0 | return NULL; |
272 | 0 | } |
273 | | |
274 | | /* |
275 | | * This is a client session. Save the address in the |
276 | | * transport-specific data pointer for later use by snmp_ipx_send. |
277 | | */ |
278 | | |
279 | 0 | t->data = malloc(sizeof(struct sockaddr_ipx)); |
280 | 0 | if (t->data == NULL) { |
281 | 0 | netsnmp_ipx_close(t); |
282 | 0 | netsnmp_transport_free(t); |
283 | 0 | return NULL; |
284 | 0 | } |
285 | 0 | memcpy(t->data, addr, sizeof(struct sockaddr_ipx)); |
286 | 0 | t->data_length = sizeof(struct sockaddr_ipx); |
287 | 0 | } |
288 | | |
289 | | /* |
290 | | * Maximum size of an IPX PDU is 576 bytes including a 30-byte header. |
291 | | * Ridiculous! |
292 | | */ |
293 | | |
294 | 0 | t->msgMaxSize = 576 - 30; |
295 | 0 | t->f_recv = netsnmp_ipx_recv; |
296 | 0 | t->f_send = netsnmp_ipx_send; |
297 | 0 | t->f_close = netsnmp_ipx_close; |
298 | 0 | t->f_accept = NULL; |
299 | 0 | t->f_fmtaddr = netsnmp_ipx_fmtaddr; |
300 | 0 | t->f_get_taddr = netsnmp_ipx_get_taddr; |
301 | |
|
302 | 0 | return t; |
303 | 0 | } |
304 | | |
305 | | |
306 | | |
307 | | /* |
308 | | * Attempt to parse a string of the form [%08x]:%12x[/%d] where the parts |
309 | | * are the network number, the node address and the port in that order. |
310 | | */ |
311 | | |
312 | | int |
313 | | netsnmp_sockaddr_ipx2(struct sockaddr_ipx *addr, const char *peername, |
314 | | const char *default_target) |
315 | 0 | { |
316 | 0 | char *input = NULL, *def = NULL; |
317 | 0 | const char *network, *node, *port; |
318 | 0 | char *tmp; |
319 | 0 | unsigned long i; |
320 | |
|
321 | 0 | if (addr == NULL) { |
322 | 0 | return 0; |
323 | 0 | } |
324 | 0 | memset(addr, 0, sizeof(struct sockaddr_ipx)); |
325 | |
|
326 | 0 | DEBUGMSGTL(("netsnmp_sockaddr_ipx", |
327 | 0 | "addr %p, peername \"%s\" default_target \"%s\"\n", |
328 | 0 | addr, peername ? peername : "[NIL]", |
329 | 0 | default_target ? default_target : "[NIL]")); |
330 | |
|
331 | 0 | addr->sipx_family = AF_IPX; |
332 | 0 | addr->sipx_type = 4; /* Specified in RFC 1420. */ |
333 | |
|
334 | 0 | network = input = strdup(peername ? peername : ""); |
335 | 0 | tmp = strchr(input, ':'); |
336 | 0 | if (tmp != NULL) { |
337 | 0 | DEBUGMSGTL(("netsnmp_sockaddr_ipx", "Node identified\n")); |
338 | 0 | *tmp++ = '\0'; |
339 | 0 | node = tmp; |
340 | 0 | tmp = strchr(tmp, '/'); |
341 | 0 | } else { |
342 | 0 | node = NULL; |
343 | 0 | tmp = strchr(input, '/'); |
344 | 0 | } |
345 | 0 | if (tmp != NULL) { |
346 | 0 | DEBUGMSGTL(("netsnmp_sockaddr_ipx", "Port identified\n")); |
347 | 0 | *tmp++ = '\0'; |
348 | 0 | port = tmp; |
349 | 0 | } else |
350 | 0 | port = NULL; |
351 | |
|
352 | 0 | DEBUGMSGTL(("netsnmp_sockaddr_ipx", "Address: %s:%s/%s\n", |
353 | 0 | network ? network : "[NIL]", node ? node : "[NIL]", |
354 | 0 | port ? port : "[NIL]")); |
355 | |
|
356 | 0 | def = strdup(default_target ? default_target : ""); |
357 | 0 | if (network == NULL || *network == '\0') |
358 | 0 | network = def; |
359 | 0 | tmp = strchr(def, ':'); |
360 | 0 | if (tmp != NULL) { |
361 | 0 | *tmp++ = '\0'; |
362 | 0 | if (node == NULL || *node == '\0') |
363 | 0 | node = tmp; |
364 | 0 | tmp = strchr(tmp, '/'); |
365 | 0 | } else |
366 | 0 | tmp = strchr(def, '/'); |
367 | 0 | if (tmp != NULL) { |
368 | 0 | *tmp++ = '\0'; |
369 | 0 | if (port == NULL || *port == '\0') |
370 | 0 | port = tmp; |
371 | 0 | } |
372 | |
|
373 | 0 | DEBUGMSGTL(("netsnmp_sockaddr_ipx", "Address: %s:%s/%s\n", |
374 | 0 | network ? network : "[NIL]", node ? node : "[NIL]", |
375 | 0 | port ? port : "[NIL]")); |
376 | |
|
377 | 0 | if (network == NULL || *network == '\0') |
378 | 0 | network = "0"; |
379 | |
|
380 | 0 | if (node == NULL || *node == '\0') |
381 | 0 | node = "000000000000"; |
382 | |
|
383 | 0 | if (port == NULL || *port == '\0') |
384 | 0 | #define val(x) __STRING(x) |
385 | 0 | port = val(SNMP_IPX_DEFAULT_PORT); |
386 | 0 | #undef val |
387 | |
|
388 | 0 | DEBUGMSGTL(("netsnmp_sockaddr_ipx", "Address: %s:%s/%s\n", |
389 | 0 | network ? network : "[NIL]", node ? node : "[NIL]", |
390 | 0 | port ? port : "[NIL]")); |
391 | |
|
392 | 0 | if(sscanf(network, "%8lx%*c", &i) == 1) { |
393 | 0 | DEBUGMSGTL(("netsnmp_sockaddr_ipx", "network parsed okay\n")); |
394 | 0 | addr->sipx_network = htonl(i); |
395 | 0 | } else { |
396 | 0 | DEBUGMSGTL(("netsnmp_sockaddr_ipx", |
397 | 0 | "failed to parse network part of address\n")); |
398 | 0 | free(def); |
399 | 0 | free(input); |
400 | 0 | return 0; |
401 | 0 | } |
402 | | |
403 | 0 | if(sscanf(node, "%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%*c", |
404 | 0 | &addr->sipx_node[0], &addr->sipx_node[1], |
405 | 0 | &addr->sipx_node[2], &addr->sipx_node[3], |
406 | 0 | &addr->sipx_node[4], &addr->sipx_node[5]) == 6) { |
407 | 0 | DEBUGMSGTL(("netsnmp_sockaddr_ipx", "node parsed okay\n")); |
408 | 0 | } else { |
409 | 0 | DEBUGMSGTL(("netsnmp_sockaddr_ipx", |
410 | 0 | "failed to parse node part of address\n")); |
411 | 0 | free(def); |
412 | 0 | free(input); |
413 | 0 | return 0; |
414 | 0 | } |
415 | | |
416 | 0 | if(sscanf(port, "%lu%*c", &i) == 1) { |
417 | 0 | DEBUGMSGTL(("netsnmp_sockaddr_ipx", "port parsed okay\n")); |
418 | 0 | addr->sipx_port = htons(i); |
419 | 0 | } else { |
420 | 0 | DEBUGMSGTL(("netsnmp_sockaddr_ipx", |
421 | 0 | "failed to parse port part of address\n")); |
422 | 0 | free(def); |
423 | 0 | free(input); |
424 | 0 | return 0; |
425 | 0 | } |
426 | | |
427 | 0 | free(def); |
428 | 0 | free(input); |
429 | 0 | return 1; |
430 | 0 | } |
431 | | |
432 | | |
433 | | |
434 | | int |
435 | | netsnmp_sockaddr_ipx(struct sockaddr_ipx *addr, const char *peername) |
436 | 0 | { |
437 | 0 | return netsnmp_sockaddr_ipx2(addr, peername, NULL); |
438 | 0 | } |
439 | | |
440 | | |
441 | | |
442 | | netsnmp_transport * |
443 | | netsnmp_ipx_create_tstring(const char *str, int local, |
444 | | const char *default_target) |
445 | 0 | { |
446 | 0 | struct sockaddr_ipx addr; |
447 | |
|
448 | 0 | if (netsnmp_sockaddr_ipx2(&addr, str, default_target)) { |
449 | 0 | return netsnmp_ipx_transport(&addr, local); |
450 | 0 | } else { |
451 | 0 | return NULL; |
452 | 0 | } |
453 | 0 | } |
454 | | |
455 | | static int netsnmp_ipx_ostring_to_sockaddr(struct sockaddr_ipx *sa, |
456 | | const void *o, size_t o_len) |
457 | 0 | { |
458 | 0 | const char *p = o; |
459 | |
|
460 | 0 | if (o_len != 12) |
461 | 0 | return 0; |
462 | | |
463 | 0 | memset(sa, 0, sizeof(*sa)); |
464 | 0 | sa->sipx_family = AF_IPX; |
465 | 0 | memcpy(&sa->sipx_network, p + 0, 4); |
466 | 0 | memcpy(&sa->sipx_node, p + 4, 6); |
467 | 0 | memcpy(&sa->sipx_port, p + 10, 2); |
468 | 0 | return 1; |
469 | 0 | } |
470 | | |
471 | | netsnmp_transport * |
472 | | netsnmp_ipx_create_ostring(const void *o, size_t o_len, int local) |
473 | 0 | { |
474 | 0 | struct sockaddr_ipx sa; |
475 | |
|
476 | 0 | if (netsnmp_ipx_ostring_to_sockaddr(&sa, o, o_len)) |
477 | 0 | return netsnmp_ipx_transport(&sa, local); |
478 | | |
479 | 0 | return NULL; |
480 | 0 | } |
481 | | |
482 | | |
483 | | |
484 | | void |
485 | | netsnmp_ipx_ctor(void) |
486 | 4.35k | { |
487 | 4.35k | ipxDomain.name = netsnmpIPXDomain; |
488 | 4.35k | ipxDomain.name_length = netsnmpIPXDomain_len; |
489 | 4.35k | ipxDomain.prefix = calloc(2, sizeof(char *)); |
490 | 4.35k | if (!ipxDomain.prefix) { |
491 | 0 | snmp_log(LOG_ERR, "calloc() failed - out of memory\n"); |
492 | 0 | return; |
493 | 0 | } |
494 | 4.35k | ipxDomain.prefix[0] = "ipx"; |
495 | | |
496 | 4.35k | ipxDomain.f_create_from_tstring_new = netsnmp_ipx_create_tstring; |
497 | 4.35k | ipxDomain.f_create_from_ostring = netsnmp_ipx_create_ostring; |
498 | | |
499 | 4.35k | netsnmp_tdomain_register(&ipxDomain); |
500 | 4.35k | } |