"Fossies" - the Fresh Open Source Software Archive

Member "hermes-1.9/src/Socket.cpp" (6 Sep 2014, 15852 Bytes) of package /linux/privat/hermes-1.9.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "Socket.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.8_vs_1.9.

    1 /**
    2  * hermes antispam proxy
    3  * Copyright (C) 2006, 2007 Juan José Gutiérrez de Quevedo <juanjo@gutierrezdequevedo.com>
    4  *
    5  * This program is free software; you can redistribute it and/or modify
    6  * it under the terms of the GNU General Public License as published by
    7  * the Free Software Foundation; version 2 of the License
    8  *
    9  * This program is distributed in the hope that it will be useful,
   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12  * GNU General Public License for more details.
   13  *
   14  * You should have received a copy of the GNU General Public License along
   15  * with this program; if not, write to the Free Software Foundation, Inc.,
   16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   17  *
   18  * @author Juan José Gutiérrez de Quevedo <juanjo@gutierrezdequevedo.com>
   19  */
   20 #include "Socket.h"
   21 #include <unistd.h>
   22 
   23 int Socket::created_sockets=0;
   24 
   25 #ifdef HAVE_SSL
   26 SSL_CTX *Socket::ssl_ctx_server=NULL;
   27 SSL_CTX *Socket::ssl_ctx_client=NULL;
   28 #endif //HAVE_SSL
   29 
   30 extern Configfile cfg;
   31 extern LOGGER_CLASS hermes_log;
   32 
   33 Socket::Socket():fd(-1)
   34 #ifdef HAVE_SSL
   35 ,ssl_enabled(false),ssl(NULL)
   36 #endif //HAVE_SSL
   37 {
   38   if(!created_sockets)
   39   {
   40     #ifdef WIN32
   41       /* fuck windows, it needs this sh*t before allowing sockets to work */
   42       WSADATA wsaData;
   43       int wsa=WSAStartup(0xff,&wsaData);
   44       if(wsa)
   45       { 
   46         perror("Windows not working, call microsoft");
   47         exit(-1);
   48       }
   49     #endif //WIN32
   50     #ifdef HAVE_SSL
   51       SSL_library_init();
   52       SSL_load_error_strings();
   53 
   54       //initialize the context for both server and client operation
   55       //server
   56       ssl_ctx_server=NULL;
   57       ssl_ctx_server=SSL_CTX_new(SSLv23_server_method());
   58       /* create context */
   59       if(!ssl_ctx_server)
   60         throw Exception(_("Error creating SSL context"),__FILE__,__LINE__);
   61       /* load certificate */
   62       if(SSL_CTX_use_certificate_chain_file(ssl_ctx_server,cfg.getCertificateFile().c_str())==-1)
   63         throw Exception(_("Error loading certificate"),__FILE__,__LINE__);
   64       /* load private key */
   65       if(SSL_CTX_use_PrivateKey_file(ssl_ctx_server,cfg.getPrivateKeyFile().c_str(),SSL_FILETYPE_PEM)==-1)
   66         throw Exception(_("Error loading private key"),__FILE__,__LINE__);
   67       /* check that private key and cert match */
   68       if(!SSL_CTX_check_private_key(ssl_ctx_server))
   69         throw Exception(_("Private key doesn't match certificate file"),__FILE__,__LINE__);
   70 
   71       //client
   72       ssl_ctx_client=NULL;
   73       ssl_ctx_client=SSL_CTX_new(SSLv23_client_method());
   74       if(!ssl_ctx_client)
   75         throw Exception(_("Error creating SSL context"),__FILE__,__LINE__);
   76 
   77       //set options to make SSL_read and SSL_write behave more like read and write
   78       SSL_CTX_set_mode(ssl_ctx_server,SSL_MODE_ENABLE_PARTIAL_WRITE); //PARTIAL_WRITE allows a write to suceed with fewer bytes sent
   79       SSL_CTX_set_mode(ssl_ctx_client,SSL_MODE_ENABLE_PARTIAL_WRITE);
   80       SSL_CTX_set_mode(ssl_ctx_server,SSL_MODE_AUTO_RETRY); //AUTO_RETRY will block SSL_read and SSL_write if a renegotiation is required
   81       SSL_CTX_set_mode(ssl_ctx_client,SSL_MODE_AUTO_RETRY);
   82 
   83 
   84     #endif //HAVE_SSL
   85   }
   86   created_sockets++;
   87 }
   88 
   89 /**
   90  *
   91  * we have constructor and init because we may want to create the object
   92  * and afterwards assign an fd obtained in other way with setFD
   93  *
   94  */
   95 void Socket::init()
   96 {
   97   // create socket ...
   98   fd=socket(PF_INET,SOCK_STREAM,0);
   99   //configure timeout
  100   setTimeout(60,60);
  101 
  102   if(fd==-1)
  103     throw Exception(_(string("Error creating socket :")+Utils::inttostr(errno)+" "+Utils::errnotostrerror(errno)),__FILE__,__LINE__);
  104 }
  105 
  106 /**
  107  * close the socket
  108  */
  109 void Socket::close()
  110 {
  111   if(fd!=-1)
  112   {
  113     /* shutdown(fd,SHUT_RDWR); */ //we don't care about return, if we are on eof we are already closed and this doesn't hurt
  114     //the same applies to close/closesocket
  115     #ifndef WIN32
  116     ::close(fd);
  117     #else
  118     closesocket(fd);
  119     #endif //WIN32
  120     fd=-1;
  121   }
  122 }
  123 
  124 /**
  125  * destructor.
  126  *
  127  * we close the socket if it's still open, and we 
  128  * decrement the created_sockets count
  129  * when the count is 0, destroy the ssl contexts and,
  130  * if on windows, do a WSACleanup
  131  */
  132 Socket::~Socket()
  133 {
  134   #ifdef HAVE_SSL
  135   if(ssl_enabled&&ssl!=NULL)
  136     SSL_free(ssl);
  137   #endif //HAVE_SSL
  138   close();
  139 
  140   created_sockets--;
  141   if(!created_sockets)
  142   {
  143     #ifdef WIN32
  144       WSACleanup();
  145     #endif //WIN32
  146     #ifdef HAVE_SSL
  147       if(ssl_enabled&&ssl_ctx_server!=NULL)
  148         SSL_CTX_free(ssl_ctx_server);
  149       if(ssl_enabled&&ssl_ctx_client!=NULL)
  150         SSL_CTX_free(ssl_ctx_client);
  151     #endif //HAVE_SSL
  152   }
  153 }
  154 
  155 #ifdef HAVE_SSL
  156 
  157 /**
  158  * is ssl enabled on the socket?
  159  */
  160 bool Socket::is_ssl_enabled()
  161 {
  162   return ssl_enabled;
  163 }
  164 
  165 /**
  166  * prepare ssl on the socket
  167  *
  168  * @param server whether to enable server ssl or client ssl
  169  */
  170 void Socket::prepareSSL(bool server)
  171 {
  172   if(server)
  173     ssl=SSL_new(ssl_ctx_server);
  174   else
  175     ssl=SSL_new(ssl_ctx_client);
  176 
  177   if(NULL==ssl)
  178     throw Exception(_("Error creating ssl structure"),__FILE__,__LINE__);
  179 
  180   if(1!=SSL_set_fd(ssl,fd))
  181     throw Exception(_("Error setting FD"),__FILE__,__LINE__);
  182 
  183   if(0==RAND_status())
  184     throw Exception(_("PRNG has not enough data. Are you missing /dev/[u]random?"),__FILE__,__LINE__);
  185 }
  186 
  187 /**
  188  * actually do the ssl handshake and start receiving encoded
  189  *
  190  * @param server whether to enable server ssl or client ssl
  191  */
  192 void Socket::startSSL(bool server)
  193 {
  194   int retval;
  195 
  196   retval=server? SSL_accept(ssl) : SSL_connect(ssl);
  197 
  198   //SSL_accept and SSL_connect have the same semantics so we handle them together
  199   if(1!=retval)
  200     throw Exception(_("Error doing SSL handshake on the socket"),__FILE__,__LINE__);
  201 
  202   //only set ssl_enabled if we have suceeded with everything
  203   ssl_enabled=true;
  204 }
  205 #endif //HAVE_SSL
  206 
  207 bool Socket::isClosed()
  208 {
  209   return -1==fd;
  210 }
  211 
  212 /**
  213  * read lon bytes from the socket to buf buffer
  214  *
  215  * @param buf buffer to fill(must be previously reserved)
  216  * @param lon number of bytes to read
  217  * @return number of bytes read
  218  */
  219 ssize_t Socket::readBytes(void *buf,ssize_t lon)
  220 {
  221   ssize_t retval=0; //shut compiler up
  222   ssize_t readed=0;
  223 
  224   while(readed<lon)
  225   {
  226     if(-1==fd)
  227       throw Exception(_("Trying to read a non-opened or already closed socket"),__FILE__,__LINE__);
  228 
  229     #ifdef HAVE_SSL
  230     if(ssl_enabled)
  231     {
  232       retval=SSL_read(ssl,buf,lon);
  233     }
  234     else
  235     #endif //HAVE_SSL
  236     {
  237       retval=recv(fd,(char *)buf,lon,MSG_NOSIGNAL);
  238     }
  239 
  240     if(!retval)
  241     {
  242       throw NetworkException(_("Peer closed connection"),__FILE__,__LINE__);
  243     }
  244 
  245     if(retval<0)
  246     {
  247       #ifdef HAVE_SSL
  248       if(ssl_enabled)
  249       {
  250         throw NetworkException(_("SSL error number: ")+Utils::inttostr(SSL_get_error(ssl,retval)),__FILE__,__LINE__);
  251       }
  252       else
  253       #endif //HAVE_SSL
  254       {
  255         throw NetworkException(_(Utils::errnotostrerror(errno)),__FILE__,__LINE__);
  256       }
  257     }
  258     readed+=lon;
  259   }
  260 
  261   return retval;
  262 }
  263 
  264 /**
  265  * read a single byte from the socket
  266  *
  267  * @return the read byte(char)
  268  */
  269 char Socket::readByte()
  270 {
  271   char c=0;
  272 
  273   readBytes(&c,1);
  274 
  275   return c;
  276 }
  277 
  278 /**
  279  * read a full line and return a string with it
  280  *
  281  * @return the read string
  282  */
  283 string Socket::readLine()
  284 {
  285   char c=0;
  286   stringstream s;
  287 
  288   do
  289   {
  290     c=readByte();
  291 
  292     if(c!=10&&c!=13&&c!=0)
  293       s<<c;
  294   }
  295   while(c!=10&&!isClosed());
  296 
  297   LDEB("r" + string(ssl_enabled?"s":"") + ">" + s.str());
  298 
  299   return s.str();
  300 }
  301 
  302 void Socket::writeBytes(void *bytes,ssize_t len)
  303 {
  304   int retval;
  305   ssize_t written=0;
  306 
  307   if(fd==-1)
  308     throw Exception(_("Trying to write to a non-opened socket"),__FILE__,__LINE__);
  309 
  310   while(written<len)
  311   {
  312     #ifdef HAVE_SSL
  313     if(ssl_enabled)
  314       retval=SSL_write(ssl,bytes,len);
  315     else
  316     #endif //HAVE_SSL
  317       retval=send(fd,(char *)bytes,len,MSG_NOSIGNAL);
  318 
  319     if(!retval)
  320       throw NetworkException(_("Peer closed connection"),__FILE__,__LINE__);
  321 
  322     if(retval<0)
  323     {
  324       #ifdef HAVE_SSL
  325       if(ssl_enabled)
  326       {
  327         throw NetworkException(_("SSL error number: ")+Utils::inttostr(SSL_get_error(ssl,retval)),__FILE__,__LINE__);
  328       }
  329       else
  330       #endif //HAVE_SSL
  331       {
  332         throw NetworkException(_(Utils::errnotostrerror(errno)),__FILE__,__LINE__);
  333       }
  334     }
  335     written+=len;
  336   }
  337 }
  338 
  339 void Socket::writeByte(char c)
  340 {
  341   writeBytes(&c,sizeof(char));
  342 }
  343 
  344 void Socket::writeLine(string s)
  345 {
  346 
  347   LDEB("w" + string(ssl_enabled?"s":"") + ">" + s);
  348   s+="\r\n";
  349 
  350   writeBytes((void *)s.c_str(),s.length());
  351 }
  352 
  353 void Socket::setFD(int p_fd)
  354 {
  355   if(fd>0)
  356     close();
  357 
  358   if(p_fd>0)
  359   {
  360     fd=p_fd;
  361     setTimeout(60,60);
  362   }
  363   else
  364     throw Exception(_("Error: fd not valid"),__FILE__,__LINE__);
  365 }
  366 
  367 /**
  368  * returns true if there's data waiting to be read on the socket
  369  * if it's a serversocket means that a new connection is waiting
  370  *
  371  * @param unsigned int seconds to wait before returning true or false
  372  *
  373  * @return bool true if there's data(or a connection waiting) and false if there's not
  374  *
  375  */
  376 bool Socket::canRead(float time)
  377 {
  378   fd_set rfd;
  379   struct timeval timeout;
  380   int seconds=0;
  381   int useconds=0;
  382 
  383   //calculate seconds and useconds
  384   seconds=int(time);
  385   useconds=int((time-seconds)*1000);
  386 
  387   //set rfd to the fd of our connection
  388   FD_ZERO(&rfd);
  389   FD_SET(fd,&rfd);
  390 
  391   //we wait x seconds for an incoming connection
  392   timeout.tv_usec=useconds;
  393   timeout.tv_sec=seconds;
  394   if(select(fd+1,&rfd,NULL,NULL,&timeout)>0)
  395     return true;
  396   else
  397   #ifdef HAVE_SSL
  398   if(ssl_enabled)
  399     if(SSL_pending(ssl))
  400       return true;
  401     else
  402       return false;
  403   else
  404     return false;
  405   #else
  406     return false;
  407   #endif //HAVE_SSL
  408 }
  409 
  410 bool Socket::connect(string host,unsigned int port)
  411 {
  412   struct sockaddr address;
  413   struct sockaddr_in *inetaddress;
  414 
  415   address=(sockaddr)Socket::resolve(host);
  416   inetaddress=(sockaddr_in *)&address;
  417   inetaddress->sin_port=htons(port);
  418 
  419   int retval=::connect(fd,&address,sizeof(address));
  420   if(retval==-1)
  421     throw Exception(string(_("Error connecting to "))+host+":"+Utils::inttostr(port)+" "+string("(")+Socket::resolveToString(host)+string(")"),__FILE__,__LINE__);
  422   if(!retval)
  423     return true;
  424   else
  425     return false;
  426 }
  427 
  428 #ifdef HAVE_GETADDRINFO
  429 struct sockaddr Socket::resolve(string host)
  430 {
  431   struct addrinfo *hostinfo=NULL;
  432   struct addrinfo hints;
  433   struct sockaddr resolvedip;
  434   int error;
  435 
  436   //configure hints to use IPv4 and IPv6, and resolve ips to name
  437   memset(&hints,0,sizeof(hints));
  438   hints.ai_flags=AI_ADDRCONFIG;
  439   hints.ai_family=AF_UNSPEC;
  440   hints.ai_socktype=SOCK_STREAM;
  441   hints.ai_protocol=IPPROTO_TCP;
  442   hints.ai_addrlen=0;
  443   hints.ai_addr=0;
  444   hints.ai_canonname=NULL;
  445 
  446   error=getaddrinfo(host.c_str(),NULL,&hints,&hostinfo);
  447   if(error)
  448     #ifdef HAVE_GAI_STRERROR
  449       throw Exception(gai_strerror(error),__FILE__,__LINE__);
  450     #else
  451       #ifdef WIN32
  452         throw Exception("Winsock error "+Utils::inttostr(WSAGetLastError()),__FILE__,__LINE__);
  453       #else
  454         throw Exception("Socket error number "+Utils::inttostr(error),__FILE__,__LINE__);
  455       #endif //WIN32
  456     #endif //HAVE_GAI_STRERROR
  457 
  458   resolvedip=*(hostinfo->ai_addr);
  459   freeaddrinfo(hostinfo);
  460   return resolvedip;
  461 }
  462 
  463 string Socket::resolveToString(string host)
  464 {
  465   struct sockaddr hostinfo;
  466   struct sockaddr_in *inetip;
  467   string strip;
  468 
  469   hostinfo=Socket::resolve(host);
  470   inetip=(sockaddr_in*)&hostinfo;
  471   strip=string(inet_ntoa(inetip->sin_addr));
  472   
  473   return strip;
  474 }
  475 
  476 string Socket::resolveInverselyToString(string ip)
  477 {
  478   int error;
  479   char hostname[NI_MAXHOST];
  480   struct sockaddr addr;
  481 
  482   addr=resolve(ip);
  483   error=getnameinfo(&addr,sizeof(struct sockaddr),hostname,NI_MAXHOST,NULL,0,NI_NAMEREQD);
  484 
  485   if(error)
  486   {
  487     if(error==EAI_NONAME)  //if the problem is that we didn't get a hostname, return empty string
  488     {
  489       hostname[0]='\0';
  490     }
  491     else
  492     {
  493       #ifdef HAVE_GAI_STRERROR
  494         throw Exception(gai_strerror(error)+Utils::inttostr(error),__FILE__,__LINE__);
  495       #else
  496         #ifdef WIN32
  497           throw Exception("Winsock error "+Utils::inttostr(WSAGetLastError()),__FILE__,__LINE__);
  498         #else
  499           throw Exception("Socket error number "+Utils::inttostr(error),__FILE__,__LINE__);
  500         #endif //WIN32
  501       #endif //HAVE_GAI_STRERROR
  502     }
  503   }
  504 
  505   return string(hostname);
  506 }
  507 
  508 #else
  509 
  510 /*
  511  * WARNING!!! WARNING!!! WARNING!!!
  512  * the following 3 functions are NOT thread-safe UNLESS used on a platform
  513  * that is using thread-local storage (i.e. windows), so be VERY careful with
  514  * them
  515  */
  516 struct sockaddr Socket::resolve(string host)
  517 {
  518   struct sockaddr_in *addr_in;
  519   struct sockaddr addr;
  520 
  521   addr_in=(sockaddr_in *)&addr;
  522   addr_in->sin_addr.s_addr=inet_addr(Socket::resolveToString(host).c_str());
  523   addr_in->sin_family=AF_INET;
  524 
  525   return addr;
  526 }
  527 
  528 string Socket::resolveToString(string host)
  529 {
  530   struct hostent *hostinfo;
  531   struct in_addr addr;
  532 
  533   hostinfo=gethostbyname(host.c_str());
  534 
  535   if(NULL==hostinfo)
  536     throw Exception("Error resolving "+host,__FILE__,__LINE__);
  537 
  538   memcpy(&addr,hostinfo->h_addr,sizeof(addr));
  539   return string(inet_ntoa(addr));
  540 }
  541 
  542 string Socket::resolveInverselyToString(string ip)
  543 {
  544   struct hostent *hostinfo;
  545   unsigned long addr;
  546 
  547   assert(ip.length()<16);
  548 
  549   addr=inet_addr(ip.c_str());
  550   hostinfo=gethostbyaddr((char *)&addr,4,AF_INET);
  551 
  552   if(NULL==hostinfo)
  553     throw Exception("Error resolving "+ip,__FILE__,__LINE__);
  554 
  555   return string(hostinfo->h_name);
  556 }
  557 #endif //HAVE_GETADDRINFO
  558 
  559 int Socket::getFD()
  560 {
  561   return fd;
  562 }
  563 
  564 /**
  565  * this function sets timeouts for a socket on receive and send
  566  * if either recv or send is -1, the timeout is not set, so for example
  567  * if you want to set recv timeout but NOT send timeout, you could call
  568  * this function like this:
  569  *   Socket::setTimeout(fd,3,-1);  <-- set 3 seconds receive timeout and
  570  *                                            don't change send timeout
  571  *
  572  * setting timeout of either one to 0 disables the timeout, socket will
  573  * block forever for data
  574  *
  575  * this function is needed because setting these timeouts is one of the
  576  * less portable setsockopt functions, and lot's of operating systems
  577  * do it differently
  578  *
  579  * @see setsockopt(2), socket(7) $LINUX_SRC/net/core/sock.c:{sock_setsockopt,sock_set_timeout}
  580  *
  581  * @todo check procedure on other operating systems like Solaris, *BSD and others
  582  *
  583  * @param recv timeout for receiving, a number in the format of 1.5 (one second and 500 milliseconds)
  584  * @param send same as recv but for sending
  585  */
  586 void Socket::setTimeout(float recv,float send)
  587 {
  588   if(fd<=0)
  589     throw Exception("Socket invalid: "+Utils::inttostr(fd)+" ",__FILE__,__LINE__);
  590   #ifdef WIN32
  591     //set timeout for receiving
  592     if(recv)
  593     {
  594       unsigned timeout=int(recv*1000);
  595       if(-1==setsockopt(fd,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(timeout)))
  596         throw Exception(Utils::errnotostrerror(errno),__FILE__,__LINE__);
  597     }
  598 
  599     //set timeout for sending
  600     if(send)
  601     {
  602       unsigned timeout=int(send*1000);
  603       if(-1==setsockopt(fd,SOL_SOCKET,SO_SNDTIMEO,(char *)&timeout,sizeof(timeout)))
  604         throw Exception(Utils::errnotostrerror(errno),__FILE__,__LINE__);
  605     }
  606   #else
  607     struct timeval timeout;
  608     int seconds=0;
  609     int useconds=0;
  610 
  611     //calculate seconds and useconds
  612     if(recv)
  613     {
  614       seconds=int(recv);
  615       useconds=int((recv-seconds)*1000);
  616       timeout.tv_usec=useconds;
  617       timeout.tv_sec=seconds;
  618       if(-1==setsockopt(fd,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(timeout)))
  619         throw Exception(Utils::errnotostrerror(errno),__FILE__,__LINE__);
  620     }
  621 
  622     if(send)
  623     {
  624       seconds=int(send);
  625       useconds=int((send-seconds)*1000);
  626       timeout.tv_usec=useconds;
  627       timeout.tv_sec=seconds;
  628       if(-1==setsockopt(fd,SOL_SOCKET,SO_SNDTIMEO,(char *)&timeout,sizeof(timeout)))
  629         throw Exception(Utils::errnotostrerror(errno),__FILE__,__LINE__);
  630     }
  631   #endif
  632 }