net-snmp  5.4.1
snmpUnixDomain.c
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  * Evaluate to actual length of the `sockaddr_un' structure.
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  * This is the structure we use to hold transport-specific data.
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  * Return a string representing the address in data, or else the "far end"
00068  * address if data is NULL.
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          * "Local IPC" is the Posix.1g term for Unix domain protocols,
00085          * according to W. R. Stevens, ``Unix Network Programming Volume I
00086          * Second Edition'', p. 374.
00087          */
00088         return strdup("Local IPC: unknown");
00089     } else if (to->sun_path[0] == 0) {
00090         /*
00091          * This is an abstract name.  We could render it as hex or something
00092          * but let's not worry about that for now.
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  * You can write something into opaque that will subsequently get passed back
00108  * to your send function if you like.  For instance, you might want to
00109  * remember where a PDU came from, so that you can send a reply there...
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          * Indicate that the acceptance of this socket failed.
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  * Open a Unix-domain transport for SNMP.  Local is TRUE if addr is the local
00259  * address to bind to (i.e. this is a server-type session); otherwise addr is
00260  * the remote address to send things to (and we make up a temporary name for
00261  * the local end of the connection).
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 = (u_char *)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          * This session is inteneded as a server, so we must bind to the given
00321          * path (unlinking it first, to avoid errors).
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          * Save the address in the transport-specific data pointer for later
00339          * use by netsnmp_unix_close.
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          * Now sit here and listen for connections to arrive.
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 = (u_char *)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          * Save the remote address in the transport-specific data pointer for
00382          * later use by netsnmp_unix_send.
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      * Message size is not limited by this transport (hence msgMaxSize
00394      * is equal to the maximum legal size of an SNMP message).
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                             const char *default_target)
00410 {
00411     struct sockaddr_un addr;
00412 
00413     if (string && *string != '\0') {
00414     } else if (default_target && *default_target != '\0') {
00415       string = default_target;
00416     }
00417 
00418     if ((string != NULL && *string != '\0') &&
00419         (strlen(string) < sizeof(addr.sun_path))) {
00420         addr.sun_family = AF_UNIX;
00421         memset(addr.sun_path, 0, sizeof(addr.sun_path));
00422         strncpy(addr.sun_path, string, sizeof(addr.sun_path) - 1);
00423         return netsnmp_unix_transport(&addr, local);
00424     } else {
00425         if (string != NULL && *string != '\0') {
00426             snmp_log(LOG_ERR, "Path too long for Unix domain transport\n");
00427         }
00428         return NULL;
00429     }
00430 }
00431 
00432 
00433 
00434 netsnmp_transport *
00435 netsnmp_unix_create_ostring(const u_char * o, size_t o_len, int local)
00436 {
00437     struct sockaddr_un addr;
00438 
00439     if (o_len > 0 && o_len < (sizeof(addr.sun_path) - 1)) {
00440         addr.sun_family = AF_UNIX;
00441         memset(addr.sun_path, 0, sizeof(addr.sun_path));
00442         strncpy(addr.sun_path, (const char *)o, o_len);
00443         return netsnmp_unix_transport(&addr, local);
00444     } else {
00445         if (o_len > 0) {
00446             snmp_log(LOG_ERR, "Path too long for Unix domain transport\n");
00447         }
00448     }
00449     return NULL;
00450 }
00451 
00452 
00453 
00454 void
00455 netsnmp_unix_ctor(void)
00456 {
00457     unixDomain.name = netsnmp_UnixDomain;
00458     unixDomain.name_length = sizeof(netsnmp_UnixDomain) / sizeof(oid);
00459     unixDomain.prefix = (const char**)calloc(2, sizeof(char *));
00460     unixDomain.prefix[0] = "unix";
00461 
00462     unixDomain.f_create_from_tstring_new = netsnmp_unix_create_tstring;
00463     unixDomain.f_create_from_ostring = netsnmp_unix_create_ostring;
00464 
00465     netsnmp_tdomain_register(&unixDomain);
00466 }
00467 
00468 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
00469 /* support for SNMPv1 and SNMPv2c on unix domain*/
00470 
00471 #define EXAMPLE_COMMUNITY "COMMUNITY"
00472 typedef struct _com2SecUnixEntry {
00473     char            community[COMMUNITY_MAX_LEN];
00474     char            sockpath[sizeof(struct sockaddr_un)];
00475     unsigned long   pathlen;
00476     char            secName[VACMSTRINGLEN];
00477     char            contextName[VACMSTRINGLEN];
00478     struct _com2SecUnixEntry *next;
00479 } com2SecUnixEntry;
00480 
00481 com2SecUnixEntry   *com2SecUnixList = NULL, *com2SecUnixListLast = NULL;
00482 
00483 
00484 int
00485 netsnmp_unix_getSecName(void *opaque, int olength,
00486                         const char *community,
00487                         size_t community_len,
00488                         char **secName, char **contextName)
00489 {
00490     com2SecUnixEntry   *c;
00491     struct sockaddr_un *to = (struct sockaddr_un *) opaque;
00492     char           *ztcommunity = NULL;
00493 
00494     if (secName != NULL) {
00495         *secName = NULL;  /* Haven't found anything yet */
00496     }
00497 
00498     /*
00499      * Special case if there are NO entries (as opposed to no MATCHING
00500      * entries).
00501      */
00502 
00503     if (com2SecUnixList == NULL) {
00504         DEBUGMSGTL(("netsnmp_unix_getSecName", "no com2sec entries\n"));
00505         return 0;
00506     }
00507 
00508     /*
00509      * If there is no unix socket path, then there can be no valid security
00510      * name.
00511      */
00512 
00513     if (opaque == NULL || olength != sizeof(struct sockaddr_un) ||
00514         to->sun_family != AF_UNIX) {
00515         DEBUGMSGTL(("netsnmp_unix_getSecName",
00516                     "no unix destine address in PDU?\n"));
00517         return 1;
00518     }
00519 
00520     DEBUGIF("netsnmp_unix_getSecName") {
00521         ztcommunity = (char *)malloc(community_len + 1);
00522         if (ztcommunity != NULL) {
00523             memcpy(ztcommunity, community, community_len);
00524             ztcommunity[community_len] = '\0';
00525         }
00526 
00527         DEBUGMSGTL(("netsnmp_unix_getSecName", "resolve <\"%s\">\n",
00528                     ztcommunity ? ztcommunity : "<malloc error>"));
00529     }
00530 
00531     for (c = com2SecUnixList; c != NULL; c = c->next) {
00532         DEBUGMSGTL(("netsnmp_unix_getSecName","compare <\"%s\",to socket %s>",
00533                     c->community, c->sockpath ));
00534         if ((community_len == strlen(c->community)) &&
00535             (memcmp(community, c->community, community_len) == 0) &&
00536             /* compare sockpath, if pathlen == 0, always match */
00537             (strlen(to->sun_path) == c->pathlen || c->pathlen == 0) &&
00538             (memcmp(to->sun_path, c->sockpath, c->pathlen) == 0)
00539             ) {
00540             DEBUGMSG(("netsnmp_unix_getSecName", "... SUCCESS\n"));
00541             if (secName != NULL) {
00542                 *secName = c->secName;
00543                 *contextName = c->contextName;
00544             }
00545             break;
00546         }
00547         DEBUGMSG(("netsnmp_unix_getSecName", "... nope\n"));
00548     }
00549     if (ztcommunity != NULL) {
00550         free(ztcommunity);
00551     }
00552     return 1;
00553 }
00554 
00555 void
00556 netsnmp_unix_parse_security(const char *token, char *param)
00557 {
00558     char              secName[VACMSTRINGLEN + 1], community[COMMUNITY_MAX_LEN + 1];
00559     char              contextName[VACMSTRINGLEN + 1];
00560     char              sockpath[sizeof(struct sockaddr_un) + 1];
00561     com2SecUnixEntry *e = NULL;
00562 
00563 
00564     param = copy_nword(param, secName, VACMSTRINGLEN);
00565     if (strcmp(secName, "-Cn") == 0) {
00566         if (!secName) {
00567             config_perror("missing CONTEXT_NAME parameter");
00568             return;
00569         }
00570         param = copy_nword( param, contextName, sizeof(contextName));
00571         param = copy_nword( param, secName, sizeof(secName));
00572     } else {
00573         contextName[0] = '\0';
00574     }
00575     if (secName[0] == '\0') {
00576         config_perror("missing NAME parameter");
00577         return;
00578     } else if (strlen(secName) > (VACMSTRINGLEN - 1)) {
00579         config_perror("security name too long");
00580         return;
00581     }
00582 
00583         param = copy_nword(param, sockpath, sizeof(struct sockaddr_un) - 1);
00584     if (sockpath[0] == '\0') {
00585         config_perror("missing SOCKPATH parameter");
00586         return;
00587     } else if (strlen(sockpath) > (sizeof(struct sockaddr_un) - 1)) {
00588         config_perror("sockpath too long");
00589         return;
00590     }
00591     /* if sockpath == "default", set pathlen=0*/
00592     if(strcmp(sockpath, "default") == 0){
00593         sockpath[0] = 0;
00594     }
00595 
00596     param = copy_nword(param, community, COMMUNITY_MAX_LEN);
00597     if (community[0] == '\0') {
00598         config_perror("missing COMMUNITY parameter\n");
00599         return;
00600     } else if (strncmp
00601                (community, EXAMPLE_COMMUNITY, strlen(EXAMPLE_COMMUNITY))
00602                == 0) {
00603         config_perror("example config COMMUNITY not properly configured");
00604         return;
00605     } else if (strlen(community) > (COMMUNITY_MAX_LEN - 1)) {
00606         config_perror("community name too long");
00607         return;
00608     }
00609 
00610     e = (com2SecUnixEntry *) malloc(sizeof(com2SecUnixEntry));
00611     if (e == NULL) {
00612         config_perror("memory error");
00613         return;
00614     }
00615 
00616     DEBUGMSGTL(("netsnmp_unix_parse_security",
00617                 "<\"%s\"> => \"%s\"\n", community, secName));
00618 
00619     strcpy(e->secName, secName);
00620     strcpy(e->contextName, contextName);
00621     strcpy(e->community, community);
00622     strcpy(e->sockpath, sockpath);
00623     e->pathlen = strlen(sockpath);
00624     e->next = NULL;
00625 
00626     if (com2SecUnixListLast != NULL) {
00627         com2SecUnixListLast->next = e;
00628         com2SecUnixListLast = e;
00629     } else {
00630         com2SecUnixListLast = com2SecUnixList = e;
00631     }
00632 }
00633 
00634 void
00635 netsnmp_unix_com2SecList_free(void)
00636 {
00637     com2SecUnixEntry   *e = com2SecUnixList;
00638     while (e != NULL) {
00639         com2SecUnixEntry   *tmp = e;
00640         e = e->next;
00641         free(tmp);
00642     }
00643     com2SecUnixList = com2SecUnixListLast = NULL;
00644 }
00645 #endif /* support for community based SNMP */
00646 
00647 void
00648 netsnmp_unix_agent_config_tokens_register(void)
00649 {
00650 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
00651     register_app_config_handler("com2secunix", netsnmp_unix_parse_security,
00652                                 netsnmp_unix_com2SecList_free,
00653                                 "[-Cn CONTEXT] secName sockpath community");
00654 #endif /* support for community based SNMP */
00655 }