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 
56 using std::cerr ;
57 using std::endl ;
58 using std::istringstream ;
59 
60 #include "TcpSocket.h"
61 #include "SocketConfig.h"
62 #include "TheBESKeys.h"
63 #include "BESDebug.h"
64 #include "BESInternalError.h"
65 #include "BESInternalFatalError.h"
66 
67 void
69 {
70  if( _listening )
71  {
72  string err( "Socket is already listening" ) ;
73  throw BESInternalError( err, __FILE__, __LINE__ ) ;
74  }
75 
76  if( _connected )
77  {
78  string err( "Socket is already connected" ) ;
79  throw BESInternalError( err, __FILE__, __LINE__ ) ;
80  }
81 
82  if( _host == "" )
83  _host = "localhost" ;
84 
85  struct protoent *pProtoEnt ;
86  struct sockaddr_in sin ;
87  struct hostent *ph ;
88  long address ;
89  if( isdigit( _host[0] ) )
90  {
91  if( ( address = inet_addr( _host.c_str() ) ) == -1 )
92  {
93  string err( "Invalid host ip address " ) ;
94  err += _host ;
95  throw BESInternalError( err, __FILE__, __LINE__ ) ;
96  }
97  sin.sin_addr.s_addr = address ;
98  sin.sin_family = AF_INET ;
99  }
100  else
101  {
102  if( ( ph = gethostbyname( _host.c_str() ) ) == NULL )
103  {
104  switch( h_errno )
105  {
106  case HOST_NOT_FOUND:
107  {
108  string err( "No such host " ) ;
109  err += _host ;
110  throw BESInternalError( err, __FILE__, __LINE__ ) ;
111  }
112  case TRY_AGAIN:
113  {
114  string err( "Host " ) ;
115  err += _host + " is busy, try again later" ;
116  throw BESInternalError( err, __FILE__, __LINE__ ) ;
117  }
118  case NO_RECOVERY:
119  {
120  string err( "DNS error for host " ) ;
121  err += _host ;
122  throw BESInternalError( err, __FILE__, __LINE__ ) ;
123  }
124  case NO_ADDRESS:
125  {
126  string err( "No IP address for host " ) ;
127  err += _host ;
128  throw BESInternalError( err, __FILE__, __LINE__ ) ;
129  }
130  default:
131  {
132  throw BESInternalError( "unknown error", __FILE__, __LINE__ ) ;
133  }
134  }
135  }
136  else
137  {
138  sin.sin_family = ph->h_addrtype ;
139  for( char **p =ph->h_addr_list; *p != NULL; p++ )
140  {
141  struct in_addr in ;
142  (void)memcpy( &in.s_addr, *p, sizeof( in.s_addr ) ) ;
143  memcpy( (char*)&sin.sin_addr, (char*)&in, sizeof( in ) ) ;
144  }
145  }
146  }
147 
148  sin.sin_port = htons( _portVal ) ;
149  pProtoEnt = getprotobyname( "tcp" ) ;
150  if( !pProtoEnt )
151  {
152  string err( "Error retreiving tcp protocol information" ) ;
153  throw BESInternalError( err, __FILE__, __LINE__ ) ;
154  }
155 
156  _connected = false;
157  int descript = socket( AF_INET, SOCK_STREAM, pProtoEnt->p_proto ) ;
158 
159  /*
160  unsigned int sockbufsize = 0 ;
161  int size = sizeof(int) ;
162  int err = getsockopt( descript, IPPROTO_TCP, TCP_MAXSEG,
163  (void *)&sockbufsize, (socklen_t*)&size) ;
164  cerr << "max size of tcp segment = " << sockbufsize << endl ;
165  */
166 
167  if( descript == -1 )
168  {
169  string err("getting socket descriptor: ");
170  const char* error_info = strerror(errno);
171  if(error_info)
172  err += (string)error_info;
173  throw BESInternalError( err, __FILE__, __LINE__ ) ;
174  } else {
175  long holder;
176  _socket = descript;
177 
178  //set socket to non-blocking mode
179  holder = fcntl(_socket, F_GETFL, NULL);
180  holder = holder | O_NONBLOCK;
181  fcntl(_socket, F_SETFL, holder);
182 
183  // we must set the send and receive buffer sizes before the connect call
184  setTcpRecvBufferSize( ) ;
185  setTcpSendBufferSize( ) ;
186 
187  int res = ::connect( descript, (struct sockaddr*)&sin, sizeof( sin ) );
188 
189 
190  if( res == -1 )
191  {
192  if(errno == EINPROGRESS) {
193 
194  fd_set write_fd ;
195  struct timeval timeout ;
196  int maxfd = _socket;
197 
198  timeout.tv_sec = 5;
199  timeout.tv_usec = 0;
200 
201  FD_ZERO( &write_fd);
202  FD_SET( _socket, &write_fd );
203 
204  if( select( maxfd+1, NULL, &write_fd, NULL, &timeout) < 0 ) {
205 
206  //reset socket to blocking mode
207  holder = fcntl(_socket, F_GETFL, NULL);
208  holder = holder & (~O_NONBLOCK);
209  fcntl(_socket, F_SETFL, holder);
210 
211  //throw error - select could not resolve socket
212  string err( "selecting sockets: " ) ;
213  const char *error_info = strerror( errno ) ;
214  if( error_info )
215  err += (string)error_info ;
216  throw BESInternalError( err, __FILE__, __LINE__ ) ;
217 
218  }
219  else
220  {
221 
222  //check socket status
223  socklen_t lon;
224  int valopt;
225  lon = sizeof(int);
226  getsockopt(_socket, SOL_SOCKET, SO_ERROR, (void*) &valopt, &lon);
227 
228  if(valopt)
229  {
230 
231  //reset socket to blocking mode
232  holder = fcntl(_socket, F_GETFL, NULL);
233  holder = holder & (~O_NONBLOCK);
234  fcntl(_socket, F_SETFL, holder);
235 
236  //throw error - did not successfully connect
237  string err("Did not successfully connect to server\n");
238  err += "Server may be down or you may be trying on the wrong port";
239  throw BESInternalError( err, __FILE__, __LINE__ ) ;
240 
241  }
242  else
243  {
244  //reset socket to blocking mode
245  holder = fcntl(_socket, F_GETFL, NULL);
246  holder = holder & (~O_NONBLOCK);
247  fcntl(_socket, F_SETFL, holder);
248 
249  //succesful connetion to server
250  _connected = true;
251  }
252  }
253  }
254  else
255  {
256 
257  //reset socket to blocking mode
258  holder = fcntl(_socket, F_GETFL, NULL);
259  holder = holder & (~O_NONBLOCK);
260  fcntl(_socket, F_SETFL, holder);
261 
262  //throw error - errno was not EINPROGRESS
263  string err("socket connect: ");
264  const char* error_info = strerror(errno);
265  if(error_info)
266  err += (string)error_info;
267  throw BESInternalError( err, __FILE__, __LINE__ ) ;
268  }
269  }
270  else
271  {
272  // The socket connect request completed immediately
273  // even that the socket was in non-blocking mode
274 
275  //reset socket to blocking mode
276  holder = fcntl(_socket, F_GETFL, NULL);
277  holder = holder & (~O_NONBLOCK);
278  fcntl(_socket, F_SETFL, holder);
279  _connected = true;
280  }
281  }
282 }
283 
284 void
286 {
287  if( _connected )
288  {
289  string err( "Socket is already connected" ) ;
290  throw BESInternalError( err, __FILE__, __LINE__ ) ;
291  }
292 
293  if( _listening )
294  {
295  string err( "Socket is already listening" ) ;
296  throw BESInternalError( err, __FILE__, __LINE__ ) ;
297  }
298 
299  int on = 1 ;
300  struct sockaddr_in server ;
301  server.sin_family = AF_INET ;
302  server.sin_addr.s_addr = INADDR_ANY ;
303  struct servent *sir = 0 ;
304  sir = getservbyport( _portVal, "tcp" ) ;
305  if( sir )
306  {
307  string error = sir->s_name + (string)" is using my socket" ;
308  throw BESInternalError( error, __FILE__, __LINE__ ) ;
309  }
310  server.sin_port = htons( _portVal ) ;
311  _socket = socket( AF_INET, SOCK_STREAM, 0 ) ;
312  if( _socket != -1 )
313  {
314  if( setsockopt( _socket, SOL_SOCKET, SO_REUSEADDR,
315  (char*)&on, sizeof( on ) ) )
316  {
317  string error( "could not set SO_REUSEADDR on TCP socket" ) ;
318  const char* error_info = strerror( errno ) ;
319  if( error_info )
320  error += " " + (string)error_info ;
321  throw BESInternalError( error, __FILE__, __LINE__ ) ;
322  }
323 
324  if( bind( _socket, (struct sockaddr*)&server, sizeof server) != -1 )
325  {
326  int length = sizeof( server ) ;
327 #ifdef _GETSOCKNAME_USES_SOCKLEN_T
328  if( getsockname( _socket, (struct sockaddr *)&server,
329  (socklen_t *)&length ) == -1 )
330 #else
331  if( getsockname( _socket, (struct sockaddr *)&server,
332  &length ) == -1 )
333 #endif
334  {
335  string error( "getting socket name" ) ;
336  const char* error_info = strerror( errno ) ;
337  if( error_info )
338  error += " " + (string)error_info ;
339  throw BESInternalError( error, __FILE__, __LINE__ ) ;
340  }
341 
342  // The send and receive buffer sizes must be set before the call to
343  // ::listen.
344  setTcpRecvBufferSize( ) ;
345  setTcpSendBufferSize( ) ;
346 
347  if( ::listen( _socket, 5 ) == 0 )
348  {
349  _listening = true ;
350  }
351  else
352  {
353  string error( "could not listen TCP socket" ) ;
354  const char* error_info = strerror( errno ) ;
355  if( error_info )
356  error += " " + (string)error_info ;
357  throw BESInternalError( error, __FILE__, __LINE__ ) ;
358  }
359  }
360  else
361  {
362  string error( "could not bind TCP socket" ) ;
363  const char* error_info = strerror( errno ) ;
364  if( error_info )
365  error += " " + (string)error_info ;
366  throw BESInternalError( error, __FILE__, __LINE__ ) ;
367  }
368  }
369  else
370  {
371  string error( "could not create socket" ) ;
372  const char *error_info = strerror( errno ) ;
373  if( error_info )
374  error += " " + (string)error_info ;
375  throw BESInternalError( error, __FILE__, __LINE__ ) ;
376  }
377 }
378 
397 void
398 TcpSocket::setTcpRecvBufferSize()
399 {
400  if( !_haveRecvBufferSize )
401  {
402  bool found = false ;
403  string setit ;
404  try
405  {
406  TheBESKeys::TheKeys()->get_value( "BES.SetSockRecvSize",
407  setit, found);
408  }
409  catch( ... )
410  {
411  // ignore any exceptions caught trying to get this key. The
412  // client also calls this function.
413  setit = "No" ;
414  }
415  if( setit == "Yes" || setit == "yes" || setit == "Yes" )
416  {
417  found = false ;
418  string sizestr ;
419  TheBESKeys::TheKeys()->get_value( "BES.SockRecvSize",
420  sizestr, found ) ;
421  istringstream sizestrm( sizestr ) ;
422  unsigned int sizenum = 0 ;
423  sizestrm >> sizenum ;
424  if( !sizenum )
425  {
426  string err = "Socket Recv Size malformed: " + sizestr ;
427  throw BESInternalFatalError( err, __FILE__, __LINE__ ) ;
428  }
429 
430  // call setsockopt
431  int err = setsockopt( _socket, SOL_SOCKET, SO_RCVBUF,
432  (char *)&sizenum, (socklen_t)sizeof(sizenum) ) ;
433  int myerrno = errno ;
434  if( err == -1 )
435  {
436  char *serr = strerror( myerrno ) ;
437  string err = "Failed to set the socket receive buffer size: " ;
438  if( serr )
439  err += serr ;
440  else
441  err += "unknow error occurred" ;
442  throw BESInternalFatalError( err, __FILE__, __LINE__ ) ;
443  }
444 
445  BESDEBUG( "ppt", "Tcp receive buffer size set to "
446  << (unsigned long)sizenum << endl ) ;
447  }
448  }
449 }
450 
469 void
470 TcpSocket::setTcpSendBufferSize()
471 {
472  bool found = false ;
473  vector<string> vals ;
474  string setit ;
475  try
476  {
477  TheBESKeys::TheKeys()->get_value( "BES.SetSockSendSize", setit, found );
478  }
479  catch( ... )
480  {
481  // ignore any exceptions caught trying to get this key. The
482  // client also calls this function.
483  setit = "No" ;
484  }
485  if( setit == "Yes" || setit == "yes" || setit == "Yes" )
486  {
487  found = false ;
488  string sizestr ;
489  try
490  {
491  TheBESKeys::TheKeys()->get_value( "BES.SockSendSize", sizestr, found ) ;
492  }
493  catch( BESError &e )
494  {
496  e.get_line() ) ;
497  }
498  istringstream sizestrm( sizestr ) ;
499  unsigned int sizenum = 0 ;
500  sizestrm >> sizenum ;
501  if( !sizenum )
502  {
503  string err = "Socket Send Size malformed: " + sizestr ;
504  throw BESInternalFatalError( err, __FILE__, __LINE__ ) ;
505  }
506 
507  // call setsockopt
508  int err = setsockopt( _socket, SOL_SOCKET, SO_SNDBUF,
509  (char *)&sizenum, (socklen_t)sizeof(sizenum) ) ;
510  int myerrno = errno ;
511  if( err == -1 )
512  {
513  char *serr = strerror( myerrno ) ;
514  string err = "Failed to set the socket send buffer size: " ;
515  if( serr )
516  err += serr ;
517  else
518  err += "unknow error occurred" ;
519  throw BESInternalFatalError( err, __FILE__, __LINE__ ) ;
520  }
521 
522  BESDEBUG( "ppt", "Tcp send buffer size set to "
523  << (unsigned long)sizenum << endl ) ;
524  }
525 }
526 
535 unsigned int
537 {
538  if( !_haveRecvBufferSize )
539  {
540  // call getsockopt and set the internal variables to the result
541  unsigned int sizenum = 0 ;
542  socklen_t sizelen = sizeof(sizenum) ;
543  int err = getsockopt( _socket, SOL_SOCKET, SO_RCVBUF,
544  (char *)&sizenum, (socklen_t *)&sizelen ) ;
545  int myerrno = errno ;
546  if( err == -1 )
547  {
548  char *serr = strerror( myerrno ) ;
549  string err = "Failed to get the socket receive buffer size: " ;
550  if( serr )
551  err += serr ;
552  else
553  err += "unknow error occurred" ;
554  throw BESInternalFatalError( err, __FILE__, __LINE__ ) ;
555  }
556 
557  BESDEBUG( "ppt", "Tcp receive buffer size is "
558  << (unsigned long)sizenum << endl ) ;
559 
560  _haveRecvBufferSize = true ;
561  _recvBufferSize = sizenum ;
562  }
563  return _recvBufferSize ;
564 }
565 
574 unsigned int
576 {
577  if( !_haveSendBufferSize )
578  {
579  // call getsockopt and set the internal variables to the result
580  unsigned int sizenum = 0 ;
581  socklen_t sizelen = sizeof(sizenum) ;
582  int err = getsockopt( _socket, SOL_SOCKET, SO_SNDBUF,
583  (char *)&sizenum, (socklen_t *)&sizelen ) ;
584  int myerrno = errno ;
585  if( err == -1 )
586  {
587  char *serr = strerror( myerrno ) ;
588  string err = "Failed to get the socket send buffer size: " ;
589  if( serr )
590  err += serr ;
591  else
592  err += "unknow error occurred" ;
593  throw BESInternalFatalError( err, __FILE__, __LINE__ ) ;
594  }
595 
596  BESDEBUG( "ppt", "Tcp send buffer size is "
597  << (unsigned long)sizenum << endl ) ;
598 
599  _haveSendBufferSize = true ;
600  _sendBufferSize = sizenum ;
601  }
602  return _sendBufferSize ;
603 }
604 
608 bool
610 {
611  bool retval = true ;
612 
613 #ifdef HAVE_LIBWRAP
614  struct request_info req ;
615  request_init( &req, RQ_DAEMON, "besdaemon", RQ_FILE,
616  getSocketDescriptor(), 0 ) ;
617  fromhost() ;
618 
619  if( STR_EQ( eval_hostname(), paranoid ) && hosts_access() )
620  {
621  retval = false ;
622  }
623 #endif
624 
625  return retval ;
626 }
627 
634 void
635 TcpSocket::dump( ostream &strm ) const
636 {
637  strm << BESIndent::LMarg << "TcpSocket::dump - ("
638  << (void *)this << ")" << endl ;
640  strm << BESIndent::LMarg << "host: " << _host << endl ;
641  strm << BESIndent::LMarg << "port: " << _portVal << endl ;
642  strm << BESIndent::LMarg << "have recv buffer size: " << _haveRecvBufferSize
643  << endl ;
644  strm << BESIndent::LMarg << "recv buffer size: " << _recvBufferSize
645  << endl ;
646  strm << BESIndent::LMarg << "have send buffer size: " << _haveSendBufferSize
647  << endl ;
648  strm << BESIndent::LMarg << "send buffer size: " << _sendBufferSize
649  << endl ;
650  Socket::dump( strm ) ;
652 }
653 
virtual unsigned int getRecvBufferSize()
get the tcp receive buffer size using getsockopt
Definition: TcpSocket.cc:536
exception thrown if an internal error is found and is fatal to the BES
exception thrown if inernal error encountered
int _socket
Definition: Socket.h:47
static void Indent()
Definition: BESIndent.cc:38
bool _listening
Definition: Socket.h:49
virtual string get_file()
get the file name where the exception was thrown
Definition: BESError.h:96
virtual string get_message()
get the error message for this exception
Definition: BESError.h:91
Abstract exception class for the BES with basic string message.
Definition: BESError.h:51
virtual void connect()
Definition: TcpSocket.cc:68
virtual unsigned int getSendBufferSize()
get the tcp send buffer size using getsockopt
Definition: TcpSocket.cc:575
static ostream & LMarg(ostream &strm)
Definition: BESIndent.cc:73
virtual void listen()
Definition: TcpSocket.cc:285
virtual void dump(ostream &strm) const
dumps information about this object
Definition: TcpSocket.cc:635
virtual int getSocketDescriptor()
Definition: Socket.h:70
void get_value(const string &s, string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: BESKeys.cc:466
virtual void dump(ostream &strm) const
dumps information about this object
Definition: Socket.cc:136
#define BESDEBUG(x, y)
macro used to send debug information to the debug stream
Definition: BESDebug.h:64
static void UnIndent()
Definition: BESIndent.cc:44
bool _connected
Definition: Socket.h:48
static BESKeys * TheKeys()
Definition: TheBESKeys.cc:46
virtual bool allowConnection()
is there any wrapper code for unix sockets
Definition: TcpSocket.cc:609
virtual int get_line()
get the line number where the exception was thrown
Definition: BESError.h:101