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