dnxUdp.c

Go to the documentation of this file.
00001 /*--------------------------------------------------------------------------
00002 
00003    Copyright (c) 2006-2007, Intellectual Reserve, Inc. All rights reserved.
00004 
00005    This program is free software; you can redistribute it and/or modify
00006    it under the terms of the GNU General Public License version 2 as
00007    published by the Free Software Foundation.
00008 
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012    GNU General Public License for more details.
00013 
00014    You should have received a copy of the GNU General Public License
00015    along with this program; if not, write to the Free Software
00016    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017 
00018   --------------------------------------------------------------------------*/
00019 
00028 #include "dnxUdp.h"
00029 #include "dnxTSPI.h"
00030 
00031 #include "dnxTransport.h"
00032 #include "dnxError.h"
00033 #include "dnxDebug.h"
00034 #include "dnxLogging.h"
00035 
00036 #include "dnxProtocol.h"
00037 
00038 #include <stdio.h>
00039 #include <stdlib.h>
00040 #include <stddef.h>
00041 #include <string.h>
00042 #include <errno.h>
00043 #include <assert.h>
00044 #include <sys/types.h>
00045 #include <sys/time.h>
00046 #include <sys/socket.h>
00047 #include <sys/select.h>
00048 #include <netinet/in.h>
00049 #include <netdb.h>
00050 #include <unistd.h>
00051 #include <pthread.h>
00052 #include <limits.h>
00053 
00054 #ifndef HOST_NAME_MAX
00055 # define HOST_NAME_MAX 256
00056 #endif
00057 
00059 typedef struct iDnxUdpChannel_
00060 {
00061    char * host;         
00062    int port;            
00063    int socket;          
00064    DnxTransStats stats; 
00065    iDnxChannel ichan;   
00066 } iDnxUdpChannel;
00067 
00070 static pthread_mutex_t udpMutex;
00071 
00072 /*--------------------------------------------------------------------------
00073                   TRANSPORT SERVICE PROVIDER INTERFACE
00074   --------------------------------------------------------------------------*/
00075 
00098 static int dnxUdpOpen(iDnxChannel * icp, int mode)
00099 {
00100    iDnxUdpChannel * iucp = (iDnxUdpChannel *)
00101          ((char *)icp - offsetof(iDnxUdpChannel, ichan));
00102    struct hostent * he;
00103    struct sockaddr_in inaddr;
00104    int sd;
00105 
00106    assert(icp && iucp->port > 0);
00107 
00108    inaddr.sin_family = AF_INET;
00109    inaddr.sin_port = htons((in_port_t)iucp->port);
00110 
00111    // see if we are listening on any address
00112    if (!strcmp(iucp->host, "INADDR_ANY")
00113          || !strcmp(iucp->host, "0.0.0.0")
00114          || !strcmp(iucp->host, "0"))
00115    {
00116       // make sure that the requested mode is passive
00117       if (mode == DNX_MODE_ACTIVE) return DNX_ERR_ADDRESS;
00118       inaddr.sin_addr.s_addr = INADDR_ANY;
00119    }
00120    else
00121    {
00122       DNX_PT_MUTEX_LOCK(&udpMutex);
00123       if ((he = gethostbyname(iucp->host)) != 0)
00124          memcpy(&inaddr.sin_addr.s_addr, he->h_addr_list[0], he->h_length);
00125       DNX_PT_MUTEX_UNLOCK(&udpMutex);
00126 
00127       if (!he) return DNX_ERR_ADDRESS;
00128    }
00129 
00130    if ((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
00131    {
00132       dnxLog("dnxUdpOpen: socket failed: %s.", strerror(errno));
00133       return DNX_ERR_OPEN;
00134    }
00135 
00136    // determine how to handle socket connectivity based upon open mode.
00137    if (mode == DNX_MODE_ACTIVE)
00138    {
00139       struct sockaddr_in inaddr_any;
00140 
00141       // want to sometimes listen for incoming packets on this 
00142       // active channel also, so bind socket to any local address
00143       // (if not done, would happen anyway on first packet sent)
00144       memset(&inaddr_any, 0, sizeof inaddr_any);
00145       inaddr_any.sin_family = AF_INET;
00146       inaddr_any.sin_addr.s_addr = htonl(INADDR_ANY);
00147       if (bind(sd, (struct sockaddr *)&inaddr_any, sizeof inaddr_any) != 0)
00148       {
00149          close(sd);
00150          dnxLog("dnxUdpOpen: bind(any) failed: %s.", strerror(errno));
00151          return DNX_ERR_OPEN;
00152       }
00153 
00154       // for UDP, this sets the default destination address, so we can
00155       // now use send() and write() in addition to sendto() and sendmsg()
00156       // (also makes it impossible to specify sendto address or retrieve
00157       // sender address in recvfrom, but there's only one in either case)
00158       if (connect(sd, (struct sockaddr *)&inaddr, sizeof inaddr) != 0)
00159       {
00160          dnxLog("dnxUdpOpen: connect(%lx) failed: %s.",
00161                (unsigned long)inaddr.sin_addr.s_addr, strerror(errno));
00162          close(sd);
00163          return DNX_ERR_OPEN;
00164       }
00165    }
00166    else
00167    {
00168       struct linger lopt;
00169       int reuse = 1;
00170 
00171       lopt.l_onoff = 0;
00172       lopt.l_linger = 0;
00173 
00174       // reuse addr and clear linger to avoid tcp time_wait state issues
00175       setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof reuse);
00176       setsockopt(sd, SOL_SOCKET, SO_LINGER, &lopt, sizeof lopt);
00177 
00178       // want to listen for incoming packets, so bind to the
00179       // specified local address and port
00180       if (bind(sd, (struct sockaddr *)&inaddr, sizeof inaddr) != 0)
00181       {
00182          close(sd);
00183          dnxLog("dnxUdpOpen: bind(%lx) failed: %s.",
00184                (unsigned long)inaddr.sin_addr.s_addr, strerror(errno));
00185          return DNX_ERR_OPEN;
00186       }
00187    }
00188 
00189    iucp->socket = sd;
00190 
00191    return DNX_OK;
00192 }
00193 
00194 //----------------------------------------------------------------------------
00195 
00202 static int dnxUdpClose(iDnxChannel * icp)
00203 {
00204    iDnxUdpChannel * iucp = (iDnxUdpChannel *)
00205          ((char *)icp - offsetof(iDnxUdpChannel, ichan));
00206 
00207    assert(icp && iucp->socket);
00208 
00209    close(iucp->socket);
00210    iucp->socket = 0;
00211 
00212    return DNX_OK;
00213 }
00214 
00215 //----------------------------------------------------------------------------
00216 
00248 static int dnxUdpRead(iDnxChannel * icp, char * buf, int * size, int timeout, char * src)
00249 {
00250    iDnxUdpChannel * iucp = (iDnxUdpChannel *)((char *)icp - offsetof(iDnxUdpChannel, ichan));
00251    socklen_t slen = sizeof(struct sockaddr_storage);
00252    ssize_t mlen;
00253 
00254    assert(icp && iucp->socket && buf && size && *size > 0);
00255 
00256    // implement timeout logic, if timeout value is greater than zero
00257    if (timeout > 0)
00258    {
00259       struct timeval tv;
00260       fd_set fd_rds;
00261       int nsd;
00262 
00263       FD_ZERO(&fd_rds);
00264       FD_SET(iucp->socket, &fd_rds);
00265 
00266       tv.tv_usec = 0L;
00267       tv.tv_sec = timeout;
00268 
00269       if ((nsd = select(iucp->socket + 1, &fd_rds, 0, 0, &tv)) == 0)
00270          return DNX_ERR_TIMEOUT;
00271 
00272       if (nsd < 0)
00273       {
00274          if (errno != EINTR)
00275          {
00276             dnxLog("UDP receive - select failed: %s.", strerror(errno));
00277             return DNX_ERR_RECEIVE;
00278          }
00279          return DNX_ERR_TIMEOUT;
00280       }
00281    }
00282 
00283    // read the incoming message, use recvfrom only if address was requested
00284    if (src)
00285       mlen = recvfrom(iucp->socket, buf, *size, 0, (struct sockaddr *)src, &slen);
00286    else 
00287       mlen = read(iucp->socket, buf, *size);
00288 
00289    if (mlen < 0)
00290    {
00291       iucp->stats.rderrs++;
00292 
00293       // on "connected" UDP sockets, if the server can't be reached -
00294       // ICMP returned ICMP_UNREACH - we could get notified here.
00295       if (errno == ECONNREFUSED) return DNX_ERR_ADDRESS;
00296 
00297       dnxDebug(2, "UDP receive - %s socket failed: %s.", 
00298             src? "recvfrom unconnected": "read connected", strerror(errno));
00299 
00300       return DNX_ERR_RECEIVE;
00301    }
00302 
00303    iucp->stats.reads++;
00304    *size = (int)mlen;
00305 
00306    return DNX_OK;
00307 }
00308 
00309 //----------------------------------------------------------------------------
00310 
00342 static int dnxUdpWrite(iDnxChannel * icp, char * buf, int size, int timeout, char * dst)
00343 {
00344    iDnxUdpChannel * iucp = (iDnxUdpChannel *) ((char *)icp - offsetof(iDnxUdpChannel, ichan));
00345    ssize_t mlen;
00346 
00347    assert(icp && iucp->socket && buf && size);
00348 
00349    // implement timeout logic, if timeout value is greater than zero
00350    if (timeout > 0)
00351    {
00352       struct timeval tv;
00353       fd_set fd_wrs;
00354       int nsd;
00355 
00356       FD_ZERO(&fd_wrs);
00357       FD_SET(iucp->socket, &fd_wrs);
00358 
00359       tv.tv_usec = 0L;
00360       tv.tv_sec = timeout;
00361 
00362       if ((nsd = select(iucp->socket + 1, 0, &fd_wrs, 0, &tv)) == 0)
00363          return DNX_ERR_TIMEOUT;
00364       if (nsd < 0)
00365       {
00366          if (errno != EINTR)
00367          {
00368             dnxLog("UDP send - select failed: %s.", strerror(errno));
00369             return DNX_ERR_SEND;
00370          }
00371          return DNX_ERR_TIMEOUT;
00372       }
00373    }
00374 
00375    // specify destination address for passive, server-side sockets only
00376    if (dst)
00377    {
00378       struct sockaddr * sa = (struct sockaddr *)dst;
00379       mlen = sendto(iucp->socket, buf, size, 0, sa, sa->sa_family == AF_INET?
00380             sizeof(struct sockaddr_in): sizeof(struct sockaddr_in6));
00381    }
00382    else
00383       mlen = write(iucp->socket, buf, size);
00384 
00385    if (mlen < 0)
00386    {
00387       iucp->stats.wrerrs++;
00388       dnxDebug(2, "UDP send - %s socket failed: %s.", 
00389             dst? "sendto unconnected": "write connected", strerror(errno));
00390       return DNX_ERR_SEND;
00391    }
00392 
00393    iucp->stats.writes++;
00394 
00395    return DNX_OK;
00396 }
00397 
00398 //----------------------------------------------------------------------------
00399 
00404 static void dnxUdpDelete(iDnxChannel * icp)
00405 {
00406    iDnxUdpChannel * iucp = (iDnxUdpChannel *)
00407          ((char *)icp - offsetof(iDnxUdpChannel, ichan));
00408 
00409    assert(icp && iucp->socket == 0);
00410 
00411    xfree(iucp->host);
00412    xfree(iucp);
00413 }
00414 
00415 //----------------------------------------------------------------------------
00416 
00422 static void dnxUdpGetStats(iDnxChannel * icp, DnxTransStats * tsp)
00423 {
00424    iDnxUdpChannel * iucp = (iDnxUdpChannel *)
00425          ((char *)icp - offsetof(iDnxUdpChannel, ichan));
00426 
00427    assert(icp && tsp);
00428 
00429    *tsp = iucp->stats;
00430 }
00431 
00432 //----------------------------------------------------------------------------
00433 
00438 static void dnxUdpResetStats(iDnxChannel * icp)
00439 {
00440    iDnxUdpChannel * iucp = (iDnxUdpChannel *)
00441          ((char *)icp - offsetof(iDnxUdpChannel, ichan));
00442 
00443    assert(icp);
00444 
00445    memset(&iucp->stats, 0, sizeof iucp->stats);
00446 }
00447 
00448 //----------------------------------------------------------------------------
00449 
00458 static int dnxUdpNew(char * url, iDnxChannel ** icpp)
00459 {
00460    char * cp, * ep, * lastchar;
00461    iDnxUdpChannel * iucp = NULL;
00462    long port;
00463 
00464    assert(icpp && url && *url);
00465 
00466    // search for host:port in URL
00467    if ((cp = strstr(url, "://")) == 0)
00468       return DNX_ERR_BADURL;
00469    cp += 3;
00470 
00471    // find host/port separator ':' - copy host name
00472    if ((ep = strchr(cp, ':')) == 0 || ep == cp || ep - cp > HOST_NAME_MAX)
00473       return DNX_ERR_BADURL;
00474 
00475    // extract port number
00476    if ((port = strtol(ep + 1, &lastchar, 0)) < 1 || port > 65535
00477          || (*lastchar && *lastchar != '/'))
00478       return DNX_ERR_BADURL;
00479 
00480     if(iucp)
00481         xfree(iucp);
00482 
00483    // allocate a new iDnxUdpChannel object
00484     if ((iucp = (iDnxUdpChannel *)xcalloc(1,sizeof *iucp)) == 0)
00485     {
00486         return DNX_ERR_MEMORY;
00487     }
00488 
00489 
00490    // save host name and port
00491    if ((iucp->host = (char *)xmalloc(ep - cp + 1)) == 0)
00492    {
00493       xfree(iucp);
00494       return DNX_ERR_MEMORY;
00495    }
00496    memcpy(iucp->host, cp, ep - cp);
00497    iucp->host[ep - cp] = 0;
00498    iucp->port = (int)port;
00499 
00500    // set I/O methods
00501    iucp->ichan.txOpen         = dnxUdpOpen;
00502    iucp->ichan.txClose        = dnxUdpClose;
00503    iucp->ichan.txRead         = dnxUdpRead;
00504    iucp->ichan.txWrite        = dnxUdpWrite;
00505    iucp->ichan.txDelete       = dnxUdpDelete;
00506    iucp->ichan.txGetStats     = dnxUdpGetStats;
00507    iucp->ichan.txResetStats   = dnxUdpResetStats;
00508    *icpp = &iucp->ichan;
00509 
00510    return DNX_OK;
00511 }
00512 
00513 /*--------------------------------------------------------------------------
00514                            EXPORTED INTERFACE
00515   --------------------------------------------------------------------------*/
00516 
00524 int dnxUdpInit(int (**ptxAlloc)(char * url, iDnxChannel ** icpp))
00525 {
00526    DNX_PT_MUTEX_INIT(&udpMutex);
00527 
00528    *ptxAlloc = dnxUdpNew;
00529 
00530    return DNX_OK;
00531 }
00532 
00533 //----------------------------------------------------------------------------
00534 
00537 void dnxUdpDeInit(void)
00538 {
00539    DNX_PT_MUTEX_DESTROY(&udpMutex);
00540 }
00541 
00542 /*--------------------------------------------------------------------------*/
00543 

Generated on Tue Apr 13 15:48:07 2010 for DNX by  doxygen 1.5.6