dnxTcp.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 "dnxTcp.h"     // temporary
00029 #include "dnxTSPI.h"
00030 
00031 #include "dnxTransport.h"
00032 #include "dnxError.h"
00033 #include "dnxDebug.h"
00034 #include "dnxLogging.h"
00035 
00036 #include <stdio.h>
00037 #include <stdlib.h>
00038 #include <stddef.h>
00039 #include <string.h>
00040 #include <errno.h>
00041 #include <assert.h>
00042 #include <sys/types.h>
00043 #include <sys/time.h>
00044 #include <sys/socket.h>
00045 #include <sys/select.h>
00046 #include <netinet/in.h>
00047 #include <netdb.h>
00048 #include <unistd.h>
00049 #include <pthread.h>
00050 #include <limits.h>
00051 
00052 #ifndef HOST_NAME_MAX
00053 # define HOST_NAME_MAX 256
00054 #endif
00055 
00057 #define DNX_TCP_LISTEN  5
00058 
00060 typedef struct iDnxTcpChannel_
00061 {
00062    char * host;         
00063    int port;            
00064    int socket;          
00065    iDnxChannel ichan;   
00066 } iDnxTcpChannel;
00067 
00070 static pthread_mutex_t tcpMutex;
00071 
00072 /*--------------------------------------------------------------------------
00073                   TRANSPORT SERVICE PROVIDER INTERFACE
00074   --------------------------------------------------------------------------*/
00075 
00085 static int dnxTcpOpen(iDnxChannel * icp, int mode)
00086 {
00087    iDnxTcpChannel * itcp = (iDnxTcpChannel *)
00088          ((char *)icp - offsetof(iDnxTcpChannel, ichan));
00089    struct hostent * he;
00090    struct sockaddr_in inaddr;
00091    int sd;
00092 
00093    assert(icp && itcp->port > 0);
00094 
00095    inaddr.sin_family = AF_INET;
00096    inaddr.sin_port = (in_port_t)itcp->port;
00097    inaddr.sin_port = htons(inaddr.sin_port);
00098 
00099    // see if we are listening on any address
00100    if (!strcmp(itcp->host, "INADDR_ANY")
00101          || !strcmp(itcp->host, "0.0.0.0")
00102          || !strcmp(itcp->host, "0"))
00103    {
00104       // make sure that the request is passive
00105       if (mode == DNX_MODE_ACTIVE) return DNX_ERR_ADDRESS;
00106       inaddr.sin_addr.s_addr = INADDR_ANY;
00107    }
00108    else  // resolve destination address
00109    {
00110       DNX_PT_MUTEX_LOCK(&tcpMutex);
00111       if ((he = gethostbyname(itcp->host)) != 0)
00112          memcpy(&inaddr.sin_addr.s_addr, he->h_addr_list[0], he->h_length);
00113       DNX_PT_MUTEX_UNLOCK(&tcpMutex);
00114 
00115       if (!he) return DNX_ERR_ADDRESS;
00116    }
00117 
00118    if ((sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
00119    {
00120       dnxLog("dnxUdpOpen: socket failed: %s.", strerror(errno));
00121       return DNX_ERR_OPEN;
00122    }
00123 
00124    // determine how to handle socket connectivity based upon open mode
00125    if (mode == DNX_MODE_ACTIVE)
00126    {
00127       if (connect(sd, (struct sockaddr *)&inaddr, sizeof inaddr) != 0)
00128       {
00129          close(sd);
00130          dnxLog("dnxTcpOpen: connect(%lx) failed: %s.",
00131                (unsigned long)inaddr.sin_addr.s_addr, strerror(errno));
00132          return DNX_ERR_OPEN;
00133       }
00134    }
00135    else
00136    {
00137       // bind the socket to a local address and port and listen
00138       if (bind(sd, (struct sockaddr *)&inaddr, sizeof inaddr) != 0)
00139       {
00140          close(sd);
00141          dnxLog("dnxTcpOpen: bind(%lx) failed: %s.",
00142                (unsigned long)inaddr.sin_addr.s_addr, strerror(errno));
00143          return DNX_ERR_OPEN;
00144       }
00145       listen(sd, DNX_TCP_LISTEN);
00146    }
00147 
00148    itcp->socket = sd;
00149 
00150    return DNX_OK;
00151 }
00152 
00153 //----------------------------------------------------------------------------
00154 
00161 static int dnxTcpClose(iDnxChannel * icp)
00162 {
00163    iDnxTcpChannel * itcp = (iDnxTcpChannel *)
00164          ((char *)icp - offsetof(iDnxTcpChannel, ichan));
00165 
00166    assert(icp && itcp->socket);
00167 
00168    shutdown(itcp->socket, SHUT_RDWR);
00169    close(itcp->socket);
00170    itcp->socket = 0;
00171 
00172    return DNX_OK;
00173 }
00174 
00175 //----------------------------------------------------------------------------
00176 
00193 static int dnxTcpRead(iDnxChannel * icp, char * buf, int * size,
00194       int timeout, char * src)
00195 {
00196    iDnxTcpChannel * itcp = (iDnxTcpChannel *)
00197          ((char *)icp - offsetof(iDnxTcpChannel, ichan));
00198    char mbuf[DNX_MAX_MSG];
00199    unsigned short mlen;
00200 
00201    assert(icp && itcp->socket && buf && size && *size > 0);
00202 
00203    // implement timeout logic, if timeout value is greater than zero
00204    if (timeout > 0)
00205    {
00206       struct timeval tv;
00207       fd_set fd_rds;
00208       int nsd;
00209 
00210       FD_ZERO(&fd_rds);
00211       FD_SET(itcp->socket, &fd_rds);
00212 
00213       tv.tv_usec = 0L;
00214       tv.tv_sec = timeout;
00215 
00216       if ((nsd = select(itcp->socket + 1, &fd_rds, 0, 0, &tv)) == 0)
00217          return DNX_ERR_TIMEOUT;
00218 
00219       if (nsd < 0)
00220       {
00221          if (errno != EINTR)
00222          {
00223             dnxLog("dnxTcpRead: select failed: %s.", strerror(errno));
00224             return DNX_ERR_RECEIVE;
00225          }
00226          return DNX_ERR_TIMEOUT;
00227       }
00228    }
00229 
00230    // read the incoming message length
00231    if (read(itcp->socket, &mlen, sizeof mlen) != sizeof mlen)
00232       return DNX_ERR_RECEIVE;
00233    mlen = ntohs(mlen);
00234 
00235    // validate the message length
00236    if (mlen < 1 || mlen > DNX_MAX_MSG)
00237       return DNX_ERR_RECEIVE;
00238 
00239    // check to see if the message fits within the user buffer
00240    if (*size >= mlen)
00241    {
00242       if (read(itcp->socket, buf, (int)mlen) != (int)mlen)
00243          return DNX_ERR_RECEIVE;
00244       *size = (int)mlen;
00245    }
00246    else  // user buffer too small, read what we can, throw the rest away
00247    {
00248       if (read(itcp->socket, mbuf, (int)mlen) != (int)mlen)
00249          return DNX_ERR_RECEIVE;
00250       memcpy(buf, mbuf, *size);
00251    }
00252 
00253    // set source addr/port information, if desired
00254    if (src)
00255    {
00256       socklen_t slen;
00257       *src = 0;   // clear first byte in case getpeeraddr fails
00258       getpeername(itcp->socket, (struct sockaddr *)src, &slen);
00259    }
00260    return DNX_OK;
00261 }
00262 
00263 //----------------------------------------------------------------------------
00264 
00281 static int dnxTcpWrite(iDnxChannel * icp, char * buf, int size,
00282       int timeout, char * dst)
00283 {
00284    iDnxTcpChannel * itcp = (iDnxTcpChannel *)
00285          ((char *)icp - offsetof(iDnxTcpChannel, ichan));
00286    unsigned short mlen;
00287 
00288    assert(icp && itcp->socket && buf && size);
00289 
00290    // implement timeout logic, if timeout value is greater than zero
00291    if (timeout > 0)
00292    {
00293       struct timeval tv;
00294       fd_set fd_wrs;
00295       int nsd;
00296 
00297       FD_ZERO(&fd_wrs);
00298       FD_SET(itcp->socket, &fd_wrs);
00299 
00300       tv.tv_usec = 0L;
00301       tv.tv_sec = timeout;
00302 
00303       if ((nsd = select(itcp->socket + 1, 0, &fd_wrs, 0, &tv)) == 0)
00304          return DNX_ERR_TIMEOUT;
00305 
00306       if (nsd < 0)
00307       {
00308          if (errno != EINTR)
00309          {
00310             dnxLog("dnxTcpWrite: select failed: %s.", strerror(errno));
00311             return DNX_ERR_SEND;
00312          }
00313          return DNX_ERR_TIMEOUT;
00314       }
00315    }
00316 
00317    // convert the size into a network ushort
00318    mlen = (unsigned short)size;
00319    mlen = htons(mlen);
00320 
00321    // send the length of the message as a header
00322    if (write(itcp->socket, &mlen, sizeof mlen) != sizeof mlen)
00323       return DNX_ERR_SEND;
00324 
00325    // send the message
00326    if (write(itcp->socket, buf, size) != size)
00327       return DNX_ERR_SEND;
00328 
00329    return DNX_OK;
00330 }
00331 
00332 //----------------------------------------------------------------------------
00333 
00338 static void dnxTcpDelete(iDnxChannel * icp)
00339 {
00340    iDnxTcpChannel * itcp = (iDnxTcpChannel *)
00341          ((char *)icp - offsetof(iDnxTcpChannel, ichan));
00342 
00343    assert(icp && itcp->socket == 0);
00344 
00345    xfree(itcp->host);
00346    xfree(itcp);
00347 }
00348 
00349 //----------------------------------------------------------------------------
00350 
00359 static int dnxTcpNew(char * url, iDnxChannel ** icpp)
00360 {
00361    char * cp, * ep, * lastchar;
00362    iDnxTcpChannel * itcp;
00363    long port;
00364 
00365    assert(icpp && url && *url);
00366 
00367    // search for host:port in URL
00368    if ((cp = strstr(url, "://")) == 0)
00369       return DNX_ERR_BADURL;
00370    cp += 3;
00371 
00372    // find host/port separator ':' - copy host name
00373    if ((ep = strchr(cp, ':')) == 0 || ep == cp || ep - cp > HOST_NAME_MAX)
00374       return DNX_ERR_BADURL;
00375 
00376    // extract port number
00377    if ((port = strtol(ep + 1, &lastchar, 0)) < 1 || port > 65535
00378          || (*lastchar && *lastchar != '/'))
00379       return DNX_ERR_BADURL;
00380 
00381    // allocate a new iDnxTcpChannel object
00382    if ((itcp = (iDnxTcpChannel *)xmalloc(sizeof *itcp)) == 0)
00383       return DNX_ERR_MEMORY;
00384    memset(itcp, 0, sizeof *itcp);
00385 
00386    // save host name and port
00387    if ((itcp->host = (char *)xmalloc(ep - cp + 1)) == 0)
00388    {
00389       xfree(itcp);
00390       return DNX_ERR_MEMORY;
00391    }
00392    memcpy(itcp->host, cp, ep - cp);
00393    itcp->host[ep - cp] = 0;
00394    itcp->port = (int)port;
00395 
00396    // set I/O methods
00397    itcp->ichan.txOpen   = dnxTcpOpen;
00398    itcp->ichan.txClose  = dnxTcpClose;
00399    itcp->ichan.txRead   = dnxTcpRead;
00400    itcp->ichan.txWrite  = dnxTcpWrite;
00401    itcp->ichan.txDelete = dnxTcpDelete;
00402 
00403    *icpp = &itcp->ichan;
00404 
00405    return DNX_OK;
00406 }
00407 
00408 /*--------------------------------------------------------------------------
00409                            EXPORTED INTERFACE
00410   --------------------------------------------------------------------------*/
00411 
00419 int dnxTcpInit(int (**ptxAlloc)(char * url, iDnxChannel ** icpp))
00420 {
00421    DNX_PT_MUTEX_INIT(&tcpMutex);
00422 
00423    *ptxAlloc = dnxTcpNew;
00424 
00425    return DNX_OK;
00426 }
00427 
00428 //----------------------------------------------------------------------------
00429 
00432 void dnxTcpDeInit(void)
00433 {
00434    DNX_PT_MUTEX_DESTROY(&tcpMutex);
00435 }
00436 
00437 /*--------------------------------------------------------------------------*/
00438 

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