ccn_sockcreate.c

Go to the documentation of this file.
00001 /**
00002  * @file ccn_sockcreate.c
00003  * @brief Setting up a socket from a text-based description.
00004  * 
00005  * Part of the CCNx C Library.
00006  *
00007  * Copyright (C) 2009-2010 Palo Alto Research Center, Inc.
00008  *
00009  * This library is free software; you can redistribute it and/or modify it
00010  * under the terms of the GNU Lesser General Public License version 2.1
00011  * as published by the Free Software Foundation.
00012  * This library is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00015  * Lesser General Public License for more details. You should have received
00016  * a copy of the GNU Lesser General Public License along with this library;
00017  * if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
00018  * Fifth Floor, Boston, MA 02110-1301 USA.
00019  */
00020 #include <stdlib.h>
00021 #include <stdarg.h>
00022 #include <errno.h>
00023 #include <string.h>
00024 #include <signal.h>
00025 #include <sys/types.h>
00026 #include <sys/time.h>
00027 #include <sys/socket.h>
00028 #include <netinet/in.h>
00029 #include <arpa/inet.h>
00030 #include <net/if.h>
00031 #include <netdb.h>
00032 #include <unistd.h>
00033 
00034 #include <ccn/sockcreate.h>
00035 
00036 #if defined(NEED_GETADDRINFO_COMPAT)
00037     #include "getaddrinfo.h"
00038     #include "dummyin6.h"
00039 #endif
00040 
00041 #define LOGGIT if (logger) (*logger)
00042 #define GOT_HERE LOGGIT(logdat, "at ccn_sockcreate.c:%d", __LINE__);
00043 
00044 
00045 static int
00046 set_multicast_socket_options(int socket_r, int socket_w,
00047                              struct addrinfo *ai,
00048                              struct addrinfo *localif_for_mcast_addrinfo,
00049                              int multicastttl,
00050                              int ifindex,
00051                              void (*logger)(void *, const char *, ...),
00052                              void *logdat)
00053 {
00054     struct addrinfo hints;
00055     struct ip_mreq mreq;
00056 #ifdef IPV6_JOIN_GROUP
00057     struct ipv6_mreq mreq6;
00058 #endif
00059     unsigned char csockopt;
00060     unsigned int isockopt;
00061     int res;
00062 
00063     memset((void *)&hints, 0, sizeof(hints));
00064     memset((void *)&mreq, 0, sizeof(mreq));
00065 #ifdef IPV6_JOIN_GROUP
00066     memset((void *)&mreq6, 0, sizeof(mreq6));
00067 #endif
00068 
00069     if (ai->ai_family == PF_INET && IN_MULTICAST(ntohl(((struct sockaddr_in *)(ai->ai_addr))->sin_addr.s_addr))) {
00070         LOGGIT(logdat, "IPv4 multicast");
00071 #ifdef IP_ADD_MEMBERSHIP
00072         memcpy((void *)&mreq.imr_multiaddr, &((struct sockaddr_in *)ai->ai_addr)->sin_addr, sizeof(mreq.imr_multiaddr));
00073         if (localif_for_mcast_addrinfo != NULL) {
00074             memcpy((void *)&mreq.imr_interface.s_addr, &((struct sockaddr_in *)localif_for_mcast_addrinfo->ai_addr)->sin_addr, sizeof(mreq.imr_interface.s_addr));
00075         }
00076         res = setsockopt(socket_r, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
00077         if (res == -1) {
00078             LOGGIT(logdat, "setsockopt(..., IP_ADD_MEMBERSHIP, ...): %s", strerror(errno));
00079             return(-1);
00080         }
00081 #endif
00082 #ifdef IP_MULTICAST_LOOP
00083         csockopt = 0;
00084         res = setsockopt(socket_w, IPPROTO_IP, IP_MULTICAST_LOOP, &csockopt, sizeof(csockopt));
00085         if (res == -1) {
00086             LOGGIT(logdat, "setsockopt(..., IP_MULTICAST_LOOP, ...): %s", strerror(errno));
00087             return(-1);
00088         }
00089 #endif
00090 #ifdef IP_MULTICAST_TTL
00091         if (multicastttl > 0) {
00092             csockopt = multicastttl;
00093             res = setsockopt(socket_w, IPPROTO_IP, IP_MULTICAST_TTL, &csockopt, sizeof(csockopt));
00094             if (res == -1) {
00095                 LOGGIT(logdat, "setsockopt(..., IP_MULTICAST_TTL, ...): %s", strerror(errno));
00096                 return(-1);
00097             }
00098         }
00099 #endif
00100 #ifdef IP_MULTICAST_IF
00101         if (localif_for_mcast_addrinfo != NULL) {
00102             struct in_addr ifaddr = { 0 };
00103             ifaddr = ((struct sockaddr_in *)localif_for_mcast_addrinfo->ai_addr)->sin_addr;
00104             res = setsockopt(socket_w, IPPROTO_IP, IP_MULTICAST_IF, &ifaddr, sizeof(ifaddr));
00105             if (res == -1) {
00106                 LOGGIT(logdat, "setsockopt(..., IP_MULTICAST_IF, ...): %s", strerror(errno));
00107                 return(-1);
00108             }
00109         }
00110 #endif
00111     } else if (ai->ai_family == PF_INET6 && IN6_IS_ADDR_MULTICAST((&((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr))) {
00112         LOGGIT(logdat, "IPv6 multicast");
00113 #ifdef IPV6_JOIN_GROUP
00114         memcpy((void *)&mreq6.ipv6mr_multiaddr, &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr, sizeof(mreq6.ipv6mr_multiaddr));
00115         if (ifindex > 0) {
00116             mreq6.ipv6mr_interface = ifindex;
00117         }
00118         res = setsockopt(socket_r, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6, sizeof(mreq6));
00119         if (res == -1) {
00120             LOGGIT(logdat, "setsockopt(..., IPV6_JOIN_GROUP, ...): %s", strerror(errno));
00121             return(-1);
00122         }
00123 #endif
00124 #ifdef IPV6_MULTICAST_LOOP
00125         isockopt = 0;
00126         res = setsockopt(socket_w, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &isockopt, sizeof(isockopt));
00127         if (res == -1) {
00128             LOGGIT(logdat, "setsockopt(..., IPV6_MULTICAST_LOOP, ...): %s", strerror(errno));
00129             return(-1);
00130         }
00131 #endif
00132 #ifdef IPV6_MULTICAST_HOPS
00133         if (multicastttl > 0) {
00134             isockopt = multicastttl;
00135             res = setsockopt(socket_w, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &isockopt, sizeof(isockopt));
00136             if (res == -1) {
00137                 LOGGIT(logdat, "setsockopt(..., IPV6_MULTICAST_LOOP, ...): %s", strerror(errno));
00138                 return(-1);
00139             }
00140         }
00141 #endif
00142 #ifdef IP6_MULTICAST_IF
00143         if (ifindex > 0) {
00144             isockopt = ifindex;
00145             res = setsockopt(socket_w, IPPROTO_IPV6, IP6_MULTICAST_IF, &isockopt, sizeof(isockopt));
00146             if (res == -1) {
00147                 LOGGIT(logdat, "setsockopt(..., IP6_MULTICAST_IF, ...): %s", strerror(errno));
00148                 return(-1);
00149             }
00150         }
00151 #endif
00152     }
00153     return(0);
00154 }
00155 
00156 /**
00157  * Utility for setting up a socket (or pair of sockets) from a text-based
00158  * description.
00159  *
00160  * Currently this is only used for UDP multicast.
00161  * 
00162  * @param descr hold the information needed to create the socket(s).
00163  * @param logger should be used for reporting errors, printf-style.
00164  * @param logdat must be passed as first argument to logger().
00165  * @param getbound is callback for getting already-bound sender socket,
00166  *          should return -1 if none.
00167  * @param getbounddat is passed as first argument to getbound
00168  * @param socks will be filled in with the pair of socket file descriptors.
00169  * @returns 0 for success, -1 for error.
00170  */
00171 int
00172 ccn_setup_socket(const struct ccn_sockdescr *descr,
00173                  void (*logger)(void *, const char *, ...),
00174                  void *logdat,
00175                  int (*getbound)(void *, struct sockaddr *, socklen_t),
00176                  void *getbounddat,
00177                  struct ccn_sockets *socks)
00178 {
00179     int result = -1;
00180     char *cp = NULL;
00181     struct addrinfo hints = {0};
00182     int res;
00183     struct addrinfo *mcast_source_addrinfo = NULL;
00184     struct addrinfo *addrinfo = NULL;
00185     struct addrinfo *laddrinfo = NULL;
00186     unsigned int if_index = 0;
00187     const int one = 1;
00188     int sock = -1;
00189     int close_protect = -1;
00190     
00191     GOT_HERE;
00192     socks->sending = socks->recving = -1;
00193     if (descr->ipproto > 0)
00194         hints.ai_protocol = descr->ipproto;
00195     if (descr->ipproto == IPPROTO_UDP)
00196         hints.ai_socktype = SOCK_DGRAM;
00197     else if (descr->ipproto == IPPROTO_TCP)
00198         hints.ai_socktype = SOCK_STREAM;
00199     hints.ai_flags =  AI_NUMERICHOST;
00200     if (descr->port == NULL ||
00201         strspn(descr->port, "0123456789") != strlen(descr->port)) {
00202         LOGGIT(logdat, "must specify numeric port");
00203         goto Finish;
00204     }
00205     GOT_HERE;
00206     if (descr->source_address != NULL) {
00207         res = getaddrinfo(descr->source_address, descr->port,
00208                           &hints, &mcast_source_addrinfo);
00209         if (res != 0 || mcast_source_addrinfo == NULL) {
00210             LOGGIT(logdat, "getaddrinfo(\"%s\", ...): %s",
00211                    descr->source_address, gai_strerror(res));
00212             goto Finish;
00213         }
00214         hints.ai_family = mcast_source_addrinfo->ai_family;
00215     }
00216     GOT_HERE;
00217     if (descr->mcast_ttl >= 0) {
00218         if (descr->mcast_ttl < 1 || descr->mcast_ttl > 255) {
00219             // XXX - It could make sense to use ttl 0 if we're talking on a loopback interface and we leave IP_MULTICAST_LOOP on.
00220             LOGGIT(logdat, "mcast_ttl(%d) out of range", descr->mcast_ttl);
00221             goto Finish;
00222         }
00223     }
00224     GOT_HERE;
00225     if (descr->address == NULL) {
00226         LOGGIT(logdat, "must specify remote address");
00227         goto Finish;
00228     }
00229 #ifdef IPV6_JOIN_GROUP
00230     cp = strchr(descr->address, '%');
00231     GOT_HERE;
00232     if (cp != NULL) {
00233         cp++;
00234         errno = 0;
00235         if_index = atoi(cp);
00236         if (if_index == 0) {
00237             if_index = if_nametoindex(cp);
00238             if (if_index == 0 && errno != 0) {
00239                 LOGGIT(logdat, "Invalid interface name %s", cp);
00240                 goto Finish;
00241             }
00242         }
00243     }
00244 #endif
00245     GOT_HERE;
00246     res = getaddrinfo(descr->address, descr->port,
00247                       &hints, &addrinfo);
00248     if (res != 0 || addrinfo == NULL) {
00249         LOGGIT(logdat, "getaddrinfo(\"%s\", ...): %s",
00250                descr->address, gai_strerror(res));
00251         goto Finish;
00252     }
00253     sock = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol);
00254     GOT_HERE;
00255     if (sock == -1) {
00256         LOGGIT(logdat, "socket: %s", strerror(errno));
00257         goto Finish;
00258     }
00259     GOT_HERE;
00260     socks->recving = socks->sending = sock;
00261     if (mcast_source_addrinfo == NULL) {
00262         /* Try binding the port now to see if we need 2 sockets. */
00263         hints.ai_family = addrinfo->ai_family;
00264         hints.ai_socktype = addrinfo->ai_socktype;
00265         hints.ai_flags = AI_PASSIVE;
00266         GOT_HERE;
00267         res = getaddrinfo(NULL, descr->port, &hints, &laddrinfo);
00268         if (res != 0)
00269             goto Finish;
00270         GOT_HERE;
00271         res = bind(socks->sending, laddrinfo->ai_addr, laddrinfo->ai_addrlen);
00272         if (res == -1 && getbound) {
00273             mcast_source_addrinfo = laddrinfo;
00274             laddrinfo = NULL;
00275         }
00276     }
00277     if (mcast_source_addrinfo != NULL) {
00278         /*
00279          * We have a specific interface to bind to for sending.
00280          * mcast_source_addrinfo is the unicast address of this interface.
00281          * Since we need to bind the recving side to the multicast address,
00282          * we need two sockets in this case.
00283          *
00284          * Our caller may choose to provide the sending side.
00285          */
00286         socks->sending = -1;
00287         if (getbound) {
00288             GOT_HERE;
00289             socks->sending = getbound(getbounddat,
00290                                       mcast_source_addrinfo->ai_addr,
00291                                       mcast_source_addrinfo->ai_addrlen);
00292             if (socks->sending >= 0) {
00293                 GOT_HERE;
00294                 close_protect = socks->sending;
00295             }
00296         }
00297         if (socks->sending == -1)
00298             socks->sending = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol);
00299         if (socks->sending == -1) {
00300             LOGGIT(logdat, "socket: %s", strerror(errno));
00301             goto Finish;
00302         }
00303         res = setsockopt(socks->recving, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
00304         if (res == -1) {
00305             LOGGIT(logdat, "setsockopt(recving, ..., SO_REUSEADDR, ...): %s", strerror(errno));
00306             goto Finish;
00307         }
00308         /* bind the recving socket to the multicast address */
00309         res = bind(socks->recving, addrinfo->ai_addr, addrinfo->ai_addrlen);
00310         if (res == -1) {
00311             LOGGIT(logdat, "bind(recving, ...): %s", strerror(errno));
00312             goto Finish;
00313         }
00314     }
00315     GOT_HERE;
00316     res = set_multicast_socket_options(socks->recving, socks->sending,
00317                                        addrinfo,
00318                                        mcast_source_addrinfo,
00319                                        descr->mcast_ttl,
00320                                        if_index,
00321                                        logger,
00322                                        logdat);
00323     if (res < 0)
00324         goto Finish;
00325     if (mcast_source_addrinfo != NULL) {
00326         GOT_HERE;
00327         if (socks->sending != close_protect) {
00328             res = bind(socks->sending,
00329                        mcast_source_addrinfo->ai_addr,
00330                        mcast_source_addrinfo->ai_addrlen);
00331             if (res == -1) {
00332                 LOGGIT(logdat, "bind(sending, ...): %s", strerror(errno));
00333                 goto Finish;
00334             }
00335         }
00336     }
00337     GOT_HERE;
00338     result = 0;
00339     
00340 Finish:
00341     if (addrinfo != NULL)
00342         freeaddrinfo(addrinfo);
00343     if (laddrinfo != NULL)
00344         freeaddrinfo(laddrinfo);
00345     if (mcast_source_addrinfo != NULL)
00346         freeaddrinfo(mcast_source_addrinfo);
00347     if (result != 0) {
00348         close(socks->recving);
00349         if (socks->sending != socks->recving && socks->sending != close_protect)
00350             close(socks->sending);
00351         socks->sending = socks->recving = -1;
00352     }
00353     return(result);
00354 }
Generated on Fri May 13 16:27:03 2011 for Content-Centric Networking in C by  doxygen 1.6.3