1 #include <net-snmp/net-snmp-config.h>
2 #include <net-snmp/net-snmp-features.h>
3
4 #include <sys/types.h>
5 #include <net-snmp/library/snmpUnixDomain.h>
6
7 #include <stddef.h>
8 #include <stdio.h>
9 #include <ctype.h>
10 #include <errno.h>
11
12 #if HAVE_STRING_H
13 #include <string.h>
14 #else
15 #include <strings.h>
16 #endif
17 #if HAVE_STDLIB_H
18 #include <stdlib.h>
19 #endif
20 #if HAVE_UNISTD_H
21 #include <unistd.h>
22 #endif
23 #if HAVE_SYS_SOCKET_H
24 #include <sys/socket.h>
25 #endif
26
27 #include <net-snmp/types.h>
28 #include <net-snmp/output_api.h>
29 #include <net-snmp/config_api.h>
30
31 #include <net-snmp/library/snmp_transport.h>
32 #include <net-snmp/library/snmpSocketBaseDomain.h>
33 #include <net-snmp/library/system.h> /* mkdirhier */
34 #include <net-snmp/library/tools.h>
35
36 #ifndef NETSNMP_NO_SYSTEMD
37 #include <net-snmp/library/sd-daemon.h>
38 #endif
39
40 netsnmp_feature_child_of(transport_unix_socket_all, transport_all);
41 netsnmp_feature_child_of(unix_socket_paths, transport_unix_socket_all);
42
43 #ifndef NETSNMP_STREAM_QUEUE_LEN
44 #define NETSNMP_STREAM_QUEUE_LEN 5
45 #endif
46
47 #ifndef SUN_LEN
48 /*
49 * Evaluate to actual length of the `sockaddr_un' structure.
50 */
51 #define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \
52 + strlen ((ptr)->sun_path))
53 #endif
54
55 oid netsnmp_UnixDomain[] = { TRANSPORT_DOMAIN_LOCAL };
56 static netsnmp_tdomain unixDomain;
57
58
59 /*
60 * This is the structure we use to hold transport-specific data.
61 */
62
63 typedef struct _sockaddr_un_pair {
64 int local;
65 struct sockaddr_un server;
66 struct sockaddr_un client;
67 } sockaddr_un_pair;
68
69
70 /*
71 * Return a string representing the address in data, or else the "far end"
72 * address if data is NULL.
73 */
74
75 static char *
netsnmp_unix_fmtaddr(netsnmp_transport * t,const void * data,int len)76 netsnmp_unix_fmtaddr(netsnmp_transport *t, const void *data, int len)
77 {
78 const struct sockaddr_un *to = NULL;
79
80 if (data != NULL)
81 to = (const struct sockaddr_un *) data;
82 else if (t != NULL && t->data != NULL)
83 to = &(((const sockaddr_un_pair *) t->data)->server);
84 if (to == NULL) {
85 /*
86 * "Local IPC" is the Posix.1g term for Unix domain protocols,
87 * according to W. R. Stevens, ``Unix Network Programming Volume I
88 * Second Edition'', p. 374.
89 */
90 return strdup("Local IPC: unknown");
91 } else if (to->sun_path[0] == 0) {
92 /*
93 * This is an abstract name. We could render it as hex or something
94 * but let's not worry about that for now.
95 */
96 return strdup("Local IPC: abstract");
97 } else {
98 char *tmp;
99
100 if (asprintf(&tmp, "Local IPC: %s", to->sun_path) < 0)
101 tmp = NULL;
102 return tmp;
103 }
104 }
105
106 static void
netsnmp_unix_get_taddr(netsnmp_transport * t,void ** addr,size_t * addr_len)107 netsnmp_unix_get_taddr(netsnmp_transport *t, void **addr, size_t *addr_len)
108 {
109 *addr_len = t->remote_length;
110 *addr = netsnmp_memdup(t->remote, *addr_len);
111 }
112
113 /*
114 * You can write something into opaque that will subsequently get passed back
115 * to your send function if you like. For instance, you might want to
116 * remember where a PDU came from, so that you can send a reply there...
117 */
118
119 static int
netsnmp_unix_recv(netsnmp_transport * t,void * buf,int size,void ** opaque,int * olength)120 netsnmp_unix_recv(netsnmp_transport *t, void *buf, int size,
121 void **opaque, int *olength)
122 {
123 int rc = -1;
124 socklen_t tolen = sizeof(struct sockaddr_un);
125 struct sockaddr *to;
126
127
128 if (t != NULL && t->sock >= 0) {
129 to = (struct sockaddr *) malloc(sizeof(struct sockaddr_un));
130 if (to == NULL) {
131 *opaque = NULL;
132 *olength = 0;
133 return -1;
134 } else {
135 memset(to, 0, tolen);
136 }
137 if(getsockname(t->sock, to, &tolen) != 0){
138 free(to);
139 *opaque = NULL;
140 *olength = 0;
141 return -1;
142 };
143 while (rc < 0) {
144 #ifdef MSG_DONTWAIT
145 rc = recvfrom(t->sock, buf, size, MSG_DONTWAIT, NULL, NULL);
146 #else
147 rc = recvfrom(t->sock, buf, size, 0, NULL, NULL);
148 #endif
149 if (rc < 0 && errno != EINTR) {
150 DEBUGMSGTL(("netsnmp_unix", "recv fd %d err %d (\"%s\")\n",
151 t->sock, errno, strerror(errno)));
152 return rc;
153 }
154 *opaque = (void*)to;
155 *olength = sizeof(struct sockaddr_un);
156 }
157 DEBUGMSGTL(("netsnmp_unix", "recv fd %d got %d bytes\n", t->sock, rc));
158 }
159 return rc;
160 }
161
162
163
164 static int
netsnmp_unix_send(netsnmp_transport * t,const void * buf,int size,void ** opaque,int * olength)165 netsnmp_unix_send(netsnmp_transport *t, const void *buf, int size,
166 void **opaque, int *olength)
167 {
168 int rc = -1;
169
170 if (t != NULL && t->sock >= 0) {
171 DEBUGMSGTL(("netsnmp_unix", "send %d bytes to %p on fd %d\n",
172 size, buf, t->sock));
173 while (rc < 0) {
174 rc = sendto(t->sock, buf, size, 0, NULL, 0);
175 if (rc < 0 && errno != EINTR) {
176 break;
177 }
178 }
179 }
180 return rc;
181 }
182
183
184
185 static int
netsnmp_unix_close(netsnmp_transport * t)186 netsnmp_unix_close(netsnmp_transport *t)
187 {
188 int rc = 0;
189 sockaddr_un_pair *sup = (sockaddr_un_pair *) t->data;
190
191 if (t->sock >= 0) {
192 #ifndef HAVE_CLOSESOCKET
193 rc = close(t->sock);
194 #else
195 rc = closesocket(t->sock);
196 #endif
197 t->sock = -1;
198 if (sup != NULL) {
199 if (sup->local) {
200 if (sup->server.sun_path[0] != 0) {
201 DEBUGMSGTL(("netsnmp_unix", "close: server unlink(\"%s\")\n",
202 sup->server.sun_path));
203 unlink(sup->server.sun_path);
204 }
205 } else {
206 if (sup->client.sun_path[0] != 0) {
207 DEBUGMSGTL(("netsnmp_unix", "close: client unlink(\"%s\")\n",
208 sup->client.sun_path));
209 unlink(sup->client.sun_path);
210 }
211 }
212 }
213 return rc;
214 } else {
215 return -1;
216 }
217 }
218
219
220
221 static int
netsnmp_unix_accept(netsnmp_transport * t)222 netsnmp_unix_accept(netsnmp_transport *t)
223 {
224 struct sockaddr *farend = NULL;
225 int newsock = -1;
226 socklen_t farendlen = sizeof(struct sockaddr_un);
227
228 farend = (struct sockaddr *) malloc(farendlen);
229
230 if (farend == NULL) {
231 /*
232 * Indicate that the acceptance of this socket failed.
233 */
234 DEBUGMSGTL(("netsnmp_unix", "accept: malloc failed\n"));
235 return -1;
236 }
237 memset(farend, 0, farendlen);
238
239 if (t != NULL && t->sock >= 0) {
240 newsock = accept(t->sock, farend, &farendlen);
241
242 if (newsock < 0) {
243 DEBUGMSGTL(("netsnmp_unix","accept failed rc %d errno %d \"%s\"\n",
244 newsock, errno, strerror(errno)));
245 free(farend);
246 return newsock;
247 }
248
249 if (t->data != NULL) {
250 free(t->data);
251 }
252
253 DEBUGMSGTL(("netsnmp_unix", "accept succeeded (farend %p len %d)\n",
254 farend, (int) farendlen));
255 t->data = farend;
256 t->data_length = sizeof(struct sockaddr_un);
257 netsnmp_sock_buffer_set(newsock, SO_SNDBUF, 1, 0);
258 netsnmp_sock_buffer_set(newsock, SO_RCVBUF, 1, 0);
259 return newsock;
260 } else {
261 free(farend);
262 return -1;
263 }
264 }
265
266 static int create_path = 0;
267 static mode_t create_mode;
268
269 #ifndef NETSNMP_FEATURE_REMOVE_UNIX_SOCKET_PATHS
270 /** If trying to create unix sockets in nonexisting directories then
271 * try to create the directory with mask mode.
272 */
netsnmp_unix_create_path_with_mode(int mode)273 void netsnmp_unix_create_path_with_mode(int mode)
274 {
275 create_path = 1;
276 create_mode = mode;
277 }
278
279 /** If trying to create unix sockets in nonexisting directories then
280 * fail.
281 */
netsnmp_unix_dont_create_path(void)282 void netsnmp_unix_dont_create_path(void)
283 {
284 create_path = 0;
285 }
286 #endif /* NETSNMP_FEATURE_REMOVE_UNIX_SOCKET_PATHS */
287
288 /*
289 * Open a Unix-domain transport for SNMP. Local is TRUE if addr is the local
290 * address to bind to (i.e. this is a server-type session); otherwise addr is
291 * the remote address to send things to (and we make up a temporary name for
292 * the local end of the connection).
293 */
294
295 netsnmp_transport *
netsnmp_unix_transport(const struct sockaddr_un * addr,int local)296 netsnmp_unix_transport(const struct sockaddr_un *addr, int local)
297 {
298 netsnmp_transport *t = NULL;
299 sockaddr_un_pair *sup = NULL;
300 int rc = 0;
301 int socket_initialized = 0;
302
303 #ifdef NETSNMP_NO_LISTEN_SUPPORT
304 /* SPECIAL CIRCUMSTANCE: We still want AgentX to be able to operate,
305 so we allow for unix domain socktes to still listen when everything
306 else isn't allowed to. Thus, we ignore this define in this file.
307 */
308 #endif /* NETSNMP_NO_LISTEN_SUPPORT */
309
310 if (addr == NULL || addr->sun_family != AF_UNIX) {
311 return NULL;
312 }
313
314 t = SNMP_MALLOC_TYPEDEF(netsnmp_transport);
315 if (t == NULL) {
316 return NULL;
317 }
318
319 DEBUGIF("netsnmp_unix") {
320 char *str = netsnmp_unix_fmtaddr(NULL, addr,
321 sizeof(struct sockaddr_un));
322 DEBUGMSGTL(("netsnmp_unix", "open %s %s\n", local ? "local" : "remote",
323 str));
324 free(str);
325 }
326
327 t->domain = netsnmp_UnixDomain;
328 t->domain_length =
329 sizeof(netsnmp_UnixDomain) / sizeof(netsnmp_UnixDomain[0]);
330
331 t->data = malloc(sizeof(sockaddr_un_pair));
332 if (t->data == NULL) {
333 netsnmp_transport_free(t);
334 return NULL;
335 }
336 memset(t->data, 0, sizeof(sockaddr_un_pair));
337 t->data_length = sizeof(sockaddr_un_pair);
338 sup = (sockaddr_un_pair *) t->data;
339
340 #ifndef NETSNMP_NO_SYSTEMD
341 /*
342 * Maybe the socket was already provided by systemd...
343 */
344 if (local) {
345 t->sock = netsnmp_sd_find_unix_socket(SOCK_STREAM, 1, addr->sun_path);
346 if (t->sock >= 0)
347 socket_initialized = 1;
348 }
349 #endif
350 if (!socket_initialized)
351 t->sock = socket(PF_UNIX, SOCK_STREAM, 0);
352 if (t->sock < 0) {
353 netsnmp_transport_free(t);
354 return NULL;
355 }
356
357 t->flags = NETSNMP_TRANSPORT_FLAG_STREAM;
358
359 if (local) {
360 t->local_length = strlen(addr->sun_path);
361 t->local = strdup(addr->sun_path);
362 if (t->local == NULL) {
363 netsnmp_transport_free(t);
364 return NULL;
365 }
366
367 /*
368 * This session is inteneded as a server, so we must bind to the given
369 * path (unlinking it first, to avoid errors).
370 */
371
372 t->flags |= NETSNMP_TRANSPORT_FLAG_LISTEN;
373
374 if (!socket_initialized) {
375 unlink(addr->sun_path);
376 rc = bind(t->sock, (const struct sockaddr *)addr, SUN_LEN(addr));
377 if (rc != 0 && errno == ENOENT && create_path) {
378 rc = mkdirhier(addr->sun_path, create_mode, 1);
379 if (rc != 0) {
380 netsnmp_unix_close(t);
381 netsnmp_transport_free(t);
382 return NULL;
383 }
384 rc = bind(t->sock, (const struct sockaddr *)addr,
385 SUN_LEN(addr));
386 }
387 if (rc != 0) {
388 DEBUGMSGTL(("netsnmp_unix_transport",
389 "couldn't bind \"%s\", errno %d (%s)\n",
390 addr->sun_path, errno, strerror(errno)));
391 netsnmp_unix_close(t);
392 netsnmp_transport_free(t);
393 return NULL;
394 }
395 }
396
397 /*
398 * Save the address in the transport-specific data pointer for later
399 * use by netsnmp_unix_close.
400 */
401
402 sup->server.sun_family = AF_UNIX;
403 strcpy(sup->server.sun_path, addr->sun_path);
404 sup->local = 1;
405
406 /*
407 * Now sit here and listen for connections to arrive.
408 */
409
410 if (!socket_initialized) {
411 rc = listen(t->sock, NETSNMP_STREAM_QUEUE_LEN);
412 if (rc != 0) {
413 DEBUGMSGTL(("netsnmp_unix_transport",
414 "couldn't listen to \"%s\", errno %d (%s)\n",
415 addr->sun_path, errno, strerror(errno)));
416 netsnmp_unix_close(t);
417 netsnmp_transport_free(t);
418 return NULL;
419 }
420 }
421 } else {
422 t->remote_length = strlen(addr->sun_path);
423 t->remote = strdup(addr->sun_path);
424 if (t->remote == NULL) {
425 netsnmp_transport_free(t);
426 return NULL;
427 }
428
429 rc = connect(t->sock, (const struct sockaddr *)addr,
430 sizeof(struct sockaddr_un));
431 if (rc != 0) {
432 DEBUGMSGTL(("netsnmp_unix_transport",
433 "couldn't connect to \"%s\", errno %d (%s)\n",
434 addr->sun_path, errno, strerror(errno)));
435 netsnmp_unix_close(t);
436 netsnmp_transport_free(t);
437 return NULL;
438 }
439
440 /*
441 * Save the remote address in the transport-specific data pointer for
442 * later use by netsnmp_unix_send.
443 */
444
445 sup->server.sun_family = AF_UNIX;
446 strcpy(sup->server.sun_path, addr->sun_path);
447 sup->local = 0;
448 netsnmp_sock_buffer_set(t->sock, SO_SNDBUF, local, 0);
449 netsnmp_sock_buffer_set(t->sock, SO_RCVBUF, local, 0);
450 }
451
452 /*
453 * Message size is not limited by this transport (hence msgMaxSize
454 * is equal to the maximum legal size of an SNMP message).
455 */
456
457 t->msgMaxSize = SNMP_MAX_PACKET_LEN;
458 t->f_recv = netsnmp_unix_recv;
459 t->f_send = netsnmp_unix_send;
460 t->f_close = netsnmp_unix_close;
461 t->f_accept = netsnmp_unix_accept;
462 t->f_fmtaddr = netsnmp_unix_fmtaddr;
463 t->f_get_taddr = netsnmp_unix_get_taddr;
464
465 return t;
466 }
467
468 netsnmp_transport *
netsnmp_unix_create_tstring(const char * string,int local,const char * default_target)469 netsnmp_unix_create_tstring(const char *string, int local,
470 const char *default_target)
471 {
472 struct sockaddr_un addr;
473
474 if (string && *string != '\0') {
475 } else if (default_target && *default_target != '\0') {
476 string = default_target;
477 }
478
479 if ((string != NULL && *string != '\0') &&
480 (strlen(string) < sizeof(addr.sun_path))) {
481 addr.sun_family = AF_UNIX;
482 memset(addr.sun_path, 0, sizeof(addr.sun_path));
483 strlcpy(addr.sun_path, string, sizeof(addr.sun_path));
484 return netsnmp_unix_transport(&addr, local);
485 } else {
486 if (string != NULL && *string != '\0') {
487 snmp_log(LOG_ERR, "Path too long for Unix domain transport\n");
488 }
489 return NULL;
490 }
491 }
492
493
494
495 netsnmp_transport *
netsnmp_unix_create_ostring(const void * ostring,size_t o_len,int local)496 netsnmp_unix_create_ostring(const void *ostring, size_t o_len, int local)
497 {
498 struct sockaddr_un addr;
499
500 if (o_len > 0 && o_len < (sizeof(addr.sun_path) - 1)) {
501 addr.sun_family = AF_UNIX;
502 memset(addr.sun_path, 0, sizeof(addr.sun_path));
503 strlcpy(addr.sun_path, ostring, sizeof(addr.sun_path));
504 return netsnmp_unix_transport(&addr, local);
505 } else {
506 if (o_len > 0) {
507 snmp_log(LOG_ERR, "Path too long for Unix domain transport\n");
508 }
509 }
510 return NULL;
511 }
512
513
514
515 void
netsnmp_unix_ctor(void)516 netsnmp_unix_ctor(void)
517 {
518 unixDomain.name = netsnmp_UnixDomain;
519 unixDomain.name_length = sizeof(netsnmp_UnixDomain) / sizeof(oid);
520 unixDomain.prefix = (const char**)calloc(2, sizeof(char *));
521 unixDomain.prefix[0] = "unix";
522
523 unixDomain.f_create_from_tstring_new = netsnmp_unix_create_tstring;
524 unixDomain.f_create_from_ostring = netsnmp_unix_create_ostring;
525
526 netsnmp_tdomain_register(&unixDomain);
527 }
528
529 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
530 /* support for SNMPv1 and SNMPv2c on unix domain*/
531
532 #define EXAMPLE_COMMUNITY "COMMUNITY"
533 typedef struct com2SecUnixEntry_s {
534 const char* sockpath;
535 const char* secName;
536 const char* contextName;
537 struct com2SecUnixEntry_s *next;
538 unsigned short pathlen;
539 const char community[1];
540 } com2SecUnixEntry;
541
542 static com2SecUnixEntry *com2SecUnixList = NULL, *com2SecUnixListLast = NULL;
543
544
545 int
netsnmp_unix_getSecName(void * opaque,int olength,const char * community,size_t community_len,const char ** secName,const char ** contextName)546 netsnmp_unix_getSecName(void *opaque, int olength,
547 const char *community,
548 size_t community_len,
549 const char **secName, const char **contextName)
550 {
551 const com2SecUnixEntry *c;
552 struct sockaddr_un *to = (struct sockaddr_un *) opaque;
553 char *ztcommunity = NULL;
554
555 if (secName != NULL) {
556 *secName = NULL; /* Haven't found anything yet */
557 }
558
559 /*
560 * Special case if there are NO entries (as opposed to no MATCHING
561 * entries).
562 */
563
564 if (com2SecUnixList == NULL) {
565 DEBUGMSGTL(("netsnmp_unix_getSecName", "no com2sec entries\n"));
566 return 0;
567 }
568
569 /*
570 * If there is no unix socket path, then there can be no valid security
571 * name.
572 */
573
574 if (opaque == NULL || olength != sizeof(struct sockaddr_un) ||
575 to->sun_family != AF_UNIX) {
576 DEBUGMSGTL(("netsnmp_unix_getSecName",
577 "no unix destine address in PDU?\n"));
578 return 1;
579 }
580
581 DEBUGIF("netsnmp_unix_getSecName") {
582 ztcommunity = (char *)malloc(community_len + 1);
583 if (ztcommunity != NULL) {
584 memcpy(ztcommunity, community, community_len);
585 ztcommunity[community_len] = '\0';
586 }
587
588 DEBUGMSGTL(("netsnmp_unix_getSecName", "resolve <\"%s\">\n",
589 ztcommunity ? ztcommunity : "<malloc error>"));
590 }
591
592 for (c = com2SecUnixList; c != NULL; c = c->next) {
593 DEBUGMSGTL(("netsnmp_unix_getSecName","compare <\"%s\",to socket %s>",
594 c->community, c->sockpath ));
595 if ((community_len == strlen(c->community)) &&
596 (memcmp(community, c->community, community_len) == 0) &&
597 /* compare sockpath, if pathlen == 0, always match */
598 (strlen(to->sun_path) == c->pathlen || c->pathlen == 0) &&
599 (memcmp(to->sun_path, c->sockpath, c->pathlen) == 0)
600 ) {
601 DEBUGMSG(("netsnmp_unix_getSecName", "... SUCCESS\n"));
602 if (secName != NULL) {
603 *secName = c->secName;
604 *contextName = c->contextName;
605 }
606 break;
607 }
608 DEBUGMSG(("netsnmp_unix_getSecName", "... nope\n"));
609 }
610 if (ztcommunity != NULL) {
611 free(ztcommunity);
612 }
613 return 1;
614 }
615
616 void
netsnmp_unix_parse_security(const char * token,char * param)617 netsnmp_unix_parse_security(const char *token, char *param)
618 {
619 char secName[VACMSTRINGLEN + 1];
620 size_t secNameLen;
621 char contextName[VACMSTRINGLEN + 1];
622 size_t contextNameLen;
623 char community[COMMUNITY_MAX_LEN + 1];
624 size_t communityLen;
625 char sockpath[sizeof(((struct sockaddr_un*)0)->sun_path) + 1];
626 size_t sockpathLen;
627
628 param = copy_nword( param, secName, sizeof(secName));
629 if (strcmp(secName, "-Cn") == 0) {
630 if (!param) {
631 config_perror("missing CONTEXT_NAME parameter");
632 return;
633 }
634 param = copy_nword( param, contextName, sizeof(contextName));
635 contextNameLen = strlen(contextName) + 1;
636 if (contextNameLen > VACMSTRINGLEN) {
637 config_perror("context name too long");
638 return;
639 }
640 if (!param) {
641 config_perror("missing NAME parameter");
642 return;
643 }
644 param = copy_nword( param, secName, sizeof(secName));
645 } else {
646 contextNameLen = 0;
647 }
648
649 secNameLen = strlen(secName) + 1;
650 if (secNameLen == 1) {
651 config_perror("empty NAME parameter");
652 return;
653 } else if (secNameLen > VACMSTRINGLEN) {
654 config_perror("security name too long");
655 return;
656 }
657
658 if (!param) {
659 config_perror("missing SOCKPATH parameter");
660 return;
661 }
662 param = copy_nword( param, sockpath, sizeof(sockpath));
663 if (sockpath[0] == '\0') {
664 config_perror("empty SOCKPATH parameter");
665 return;
666 }
667 sockpathLen = strlen(sockpath) + 1;
668 if (sockpathLen > sizeof(((struct sockaddr_un*)0)->sun_path)) {
669 config_perror("sockpath too long");
670 return;
671 }
672
673 if (!param) {
674 config_perror("missing COMMUNITY parameter");
675 return;
676 }
677 param = copy_nword( param, community, sizeof(community));
678 if (community[0] == '\0') {
679 config_perror("empty COMMUNITY parameter");
680 return;
681 }
682 communityLen = strlen(community) + 1;
683 if (communityLen >= COMMUNITY_MAX_LEN) {
684 config_perror("community name too long");
685 return;
686 }
687 if (communityLen == sizeof(EXAMPLE_COMMUNITY) &&
688 memcmp(community, EXAMPLE_COMMUNITY, sizeof(EXAMPLE_COMMUNITY)) == 0) {
689 config_perror("example config COMMUNITY not properly configured");
690 return;
691 }
692
693 /* Deal with the "default" case */
694 if(strcmp(sockpath, "default") == 0) {
695 sockpathLen = 0;
696 }
697
698 {
699 void* v = malloc(offsetof(com2SecUnixEntry, community) + communityLen +
700 sockpathLen + secNameLen + contextNameLen);
701 com2SecUnixEntry* e = (com2SecUnixEntry*)v;
702 char* last = ((char*)v) + offsetof(com2SecUnixEntry, community);
703 if (e == NULL) {
704 config_perror("memory error");
705 return;
706 }
707
708 DEBUGMSGTL(("netsnmp_unix_parse_security",
709 "<\"%s\", \"%.*s\"> => \"%s\"\n",
710 community, (int)sockpathLen, sockpath, secName));
711
712 memcpy(last, community, communityLen);
713 last += communityLen;
714
715 if (sockpathLen) {
716 e->sockpath = last;
717 memcpy(last, sockpath, sockpathLen);
718 last += sockpathLen;
719 e->pathlen = sockpathLen - 1;
720 } else {
721 e->sockpath = last - 1;
722 e->pathlen = 0;
723 }
724
725 e->secName = last;
726 memcpy(last, secName, secNameLen);
727 last += secNameLen;
728
729 if (contextNameLen) {
730 e->contextName = last;
731 memcpy(last, contextName, contextNameLen);
732 last += contextNameLen;
733 } else
734 e->contextName = last - 1;
735
736 e->next = NULL;
737
738 if (com2SecUnixListLast != NULL) {
739 com2SecUnixListLast->next = e;
740 com2SecUnixListLast = e;
741 } else {
742 com2SecUnixListLast = com2SecUnixList = e;
743 }
744 }
745 }
746
747 void
netsnmp_unix_com2SecList_free(void)748 netsnmp_unix_com2SecList_free(void)
749 {
750 com2SecUnixEntry *e = com2SecUnixList;
751 while (e != NULL) {
752 com2SecUnixEntry *tmp = e;
753 e = e->next;
754 free(tmp);
755 }
756 com2SecUnixList = com2SecUnixListLast = NULL;
757 }
758 #endif /* support for community based SNMP */
759
760 void
netsnmp_unix_agent_config_tokens_register(void)761 netsnmp_unix_agent_config_tokens_register(void)
762 {
763 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
764 register_app_config_handler("com2secunix", netsnmp_unix_parse_security,
765 netsnmp_unix_com2SecList_free,
766 "[-Cn CONTEXT] secName sockpath community");
767 #endif /* support for community based SNMP */
768 }
769