SASC-NG
DVBSoftCAM

network.c

Go to the documentation of this file.
00001 /*
00002  * Softcam plugin to VDR (C++)
00003  *
00004  * This code is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version.
00008  *
00009  * This code 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  * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
00018  */
00019 
00020 #include <stdlib.h>
00021 #include <stdio.h>
00022 #include <string.h>
00023 #include <errno.h>
00024 #include <unistd.h>
00025 #include <fcntl.h>
00026 #include <sys/socket.h>
00027 #include <netinet/in.h>
00028 #include <netdb.h>
00029 
00030 #include <vdr/thread.h>
00031 #include <vdr/tools.h>
00032 
00033 #include "network.h"
00034 #include "misc.h"
00035 #include "log-core.h"
00036 
00037 #define DEFAULT_CONNECT_TIMEOUT   20*1000   // ms
00038 #define DEFAULT_READWRITE_TIMEOUT 3*1000    // ms
00039 #define DEFAULT_IDLE_TIMEOUT      120*1000  // ms
00040 
00041 // -- cNetWatcher ---------------------------------------------------------------
00042 
00043 class cNetWatcher : protected cThread {
00044 private:
00045   cSimpleList<cNetSocket> socks;
00046 protected:
00047   virtual void Action(void);
00048 public:
00049   cNetWatcher(void);
00050   ~cNetWatcher();
00051   void Up(cNetSocket *so);
00052   void Down(cNetSocket *so);
00053   };
00054 
00055 static cNetWatcher nw;
00056 
00057 cNetWatcher::cNetWatcher(void)
00058 :cThread("Netwatcher")
00059 {}
00060 
00061 cNetWatcher::~cNetWatcher()
00062 {
00063   Cancel(2);
00064   Lock();
00065   cNetSocket *so;
00066   while((so=socks.First())) socks.Del(so,false);
00067   Unlock();
00068 }
00069 
00070 void cNetWatcher::Up(cNetSocket *so)
00071 {
00072   Lock();
00073   socks.Add(so);
00074   if(!Active()) Start();
00075   Unlock();
00076 }
00077 
00078 void cNetWatcher::Down(cNetSocket *so)
00079 {
00080   Lock();
00081   socks.Del(so,false);
00082   Unlock();
00083 }
00084 
00085 void cNetWatcher::Action(void)
00086 {
00087   while(Running()) {
00088     sleep(1);
00089     Lock();
00090     for(cNetSocket *so=socks.First(); so;) {
00091       cNetSocket *next=socks.Next(so);
00092       so->Lock();
00093       if(so->connected && so->activity.TimedOut()) {
00094         PRINTF(L_CORE_NET,"idle timeout, disconnected %s:%d",so->hostname,so->port);
00095         so->Disconnect();
00096         }
00097       so->Unlock();
00098       so=next;
00099       }
00100     Unlock();
00101     }
00102 }
00103 
00104 // -- cNetSocket ------------------------------------------------------------------
00105 
00106 cNetSocket::cNetSocket(void)
00107 {
00108   hostname=0; sd=-1; udp=connected=quietlog=false;
00109   conTimeout=DEFAULT_CONNECT_TIMEOUT; rwTimeout=DEFAULT_READWRITE_TIMEOUT;
00110   idleTimeout=DEFAULT_IDLE_TIMEOUT;
00111 }
00112 
00113 cNetSocket::~cNetSocket()
00114 {
00115   Disconnect();
00116   free(hostname);
00117 }
00118 
00119 void cNetSocket::Activity(void)
00120 {
00121   activity.Set(idleTimeout);
00122 }
00123 
00124 bool cNetSocket::GetAddr(struct sockaddr_in *saddr, const char *Hostname, int Port)
00125 {
00126   const struct hostent * const hostaddr=gethostbyname(Hostname);
00127   if(hostaddr) {
00128     saddr->sin_family=AF_INET;
00129     saddr->sin_port=htons(Port);
00130     saddr->sin_addr.s_addr=((struct in_addr *)hostaddr->h_addr)->s_addr;
00131     return true;
00132     }
00133   PRINTF(L_GEN_ERROR,"socket: name lookup error for %s",Hostname);
00134   return false;
00135 }
00136 
00137 cString cNetSocket::StrError(int err) const
00138 {
00139   char buf[64];
00140   return strerror_r(err,buf,sizeof(buf));
00141 }
00142 
00143 int cNetSocket::GetSocket(bool Udp)
00144 {
00145   int proto=0;
00146   const struct protoent * const ptrp=getprotobyname(Udp?"udp":"tcp");
00147   if(ptrp) proto=ptrp->p_proto;
00148   int so;
00149   if((so=socket(PF_INET,Udp?SOCK_DGRAM:SOCK_STREAM,proto))>=0) {
00150     int flgs=fcntl(so,F_GETFL);
00151     if(flgs>=0) {
00152       if(fcntl(so,F_SETFL,flgs|O_NONBLOCK)==0)
00153         return so;
00154       else PRINTF(L_GEN_ERROR,"socket: fcntl SETFL failed: %s",*StrError(errno));
00155       }
00156     else PRINTF(L_GEN_ERROR,"socket: fcntl GETFL failed: %s",*StrError(errno));
00157     close(so);
00158     }
00159   else PRINTF(L_GEN_ERROR,"socket: socket failed: %s",*StrError(errno));
00160   return -1;
00161 }
00162 
00163 bool cNetSocket::Connect(const char *Hostname, int Port, int timeout)
00164 {
00165   Lock();
00166   Disconnect();
00167   if(Hostname) {
00168     free(hostname);
00169     hostname=strdup(Hostname); port=Port;
00170     }
00171   if(timeout<0) timeout=conTimeout;
00172   struct sockaddr_in socketAddr;
00173   if(GetAddr(&socketAddr,hostname,port) && (sd=GetSocket(udp))>=0) {
00174     if(!quietlog) PRINTF(L_CORE_NET,"connecting to %s:%d/%s (%d.%d.%d.%d)",
00175                hostname,port,udp?"udp":"tcp",
00176                (socketAddr.sin_addr.s_addr>> 0)&0xff,(socketAddr.sin_addr.s_addr>> 8)&0xff,(socketAddr.sin_addr.s_addr>>16)&0xff,(socketAddr.sin_addr.s_addr>>24)&0xff);
00177     int r;
00178     do { r=connect(sd,(struct sockaddr *)&socketAddr,sizeof(socketAddr)); } while(r<0 && errno==EINTR);
00179     if(r==0) connected=true;
00180     else if(errno==EINPROGRESS) {
00181       if(Select(false,timeout)>0) {
00182         int r=-1;
00183         unsigned int l=sizeof(r);
00184         if(getsockopt(sd,SOL_SOCKET,SO_ERROR,&r,&l)==0) {
00185           if(r==0)
00186             connected=true;
00187           else PRINTF(L_GEN_ERROR,"socket: connect failed (late): %s",*StrError(r));
00188           }
00189         else PRINTF(L_GEN_ERROR,"socket: getsockopt failed: %s",*StrError(errno));
00190         }
00191       else PRINTF(L_GEN_ERROR,"socket: connect timed out");
00192       }
00193     else PRINTF(L_GEN_ERROR,"socket: connect failed: %s",*StrError(errno));
00194 
00195     if(connected) { Activity(); Unlock(); nw.Up(this); return true; }
00196     }
00197   Unlock();
00198   Disconnect();
00199   return false;
00200 }
00201 
00202 bool cNetSocket::Bind(const char *Hostname, int Port)
00203 {
00204   Lock();
00205   Disconnect();
00206   if(Hostname) {
00207     free(hostname);
00208     hostname=strdup(Hostname); port=Port;
00209     }
00210   struct sockaddr_in socketAddr;
00211   if(GetAddr(&socketAddr,hostname,port) && (sd=GetSocket(udp))>=0) {
00212     if(!quietlog) PRINTF(L_CORE_NET,"socket: binding to %s:%d/%s (%d.%d.%d.%d)",
00213                hostname,port,udp?"udp":"tcp",
00214                (socketAddr.sin_addr.s_addr>> 0)&0xff,(socketAddr.sin_addr.s_addr>> 8)&0xff,(socketAddr.sin_addr.s_addr>>16)&0xff,(socketAddr.sin_addr.s_addr>>24)&0xff);
00215     int r;
00216     do { r=bind(sd,(struct sockaddr *)&socketAddr,sizeof(socketAddr)); } while(r<0 && errno==EINTR);
00217     if(r==0) {
00218       connected=true;
00219       Activity(); Unlock(); nw.Up(this);
00220       return true;
00221       }
00222     else PRINTF(L_GEN_ERROR,"socket: bind failed: %s",*StrError(errno));
00223     }
00224   Unlock();
00225   Disconnect();
00226   return false;
00227 }
00228 
00229 void cNetSocket::Disconnect(void)
00230 {
00231   nw.Down(this);
00232   cMutexLock lock(this);
00233   if(sd>=0) { close(sd); sd=-1; }
00234   quietlog=connected=false;
00235 }
00236 
00237 void cNetSocket::Flush(void)
00238 {
00239   cMutexLock lock(this);
00240   if(sd>=0) {
00241     unsigned char buff[512];
00242     while(read(sd,buff,sizeof(buff))>0) Activity();
00243     }
00244 }
00245 
00246 // len>0 : block read len bytes or fail
00247 // len<0 : read available bytes up to len
00248 int cNetSocket::Read(unsigned char *data, int len, int timeout)
00249 {
00250   cMutexLock lock(this);
00251   if(timeout<0) timeout=rwTimeout;
00252   bool blockmode=true;
00253   if(len<0) { len=-len; blockmode=false; }
00254   else if(len==0) {
00255     PRINTF(L_GEN_DEBUG,"internal: zero length on socket read");
00256     errno=EINVAL;
00257     return -1;
00258     }
00259   int cnt=0, r;
00260   cTimeMs tim;
00261   do {
00262     r=timeout;
00263     if(r>0 && blockmode) { r-=tim.Elapsed(); if(r<10) r=10; }
00264     r=Select(true,r);
00265     if(r>0) {
00266       do { r=read(sd,data+cnt,len-cnt); } while(r<0 && errno==EINTR);
00267       if(r<0) PRINTF(L_GEN_ERROR,"socket: read failed: %s",*StrError(errno));
00268       else if(r>0) cnt+=r;
00269       else {
00270         PRINTF(L_GEN_ERROR,"socket: EOF on read");
00271         errno=ECONNRESET;
00272         }
00273       }
00274     } while(r>0 && cnt<len && blockmode);
00275   Activity();
00276   if((!blockmode && cnt>0) || cnt>=len) {
00277     HEXDUMP(L_CORE_NETDATA,data,cnt,"network read");
00278     return cnt;
00279     }
00280   return -1;
00281 }
00282 
00283 int cNetSocket::Write(const unsigned char *data, int len, int timeout)
00284 {
00285   cMutexLock lock(this);
00286   if(timeout<0) timeout=rwTimeout;
00287   int cnt=0, r;
00288   cTimeMs tim;
00289   do {
00290     r=timeout;
00291     if(r>0) { r-=tim.Elapsed(); if(r<10) r=10; }
00292     r=Select(false,r);
00293     if(r>0) {
00294       do { r=write(sd,data+cnt,len-cnt); } while(r<0 && errno==EINTR);
00295       if(r<0) PRINTF(L_GEN_ERROR,"socket: write failed: %s",*StrError(errno));
00296       else if(r>0) cnt+=r;
00297       }
00298     } while(r>0 && cnt<len);
00299   Activity();
00300   if(cnt>=len) {
00301     HEXDUMP(L_CORE_NETDATA,data,cnt,"network write");
00302     return cnt;
00303     }
00304   return -1;
00305 }
00306 
00307 int cNetSocket::SendTo(const char *Host, int Port, const unsigned char *data, int len, int timeout)
00308 {
00309   cMutexLock lock(this);
00310   if(timeout<0) timeout=rwTimeout;
00311   int cnt=0, r=-1;
00312   struct sockaddr_in saddr;
00313   if(GetAddr(&saddr,Host,Port)) {
00314     cTimeMs tim;
00315     do {
00316       r=timeout;
00317       if(r>0) { r-=tim.Elapsed(); if(r<10) r=10; }
00318       r=Select(false,r);
00319       if(r>0) {
00320         do { r=sendto(sd,data+cnt,len-cnt,0,(struct sockaddr *)&saddr,sizeof(saddr)); } while(r<0 && errno==EINTR);
00321         if(r<0) PRINTF(L_GEN_ERROR,"socket: sendto %d.%d.%d.%d:%d failed: %s",(saddr.sin_addr.s_addr>> 0)&0xff,(saddr.sin_addr.s_addr>> 8)&0xff,(saddr.sin_addr.s_addr>>16)&0xff,(saddr.sin_addr.s_addr>>24)&0xff,Port,*StrError(errno));
00322         else if(r>0) cnt+=r;
00323         }
00324       } while(r>0 && cnt<len);
00325     }
00326   Activity();
00327   if(cnt>=len) {
00328     HEXDUMP(L_CORE_NETDATA,data,cnt,"network sendto %d.%d.%d.%d:%d",(saddr.sin_addr.s_addr>> 0)&0xff,(saddr.sin_addr.s_addr>> 8)&0xff,(saddr.sin_addr.s_addr>>16)&0xff,(saddr.sin_addr.s_addr>>24)&0xff,Port);
00329     return cnt;
00330     }
00331   return -1;
00332 }
00333 
00334 int cNetSocket::Select(bool forRead, int timeout)
00335 {
00336   if(sd>=0) {
00337     fd_set fds;
00338     FD_ZERO(&fds); FD_SET(sd,&fds);
00339     struct timeval tv;
00340 
00341     if(timeout>0 && timeout<60)
00342       PRINTF(L_GEN_DEBUG,"socket: internal: small timeout value %d",timeout);
00343 
00344     tv.tv_sec=timeout/1000; tv.tv_usec=(timeout%1000)*1000;
00345     int r;
00346     do { r=select(sd+1,forRead ? &fds:0,forRead ? 0:&fds,0,&tv); } while(r<0 && errno==EINTR);
00347     if(r>0) return 1;
00348     else if(r<0) {
00349       PRINTF(L_GEN_ERROR,"socket: select failed: %s",*StrError(errno));
00350       return -1;
00351       }
00352     else {
00353       if(timeout>0 && !quietlog) PRINTF(L_CORE_NET,"socket: select timed out (%d ms)",timeout);
00354       errno=ETIMEDOUT;
00355       return 0;
00356       }
00357     }
00358   return -1;
00359 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines