OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
TcpSocket.cc
Go to the documentation of this file.
1 // TcpSocket.cc
2 
3 // This file is part of bes, A C++ back-end server implementation framework
4 // for the OPeNDAP Data Access Protocol.
5 
6 // Copyright (c) 2004-2009 University Corporation for Atmospheric Research
7 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 //
23 // You can contact University Corporation for Atmospheric Research at
24 // 3080 Center Green Drive, Boulder, CO 80301
25 
26 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
27 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
28 //
29 // Authors:
30 // pwest Patrick West <pwest@ucar.edu>
31 // jgarcia Jose Garcia <jgarcia@ucar.edu>
32 
33 #include <ctype.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
38 #include <netdb.h>
39 #include <fcntl.h>
40 #include <netinet/tcp.h>
41 
42 #ifdef HAVE_LIBWRAP
43 extern "C" {
44 #include "tcpd.h"
45  int allow_severity;
46  int deny_severity;
47 }
48 #endif
49 
50 #include <cstring>
51 #include <cerrno>
52 
53 #include <iostream>
54 #include <sstream>
55 #include <arpa/inet.h>
56 
57 using std::cerr ;
58 using std::endl ;
59 using std::istringstream ;
60 
61 #include "TcpSocket.h"
62 #include "SocketConfig.h"
63 #include "TheBESKeys.h"
64 #include "BESDebug.h"
65 #include "BESInternalError.h"
66 #include "BESInternalFatalError.h"
67 
68 void
70 {
71  if( _listening )
72  {
73  string err( "Socket is already listening" ) ;
74  throw BESInternalError( err, __FILE__, __LINE__ ) ;
75  }
76 
77  if( _connected )
78  {
79  string err( "Socket is already connected" ) ;
80  throw BESInternalError( err, __FILE__, __LINE__ ) ;
81  }
82 
83  if( _host == "" )
84  _host = "localhost" ;
85 
86  struct protoent *pProtoEnt ;
87  struct sockaddr_in sin ;
88  struct hostent *ph ;
89  long address ;
90  if( isdigit( _host[0] ) )
91  {
92  if( ( address = inet_addr( _host.c_str() ) ) == -1 )
93  {
94  string err( "Invalid host ip address " ) ;
95  err += _host ;
96  throw BESInternalError( err, __FILE__, __LINE__ ) ;
97  }
98  sin.sin_addr.s_addr = address ;
99  sin.sin_family = AF_INET ;
100  }
101  else
102  {
103  if( ( ph = gethostbyname( _host.c_str() ) ) == NULL )
104  {
105  switch( h_errno )
106  {
107  case HOST_NOT_FOUND:
108  {
109  string err( "No such host " ) ;
110  err += _host ;
111  throw BESInternalError( err, __FILE__, __LINE__ ) ;
112  }
113  case TRY_AGAIN:
114  {
115  string err( "Host " ) ;
116  err += _host + " is busy, try again later" ;
117  throw BESInternalError( err, __FILE__, __LINE__ ) ;
118  }
119  case NO_RECOVERY:
120  {
121  string err( "DNS error for host " ) ;
122  err += _host ;
123  throw BESInternalError( err, __FILE__, __LINE__ ) ;
124  }
125  case NO_ADDRESS:
126  {
127  string err( "No IP address for host " ) ;
128  err += _host ;
129  throw BESInternalError( err, __FILE__, __LINE__ ) ;
130  }
131  default:
132  {
133  throw BESInternalError( "unknown error", __FILE__, __LINE__ ) ;
134  }
135  }
136  }
137  else
138  {
139  sin.sin_family = ph->h_addrtype ;
140  for( char **p =ph->h_addr_list; *p != NULL; p++ )
141  {
142  struct in_addr in ;
143  (void)memcpy( &in.s_addr, *p, sizeof( in.s_addr ) ) ;
144  memcpy( (char*)&sin.sin_addr, (char*)&in, sizeof( in ) ) ;
145  }
146  }
147  }
148 
149  sin.sin_port = htons( _portVal ) ;
150  pProtoEnt = getprotobyname( "tcp" ) ;
151  if( !pProtoEnt )
152  {
153  string err( "Error retreiving tcp protocol information" ) ;
154  throw BESInternalError( err, __FILE__, __LINE__ ) ;
155  }
156 
157  _connected = false;
158  int descript = socket( AF_INET, SOCK_STREAM, pProtoEnt->p_proto ) ;
159 
160  /*
161  unsigned int sockbufsize = 0 ;
162  int size = sizeof(int) ;
163  int err = getsockopt( descript, IPPROTO_TCP, TCP_MAXSEG,
164  (void *)&sockbufsize, (socklen_t*)&size) ;
165  cerr << "max size of tcp segment = " << sockbufsize << endl ;
166  */
167 
168  if( descript == -1 )
169  {
170  string err("getting socket descriptor: ");
171  const char* error_info = strerror(errno);
172  if(error_info)
173  err += (string)error_info;
174  throw BESInternalError( err, __FILE__, __LINE__ ) ;
175  } else {
176  long holder;
177  _socket = descript;
178 
179  //set socket to non-blocking mode
180  holder = fcntl(_socket, F_GETFL, NULL);
181  holder = holder | O_NONBLOCK;
182  fcntl(_socket, F_SETFL, holder);
183 
184  // we must set the send and receive buffer sizes before the connect call
185  setTcpRecvBufferSize( ) ;
186  setTcpSendBufferSize( ) ;
187 
188  int res = ::connect( descript, (struct sockaddr*)&sin, sizeof( sin ) );
189 
190 
191  if( res == -1 )
192  {
193  if(errno == EINPROGRESS) {
194 
195  fd_set write_fd ;
196  struct timeval timeout ;
197  int maxfd = _socket;
198 
199  timeout.tv_sec = 5;
200  timeout.tv_usec = 0;
201 
202  FD_ZERO( &write_fd);
203  FD_SET( _socket, &write_fd );
204 
205  if( select( maxfd+1, NULL, &write_fd, NULL, &timeout) < 0 ) {
206 
207  //reset socket to blocking mode
208  holder = fcntl(_socket, F_GETFL, NULL);
209  holder = holder & (~O_NONBLOCK);
210  fcntl(_socket, F_SETFL, holder);
211 
212  //throw error - select could not resolve socket
213  string err( "selecting sockets: " ) ;
214  const char *error_info = strerror( errno ) ;
215  if( error_info )
216  err += (string)error_info ;
217  throw BESInternalError( err, __FILE__, __LINE__ ) ;
218 
219  }
220  else
221  {
222 
223  //check socket status
224  socklen_t lon;
225  int valopt;
226  lon = sizeof(int);
227  getsockopt(_socket, SOL_SOCKET, SO_ERROR, (void*) &valopt, &lon);
228 
229  if(valopt)
230  {
231 
232  //reset socket to blocking mode
233  holder = fcntl(_socket, F_GETFL, NULL);
234  holder = holder & (~O_NONBLOCK);
235  fcntl(_socket, F_SETFL, holder);
236 
237  //throw error - did not successfully connect
238  string err("Did not successfully connect to server\n");
239  err += "Server may be down or you may be trying on the wrong port";
240  throw BESInternalError( err, __FILE__, __LINE__ ) ;
241 
242  }
243  else
244  {
245  //reset socket to blocking mode
246  holder = fcntl(_socket, F_GETFL, NULL);
247  holder = holder & (~O_NONBLOCK);
248  fcntl(_socket, F_SETFL, holder);
249 
250  //succesful connetion to server
251  _connected = true;
252  }
253  }
254  }
255  else
256  {
257 
258  //reset socket to blocking mode
259  holder = fcntl(_socket, F_GETFL, NULL);
260  holder = holder & (~O_NONBLOCK);
261  fcntl(_socket, F_SETFL, holder);
262 
263  //throw error - errno was not EINPROGRESS
264  string err("socket connect: ");
265  const char* error_info = strerror(errno);
266  if(error_info)
267  err += (string)error_info;
268  throw BESInternalError( err, __FILE__, __LINE__ ) ;
269  }
270  }
271  else
272  {
273  // The socket connect request completed immediately
274  // even that the socket was in non-blocking mode
275 
276  //reset socket to blocking mode
277  holder = fcntl(_socket, F_GETFL, NULL);
278  holder = holder & (~O_NONBLOCK);
279  fcntl(_socket, F_SETFL, holder);
280  _connected = true;
281  }
282  }
283 }
284 
285 void
287 {
288  if( _connected )
289  {
290  string err( "Socket is already connected" ) ;
291  throw BESInternalError( err, __FILE__, __LINE__ ) ;
292  }
293 
294  if( _listening )
295  {
296  string err( "Socket is already listening" ) ;
297  throw BESInternalError( err, __FILE__, __LINE__ ) ;
298  }
299 
300  int on = 1 ;
301  struct sockaddr_in server ;
302  server.sin_family = AF_INET ;
303  server.sin_addr.s_addr = INADDR_ANY ;
304  struct servent *sir = 0 ;
305  BESDEBUG( "ppt", "Checking /etc/services for port "
306  << _portVal << endl ) ;
307 
308  sir = getservbyport(htons(_portVal), 0);
309  if( sir )
310  {
311  std::ostringstream error_oss;
312  error_oss << endl <<"CONFIGURATION ERROR: The requested port (" << _portVal << ") appears in the system services list. ";
313  error_oss << "Port " << _portVal << " is assigned to the service '" << sir->s_name << (string)"'" ;
314 
315  if(sir->s_aliases[0]!=0){
316  error_oss << " which may also be known as: ";
317  for(int i=0; sir->s_aliases[i]!=0 ;i++) {
318  if(i>0)
319  error_oss << " or ";
320 
321  error_oss << sir->s_aliases[i] ;
322  }
323  }
324 
325  error_oss << endl;
326 
327  throw BESInternalError( error_oss.str(), __FILE__, __LINE__ ) ;
328  }
329 
330  server.sin_port = htons( _portVal ) ;
331  _socket = socket( AF_INET, SOCK_STREAM, 0 ) ;
332  if( _socket != -1 )
333  {
334 
335  if( setsockopt( _socket, SOL_SOCKET, SO_REUSEADDR,
336  (char*)&on, sizeof( on ) ) )
337  {
338  std::ostringstream errMsg;
339  errMsg << endl <<"ERROR: Failed to set SO_REUSEADDR on TCP socket";
340  const char* error_info = strerror( errno ) ;
341  if( error_info )
342  errMsg << ". Msg:: " <<error_info ;
343  errMsg << endl;
344  throw BESInternalError( errMsg.str(), __FILE__, __LINE__ ) ;
345  }
346 
347  BESDEBUG("besdaemon", "About to bind to port: " << _portVal << " in process: " << getpid() << endl);
348 
349  if( bind( _socket, (struct sockaddr*)&server, sizeof server) != -1 )
350  {
351  int length = sizeof( server ) ;
352 #ifdef _GETSOCKNAME_USES_SOCKLEN_T
353  if( getsockname( _socket, (struct sockaddr *)&server,
354  (socklen_t *)&length ) == -1 )
355 #else
356  if( getsockname( _socket, (struct sockaddr *)&server,
357  &length ) == -1 )
358 #endif
359  {
360  string error( "getting socket name" ) ;
361  const char* error_info = strerror( errno ) ;
362  if( error_info )
363  error += " " + (string)error_info ;
364  throw BESInternalError( error, __FILE__, __LINE__ ) ;
365  }
366 
367  // The send and receive buffer sizes must be set before the call to
368  // ::listen.
369  setTcpRecvBufferSize( ) ;
370  setTcpSendBufferSize( ) ;
371 
372  if( ::listen( _socket, 5 ) == 0 )
373  {
374  _listening = true ;
375  }
376  else
377  {
378  string error( "could not listen TCP socket" ) ;
379  const char* error_info = strerror( errno ) ;
380  if( error_info )
381  error += " " + (string)error_info ;
382  throw BESInternalError( error, __FILE__, __LINE__ ) ;
383  }
384  }
385  else
386  {
387  std::ostringstream error_msg;
388  error_msg << endl << "ERROR: Failed to bind TCP socket: "<< _portVal ;
389  const char* error_info = strerror( errno ) ;
390  if( error_info )
391  error_msg << ": " << error_info ;
392  error_msg << endl;
393  throw BESInternalError( error_msg.str(), __FILE__, __LINE__ ) ;
394  }
395  }
396  else
397  {
398  std::ostringstream error_oss;
399  error_oss << endl << "ERROR: Failed to create socket for port "<< _portVal << endl;
400  const char *error_info = strerror( errno ) ;
401  if( error_info )
402  error_oss << " " << (string)error_info ;
403  throw BESInternalError( error_oss.str() , __FILE__, __LINE__ ) ;
404  }
405 }
406 
425 void
426 TcpSocket::setTcpRecvBufferSize()
427 {
428  if( !_haveRecvBufferSize )
429  {
430  bool found = false ;
431  string setit ;
432  try
433  {
434  TheBESKeys::TheKeys()->get_value( "BES.SetSockRecvSize",
435  setit, found);
436  }
437  catch( ... )
438  {
439  // ignore any exceptions caught trying to get this key. The
440  // client also calls this function.
441  setit = "No" ;
442  }
443  if( setit == "Yes" || setit == "yes" || setit == "Yes" )
444  {
445  found = false ;
446  string sizestr ;
447  TheBESKeys::TheKeys()->get_value( "BES.SockRecvSize",
448  sizestr, found ) ;
449  istringstream sizestrm( sizestr ) ;
450  unsigned int sizenum = 0 ;
451  sizestrm >> sizenum ;
452  if( !sizenum )
453  {
454  string err = "Socket Recv Size malformed: " + sizestr ;
455  throw BESInternalFatalError( err, __FILE__, __LINE__ ) ;
456  }
457 
458  // call setsockopt
459  int err = setsockopt( _socket, SOL_SOCKET, SO_RCVBUF,
460  (char *)&sizenum, (socklen_t)sizeof(sizenum) ) ;
461  int myerrno = errno ;
462  if( err == -1 )
463  {
464  char *serr = strerror( myerrno ) ;
465  string err = "Failed to set the socket receive buffer size: " ;
466  if( serr )
467  err += serr ;
468  else
469  err += "unknow error occurred" ;
470  throw BESInternalFatalError( err, __FILE__, __LINE__ ) ;
471  }
472 
473  BESDEBUG( "ppt", "Tcp receive buffer size set to "
474  << (unsigned long)sizenum << endl ) ;
475  }
476  }
477 }
478 
497 void
498 TcpSocket::setTcpSendBufferSize()
499 {
500  bool found = false ;
501  vector<string> vals ;
502  string setit ;
503  try
504  {
505  TheBESKeys::TheKeys()->get_value( "BES.SetSockSendSize", setit, found );
506  }
507  catch( ... )
508  {
509  // ignore any exceptions caught trying to get this key. The
510  // client also calls this function.
511  setit = "No" ;
512  }
513  if( setit == "Yes" || setit == "yes" || setit == "Yes" )
514  {
515  found = false ;
516  string sizestr ;
517  try
518  {
519  TheBESKeys::TheKeys()->get_value( "BES.SockSendSize", sizestr, found ) ;
520  }
521  catch( BESError &e )
522  {
524  e.get_line() ) ;
525  }
526  istringstream sizestrm( sizestr ) ;
527  unsigned int sizenum = 0 ;
528  sizestrm >> sizenum ;
529  if( !sizenum )
530  {
531  string err = "Socket Send Size malformed: " + sizestr ;
532  throw BESInternalFatalError( err, __FILE__, __LINE__ ) ;
533  }
534 
535  // call setsockopt
536  int err = setsockopt( _socket, SOL_SOCKET, SO_SNDBUF,
537  (char *)&sizenum, (socklen_t)sizeof(sizenum) ) ;
538  int myerrno = errno ;
539  if( err == -1 )
540  {
541  char *serr = strerror( myerrno ) ;
542  string err = "Failed to set the socket send buffer size: " ;
543  if( serr )
544  err += serr ;
545  else
546  err += "unknow error occurred" ;
547  throw BESInternalFatalError( err, __FILE__, __LINE__ ) ;
548  }
549 
550  BESDEBUG( "ppt", "Tcp send buffer size set to "
551  << (unsigned long)sizenum << endl ) ;
552  }
553 }
554 
563 unsigned int
565 {
566  if( !_haveRecvBufferSize )
567  {
568  // call getsockopt and set the internal variables to the result
569  unsigned int sizenum = 0 ;
570  socklen_t sizelen = sizeof(sizenum) ;
571  int err = getsockopt( _socket, SOL_SOCKET, SO_RCVBUF,
572  (char *)&sizenum, (socklen_t *)&sizelen ) ;
573  int myerrno = errno ;
574  if( err == -1 )
575  {
576  char *serr = strerror( myerrno ) ;
577  string err = "Failed to get the socket receive buffer size: " ;
578  if( serr )
579  err += serr ;
580  else
581  err += "unknow error occurred" ;
582  throw BESInternalFatalError( err, __FILE__, __LINE__ ) ;
583  }
584 
585  BESDEBUG( "ppt", "Tcp receive buffer size is "
586  << (unsigned long)sizenum << endl ) ;
587 
588  _haveRecvBufferSize = true ;
589  _recvBufferSize = sizenum ;
590  }
591  return _recvBufferSize ;
592 }
593 
602 unsigned int
604 {
605  if( !_haveSendBufferSize )
606  {
607  // call getsockopt and set the internal variables to the result
608  unsigned int sizenum = 0 ;
609  socklen_t sizelen = sizeof(sizenum) ;
610  int err = getsockopt( _socket, SOL_SOCKET, SO_SNDBUF,
611  (char *)&sizenum, (socklen_t *)&sizelen ) ;
612  int myerrno = errno ;
613  if( err == -1 )
614  {
615  char *serr = strerror( myerrno ) ;
616  string err = "Failed to get the socket send buffer size: " ;
617  if( serr )
618  err += serr ;
619  else
620  err += "unknow error occurred" ;
621  throw BESInternalFatalError( err, __FILE__, __LINE__ ) ;
622  }
623 
624  BESDEBUG( "ppt", "Tcp send buffer size is "
625  << (unsigned long)sizenum << endl ) ;
626 
627  _haveSendBufferSize = true ;
628  _sendBufferSize = sizenum ;
629  }
630  return _sendBufferSize ;
631 }
632 
636 bool
638 {
639  bool retval = true ;
640 
641 #ifdef HAVE_LIBWRAP
642  struct request_info req ;
643  request_init( &req, RQ_DAEMON, "besdaemon", RQ_FILE,
644  getSocketDescriptor(), 0 ) ;
645  fromhost() ;
646 
647  if( STR_EQ( eval_hostname(), paranoid ) && hosts_access() )
648  {
649  retval = false ;
650  }
651 #endif
652 
653  return retval ;
654 }
655 
662 void
663 TcpSocket::dump( ostream &strm ) const
664 {
665  strm << BESIndent::LMarg << "TcpSocket::dump - ("
666  << (void *)this << ")" << endl ;
668  strm << BESIndent::LMarg << "host: " << _host << endl ;
669  strm << BESIndent::LMarg << "port: " << _portVal << endl ;
670  strm << BESIndent::LMarg << "have recv buffer size: " << _haveRecvBufferSize
671  << endl ;
672  strm << BESIndent::LMarg << "recv buffer size: " << _recvBufferSize
673  << endl ;
674  strm << BESIndent::LMarg << "have send buffer size: " << _haveSendBufferSize
675  << endl ;
676  strm << BESIndent::LMarg << "send buffer size: " << _sendBufferSize
677  << endl ;
678  Socket::dump( strm ) ;
680 }
681