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 = 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 = 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 {
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 /* support for SNMPv1 and SNMPv2c on unix domain*/
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;  /* Haven't found anything yet */
00489     }
00490 
00491     /*
00492      * Special case if there are NO entries (as opposed to no MATCHING
00493      * entries).
00494      */
00495 
00496     if (com2SecUnixList == NULL) {
00497         DEBUGMSGTL(("netsnmp_unix_getSecName", "no com2sec entries\n"));
00498         return 0;
00499     }
00500 
00501     /*
00502      * If there is no unix socket path, then there can be no valid security
00503      * name.
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             /* compare sockpath, if pathlen == 0, always match */
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     /* if sockpath == "default", set pathlen=0*/
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 /* support for community based SNMP */
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 /* support for community based SNMP */
00648 }

net-snmpに対してSat Sep 5 13:14:26 2009に生成されました。  doxygen 1.4.7