00001 #include <net-snmp/net-snmp-config.h>
00002
00003 #include <stdio.h>
00004 #include <sys/types.h>
00005 #include <ctype.h>
00006 #include <errno.h>
00007
00008 #if HAVE_STRING_H
00009 #include <string.h>
00010 #else
00011 #include <strings.h>
00012 #endif
00013 #if HAVE_STDLIB_H
00014 #include <stdlib.h>
00015 #endif
00016 #if HAVE_UNISTD_H
00017 #include <unistd.h>
00018 #endif
00019 #if HAVE_SYS_SOCKET_H
00020 #include <sys/socket.h>
00021 #endif
00022 #if HAVE_SYS_UN_H
00023 #include <sys/un.h>
00024 #endif
00025
00026 #if HAVE_DMALLOC_H
00027 #include <dmalloc.h>
00028 #endif
00029
00030 #include <net-snmp/types.h>
00031 #include <net-snmp/output_api.h>
00032 #include <net-snmp/config_api.h>
00033
00034 #include <net-snmp/library/snmp_transport.h>
00035 #include <net-snmp/library/snmpUDPDomain.h>
00036 #include <net-snmp/library/snmpUnixDomain.h>
00037 #include <net-snmp/library/system.h>
00038
00039
00040 #ifndef NETSNMP_STREAM_QUEUE_LEN
00041 #define NETSNMP_STREAM_QUEUE_LEN 5
00042 #endif
00043
00044 #ifndef SUN_LEN
00045
00046
00047
00048 #define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \
00049 + strlen ((ptr)->sun_path))
00050 #endif
00051
00052 oid netsnmp_UnixDomain[] = { TRANSPORT_DOMAIN_LOCAL };
00053 static netsnmp_tdomain unixDomain;
00054
00055
00056
00057
00058
00059
00060 typedef struct _sockaddr_un_pair {
00061 int local;
00062 struct sockaddr_un server;
00063 struct sockaddr_un client;
00064 } sockaddr_un_pair;
00065
00066
00067
00068
00069
00070
00071
00072 static char *
00073 netsnmp_unix_fmtaddr(netsnmp_transport *t, void *data, int len)
00074 {
00075 struct sockaddr_un *to = NULL;
00076
00077 if (data != NULL) {
00078 to = (struct sockaddr_un *) data;
00079 } else if (t != NULL && t->data != NULL) {
00080 to = &(((sockaddr_un_pair *) t->data)->server);
00081 len = SUN_LEN(to);
00082 }
00083 if (to == NULL) {
00084
00085
00086
00087
00088
00089 return strdup("Local IPC: unknown");
00090 } else if (to->sun_path[0] == 0) {
00091
00092
00093
00094
00095 return strdup("Local IPC: abstract");
00096 } else {
00097 char *tmp = (char *) malloc(16 + len);
00098 if (tmp != NULL) {
00099 sprintf(tmp, "Local IPC: %s", to->sun_path);
00100 }
00101 return tmp;
00102 }
00103 }
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113 static int
00114 netsnmp_unix_recv(netsnmp_transport *t, void *buf, int size,
00115 void **opaque, int *olength)
00116 {
00117 int rc = -1;
00118 socklen_t tolen = sizeof(struct sockaddr_un);
00119 struct sockaddr *to;
00120
00121
00122 if (t != NULL && t->sock >= 0) {
00123 to = (struct sockaddr *) malloc(sizeof(struct sockaddr_un));
00124 if (to == NULL) {
00125 *opaque = NULL;
00126 *olength = 0;
00127 return -1;
00128 } else {
00129 memset(to, 0, tolen);
00130 }
00131 if(getsockname(t->sock, to, &tolen) != 0){
00132 free(to);
00133 *opaque = NULL;
00134 *olength = 0;
00135 return -1;
00136 };
00137 while (rc < 0) {
00138 rc = recvfrom(t->sock, buf, size, 0, NULL, NULL);
00139 if (rc < 0 && errno != EINTR) {
00140 DEBUGMSGTL(("netsnmp_unix", "recv fd %d err %d (\"%s\")\n",
00141 t->sock, errno, strerror(errno)));
00142 return rc;
00143 }
00144 *opaque = (void*)to;
00145 *olength = sizeof(struct sockaddr_un);
00146 }
00147 DEBUGMSGTL(("netsnmp_unix", "recv fd %d got %d bytes\n", t->sock, rc));
00148 }
00149 return rc;
00150 }
00151
00152
00153
00154 static int
00155 netsnmp_unix_send(netsnmp_transport *t, void *buf, int size,
00156 void **opaque, int *olength)
00157 {
00158 int rc = -1;
00159
00160 if (t != NULL && t->sock >= 0) {
00161 DEBUGMSGTL(("netsnmp_unix", "send %d bytes to %p on fd %d\n",
00162 size, buf, t->sock));
00163 while (rc < 0) {
00164 rc = sendto(t->sock, buf, size, 0, NULL, 0);
00165 if (rc < 0 && errno != EINTR) {
00166 break;
00167 }
00168 }
00169 }
00170 return rc;
00171 }
00172
00173
00174
00175 static int
00176 netsnmp_unix_close(netsnmp_transport *t)
00177 {
00178 int rc = 0;
00179 sockaddr_un_pair *sup = (sockaddr_un_pair *) t->data;
00180
00181 if (t->sock >= 0) {
00182 #ifndef HAVE_CLOSESOCKET
00183 rc = close(t->sock);
00184 #else
00185 rc = closesocket(t->sock);
00186 #endif
00187 t->sock = -1;
00188 if (sup != NULL) {
00189 if (sup->local) {
00190 if (sup->server.sun_path[0] != 0) {
00191 DEBUGMSGTL(("netsnmp_unix", "close: server unlink(\"%s\")\n",
00192 sup->server.sun_path));
00193 unlink(sup->server.sun_path);
00194 }
00195 } else {
00196 if (sup->client.sun_path[0] != 0) {
00197 DEBUGMSGTL(("netsnmp_unix", "close: client unlink(\"%s\")\n",
00198 sup->client.sun_path));
00199 unlink(sup->client.sun_path);
00200 }
00201 }
00202 }
00203 return rc;
00204 } else {
00205 return -1;
00206 }
00207 }
00208
00209
00210
00211 static int
00212 netsnmp_unix_accept(netsnmp_transport *t)
00213 {
00214 struct sockaddr *farend = NULL;
00215 int newsock = -1;
00216 socklen_t farendlen = sizeof(struct sockaddr_un);
00217
00218 farend = (struct sockaddr *) malloc(farendlen);
00219
00220 if (farend == NULL) {
00221
00222
00223
00224 DEBUGMSGTL(("netsnmp_unix", "accept: malloc failed\n"));
00225 return -1;
00226 }
00227 memset(farend, 0, farendlen);
00228
00229 if (t != NULL && t->sock >= 0) {
00230 newsock = accept(t->sock, farend, &farendlen);
00231
00232 if (newsock < 0) {
00233 DEBUGMSGTL(("netsnmp_unix","accept failed rc %d errno %d \"%s\"\n",
00234 newsock, errno, strerror(errno)));
00235 free(farend);
00236 return newsock;
00237 }
00238
00239 if (t->data != NULL) {
00240 free(t->data);
00241 }
00242
00243 DEBUGMSGTL(("netsnmp_unix", "accept succeeded (farend %p len %d)\n",
00244 farend, farendlen));
00245 t->data = farend;
00246 t->data_length = sizeof(struct sockaddr_un);
00247 netsnmp_sock_buffer_set(newsock, SO_SNDBUF, 1, 0);
00248 netsnmp_sock_buffer_set(newsock, SO_RCVBUF, 1, 0);
00249 return newsock;
00250 } else {
00251 free(farend);
00252 return -1;
00253 }
00254 }
00255
00256 static int create_path = 0;
00257 static mode_t create_mode;
00258
00262 void netsnmp_unix_create_path_with_mode(int mode)
00263 {
00264 create_path = 1;
00265 create_mode = mode;
00266 }
00267
00271 void netsnmp_unix_dont_create_path(void)
00272 {
00273 create_path = 0;
00274 }
00275
00276
00277
00278
00279
00280
00281
00282
00283 netsnmp_transport *
00284 netsnmp_unix_transport(struct sockaddr_un *addr, int local)
00285 {
00286 netsnmp_transport *t = NULL;
00287 sockaddr_un_pair *sup = NULL;
00288 int rc = 0;
00289 char *string = NULL;
00290
00291 if (addr == NULL || addr->sun_family != AF_UNIX) {
00292 return NULL;
00293 }
00294
00295 t = (netsnmp_transport *) malloc(sizeof(netsnmp_transport));
00296 if (t == NULL) {
00297 return NULL;
00298 }
00299
00300 string = netsnmp_unix_fmtaddr(NULL, (void *)addr,
00301 sizeof(struct sockaddr_un));
00302 DEBUGMSGTL(("netsnmp_unix", "open %s %s\n", local ? "local" : "remote",
00303 string));
00304 free(string);
00305
00306 memset(t, 0, sizeof(netsnmp_transport));
00307
00308 t->domain = netsnmp_UnixDomain;
00309 t->domain_length =
00310 sizeof(netsnmp_UnixDomain) / sizeof(netsnmp_UnixDomain[0]);
00311
00312 t->data = malloc(sizeof(sockaddr_un_pair));
00313 if (t->data == NULL) {
00314 netsnmp_transport_free(t);
00315 return NULL;
00316 }
00317 memset(t->data, 0, sizeof(sockaddr_un_pair));
00318 t->data_length = sizeof(sockaddr_un_pair);
00319 sup = (sockaddr_un_pair *) t->data;
00320
00321 t->sock = socket(PF_UNIX, SOCK_STREAM, 0);
00322 if (t->sock < 0) {
00323 netsnmp_transport_free(t);
00324 return NULL;
00325 }
00326
00327 t->flags = NETSNMP_TRANSPORT_FLAG_STREAM;
00328
00329 if (local) {
00330 t->local = (u_char *)malloc(strlen(addr->sun_path));
00331 if (t->local == NULL) {
00332 netsnmp_transport_free(t);
00333 return NULL;
00334 }
00335 memcpy(t->local, addr->sun_path, strlen(addr->sun_path));
00336 t->local_length = strlen(addr->sun_path);
00337
00338
00339
00340
00341
00342
00343 t->flags |= NETSNMP_TRANSPORT_FLAG_LISTEN;
00344
00345 unlink(addr->sun_path);
00346 rc = bind(t->sock, (struct sockaddr *) addr, SUN_LEN(addr));
00347
00348 if (rc != 0 && errno == ENOENT && create_path) {
00349 rc = mkdirhier(addr->sun_path, create_mode, 1);
00350 if (rc != 0) {
00351 netsnmp_unix_close(t);
00352 netsnmp_transport_free(t);
00353 return NULL;
00354 }
00355 rc = bind(t->sock, (struct sockaddr *) addr, SUN_LEN(addr));
00356 }
00357 if (rc != 0) {
00358 DEBUGMSGTL(("netsnmp_unix_transport",
00359 "couldn't bind \"%s\", errno %d (%s)\n",
00360 addr->sun_path, errno, strerror(errno)));
00361 netsnmp_unix_close(t);
00362 netsnmp_transport_free(t);
00363 return NULL;
00364 }
00365
00366
00367
00368
00369
00370
00371 sup->server.sun_family = AF_UNIX;
00372 strcpy(sup->server.sun_path, addr->sun_path);
00373 sup->local = 1;
00374
00375
00376
00377
00378
00379 rc = listen(t->sock, NETSNMP_STREAM_QUEUE_LEN);
00380 if (rc != 0) {
00381 DEBUGMSGTL(("netsnmp_unix_transport",
00382 "couldn't listen to \"%s\", errno %d (%s)\n",
00383 addr->sun_path, errno, strerror(errno)));
00384 netsnmp_unix_close(t);
00385 netsnmp_transport_free(t);
00386 return NULL;
00387 }
00388
00389 } else {
00390 t->remote = (u_char *)malloc(strlen(addr->sun_path));
00391 if (t->remote == NULL) {
00392 netsnmp_transport_free(t);
00393 return NULL;
00394 }
00395 memcpy(t->remote, addr->sun_path, strlen(addr->sun_path));
00396 t->remote_length = strlen(addr->sun_path);
00397
00398 rc = connect(t->sock, (struct sockaddr *) addr,
00399 sizeof(struct sockaddr_un));
00400 if (rc != 0) {
00401 DEBUGMSGTL(("netsnmp_unix_transport",
00402 "couldn't connect to \"%s\", errno %d (%s)\n",
00403 addr->sun_path, errno, strerror(errno)));
00404 netsnmp_unix_close(t);
00405 netsnmp_transport_free(t);
00406 return NULL;
00407 }
00408
00409
00410
00411
00412
00413
00414 sup->server.sun_family = AF_UNIX;
00415 strcpy(sup->server.sun_path, addr->sun_path);
00416 sup->local = 0;
00417 netsnmp_sock_buffer_set(t->sock, SO_SNDBUF, local, 0);
00418 netsnmp_sock_buffer_set(t->sock, SO_RCVBUF, local, 0);
00419 }
00420
00421
00422
00423
00424
00425
00426 t->msgMaxSize = 0x7fffffff;
00427 t->f_recv = netsnmp_unix_recv;
00428 t->f_send = netsnmp_unix_send;
00429 t->f_close = netsnmp_unix_close;
00430 t->f_accept = netsnmp_unix_accept;
00431 t->f_fmtaddr = netsnmp_unix_fmtaddr;
00432
00433 return t;
00434 }
00435
00436 netsnmp_transport *
00437 netsnmp_unix_create_tstring(const char *string, int local,
00438 const char *default_target)
00439 {
00440 struct sockaddr_un addr;
00441
00442 if (string && *string != '\0') {
00443 } else if (default_target && *default_target != '\0') {
00444 string = default_target;
00445 }
00446
00447 if ((string != NULL && *string != '\0') &&
00448 (strlen(string) < sizeof(addr.sun_path))) {
00449 addr.sun_family = AF_UNIX;
00450 memset(addr.sun_path, 0, sizeof(addr.sun_path));
00451 strncpy(addr.sun_path, string, sizeof(addr.sun_path) - 1);
00452 return netsnmp_unix_transport(&addr, local);
00453 } else {
00454 if (string != NULL && *string != '\0') {
00455 snmp_log(LOG_ERR, "Path too long for Unix domain transport\n");
00456 }
00457 return NULL;
00458 }
00459 }
00460
00461
00462
00463 netsnmp_transport *
00464 netsnmp_unix_create_ostring(const u_char * o, size_t o_len, int local)
00465 {
00466 struct sockaddr_un addr;
00467
00468 if (o_len > 0 && o_len < (sizeof(addr.sun_path) - 1)) {
00469 addr.sun_family = AF_UNIX;
00470 memset(addr.sun_path, 0, sizeof(addr.sun_path));
00471 strncpy(addr.sun_path, (const char *)o, o_len);
00472 return netsnmp_unix_transport(&addr, local);
00473 } else {
00474 if (o_len > 0) {
00475 snmp_log(LOG_ERR, "Path too long for Unix domain transport\n");
00476 }
00477 }
00478 return NULL;
00479 }
00480
00481
00482
00483 void
00484 netsnmp_unix_ctor(void)
00485 {
00486 unixDomain.name = netsnmp_UnixDomain;
00487 unixDomain.name_length = sizeof(netsnmp_UnixDomain) / sizeof(oid);
00488 unixDomain.prefix = (const char**)calloc(2, sizeof(char *));
00489 unixDomain.prefix[0] = "unix";
00490
00491 unixDomain.f_create_from_tstring_new = netsnmp_unix_create_tstring;
00492 unixDomain.f_create_from_ostring = netsnmp_unix_create_ostring;
00493
00494 netsnmp_tdomain_register(&unixDomain);
00495 }
00496
00497 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
00498
00499
00500 #define EXAMPLE_COMMUNITY "COMMUNITY"
00501 typedef struct _com2SecUnixEntry {
00502 char community[COMMUNITY_MAX_LEN];
00503 char sockpath[sizeof(struct sockaddr_un)];
00504 unsigned long pathlen;
00505 char secName[VACMSTRINGLEN];
00506 char contextName[VACMSTRINGLEN];
00507 struct _com2SecUnixEntry *next;
00508 } com2SecUnixEntry;
00509
00510 com2SecUnixEntry *com2SecUnixList = NULL, *com2SecUnixListLast = NULL;
00511
00512
00513 int
00514 netsnmp_unix_getSecName(void *opaque, int olength,
00515 const char *community,
00516 size_t community_len,
00517 char **secName, char **contextName)
00518 {
00519 com2SecUnixEntry *c;
00520 struct sockaddr_un *to = (struct sockaddr_un *) opaque;
00521 char *ztcommunity = NULL;
00522
00523 if (secName != NULL) {
00524 *secName = NULL;
00525 }
00526
00527
00528
00529
00530
00531
00532 if (com2SecUnixList == NULL) {
00533 DEBUGMSGTL(("netsnmp_unix_getSecName", "no com2sec entries\n"));
00534 return 0;
00535 }
00536
00537
00538
00539
00540
00541
00542 if (opaque == NULL || olength != sizeof(struct sockaddr_un) ||
00543 to->sun_family != AF_UNIX) {
00544 DEBUGMSGTL(("netsnmp_unix_getSecName",
00545 "no unix destine address in PDU?\n"));
00546 return 1;
00547 }
00548
00549 DEBUGIF("netsnmp_unix_getSecName") {
00550 ztcommunity = (char *)malloc(community_len + 1);
00551 if (ztcommunity != NULL) {
00552 memcpy(ztcommunity, community, community_len);
00553 ztcommunity[community_len] = '\0';
00554 }
00555
00556 DEBUGMSGTL(("netsnmp_unix_getSecName", "resolve <\"%s\">\n",
00557 ztcommunity ? ztcommunity : "<malloc error>"));
00558 }
00559
00560 for (c = com2SecUnixList; c != NULL; c = c->next) {
00561 DEBUGMSGTL(("netsnmp_unix_getSecName","compare <\"%s\",to socket %s>",
00562 c->community, c->sockpath ));
00563 if ((community_len == strlen(c->community)) &&
00564 (memcmp(community, c->community, community_len) == 0) &&
00565
00566 (strlen(to->sun_path) == c->pathlen || c->pathlen == 0) &&
00567 (memcmp(to->sun_path, c->sockpath, c->pathlen) == 0)
00568 ) {
00569 DEBUGMSG(("netsnmp_unix_getSecName", "... SUCCESS\n"));
00570 if (secName != NULL) {
00571 *secName = c->secName;
00572 *contextName = c->contextName;
00573 }
00574 break;
00575 }
00576 DEBUGMSG(("netsnmp_unix_getSecName", "... nope\n"));
00577 }
00578 if (ztcommunity != NULL) {
00579 free(ztcommunity);
00580 }
00581 return 1;
00582 }
00583
00584 void
00585 netsnmp_unix_parse_security(const char *token, char *param)
00586 {
00587 char secName[VACMSTRINGLEN + 1], community[COMMUNITY_MAX_LEN + 1];
00588 char contextName[VACMSTRINGLEN + 1];
00589 char sockpath[sizeof(struct sockaddr_un) + 1];
00590 com2SecUnixEntry *e = NULL;
00591
00592
00593 param = copy_nword(param, secName, VACMSTRINGLEN);
00594 if (strcmp(secName, "-Cn") == 0) {
00595 if (!secName) {
00596 config_perror("missing CONTEXT_NAME parameter");
00597 return;
00598 }
00599 param = copy_nword( param, contextName, sizeof(contextName));
00600 param = copy_nword( param, secName, sizeof(secName));
00601 } else {
00602 contextName[0] = '\0';
00603 }
00604 if (secName[0] == '\0') {
00605 config_perror("missing NAME parameter");
00606 return;
00607 } else if (strlen(secName) > (VACMSTRINGLEN - 1)) {
00608 config_perror("security name too long");
00609 return;
00610 }
00611
00612 param = copy_nword(param, sockpath, sizeof(struct sockaddr_un) - 1);
00613 if (sockpath[0] == '\0') {
00614 config_perror("missing SOCKPATH parameter");
00615 return;
00616 } else if (strlen(sockpath) > (sizeof(struct sockaddr_un) - 1)) {
00617 config_perror("sockpath too long");
00618 return;
00619 }
00620
00621 if(strcmp(sockpath, "default") == 0){
00622 sockpath[0] = 0;
00623 }
00624
00625 param = copy_nword(param, community, COMMUNITY_MAX_LEN);
00626 if (community[0] == '\0') {
00627 config_perror("missing COMMUNITY parameter\n");
00628 return;
00629 } else if (strncmp
00630 (community, EXAMPLE_COMMUNITY, strlen(EXAMPLE_COMMUNITY))
00631 == 0) {
00632 config_perror("example config COMMUNITY not properly configured");
00633 return;
00634 } else if (strlen(community) > (COMMUNITY_MAX_LEN - 1)) {
00635 config_perror("community name too long");
00636 return;
00637 }
00638
00639 e = (com2SecUnixEntry *) malloc(sizeof(com2SecUnixEntry));
00640 if (e == NULL) {
00641 config_perror("memory error");
00642 return;
00643 }
00644
00645 DEBUGMSGTL(("netsnmp_unix_parse_security",
00646 "<\"%s\"> => \"%s\"\n", community, secName));
00647
00648 strcpy(e->secName, secName);
00649 strcpy(e->contextName, contextName);
00650 strcpy(e->community, community);
00651 strcpy(e->sockpath, sockpath);
00652 e->pathlen = strlen(sockpath);
00653 e->next = NULL;
00654
00655 if (com2SecUnixListLast != NULL) {
00656 com2SecUnixListLast->next = e;
00657 com2SecUnixListLast = e;
00658 } else {
00659 com2SecUnixListLast = com2SecUnixList = e;
00660 }
00661 }
00662
00663 void
00664 netsnmp_unix_com2SecList_free(void)
00665 {
00666 com2SecUnixEntry *e = com2SecUnixList;
00667 while (e != NULL) {
00668 com2SecUnixEntry *tmp = e;
00669 e = e->next;
00670 free(tmp);
00671 }
00672 com2SecUnixList = com2SecUnixListLast = NULL;
00673 }
00674 #endif
00675
00676 void
00677 netsnmp_unix_agent_config_tokens_register(void)
00678 {
00679 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
00680 register_app_config_handler("com2secunix", netsnmp_unix_parse_security,
00681 netsnmp_unix_com2SecList_free,
00682 "[-Cn CONTEXT] secName sockpath community");
00683 #endif
00684 }