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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 
37 // Added for OS/X 10.9
38 #include <sys/select.h>
39 #include <unistd.h>
40 
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
43 #include <netdb.h>
44 #include <fcntl.h>
45 #include <netinet/tcp.h>
46 
47 #ifdef HAVE_LIBWRAP
48 extern "C" {
49 #include "tcpd.h"
50  int allow_severity;
51  int deny_severity;
52 }
53 #endif
54 
55 #include <cstring>
56 #include <cerrno>
57 
58 #include <iostream>
59 #include <sstream>
60 #include <arpa/inet.h>
61 
62 using std::cerr;
63 using std::endl;
64 using std::istringstream;
65 
66 #include "TcpSocket.h"
67 #include "SocketConfig.h"
68 #include "TheBESKeys.h"
69 #include "BESDebug.h"
70 #include "BESInternalError.h"
71 #include "BESInternalFatalError.h"
72 
74 {
75  if (_listening) {
76  string err("Socket is already listening");
77  throw BESInternalError(err, __FILE__, __LINE__);
78  }
79 
80  if (_connected) {
81  string err("Socket is already connected");
82  throw BESInternalError(err, __FILE__, __LINE__);
83  }
84 
85  if (_host == "") _host = "localhost";
86 
87  struct protoent *pProtoEnt;
88  struct sockaddr_in sin;
89  struct hostent *ph;
90  long address;
91  if (isdigit(_host[0])) {
92  if ((address = inet_addr(_host.c_str())) == -1) {
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  if ((ph = gethostbyname(_host.c_str())) == NULL) {
102  switch (h_errno) {
103  case HOST_NOT_FOUND: {
104  string err("No such host ");
105  err += _host;
106  throw BESInternalError(err, __FILE__, __LINE__);
107  }
108  case TRY_AGAIN: {
109  string err("Host ");
110  err += _host + " is busy, try again later";
111  throw BESInternalError(err, __FILE__, __LINE__);
112  }
113  case NO_RECOVERY: {
114  string err("DNS error for host ");
115  err += _host;
116  throw BESInternalError(err, __FILE__, __LINE__);
117  }
118  case NO_ADDRESS: {
119  string err("No IP address for host ");
120  err += _host;
121  throw BESInternalError(err, __FILE__, __LINE__);
122  }
123  default: {
124  throw BESInternalError("unknown error", __FILE__, __LINE__);
125  }
126  }
127  }
128  else {
129  sin.sin_family = ph->h_addrtype;
130  for (char **p = ph->h_addr_list; *p != NULL; p++) {
131  struct in_addr in;
132  (void) memcpy(&in.s_addr, *p, sizeof(in.s_addr));
133  memcpy((char*) &sin.sin_addr, (char*) &in, sizeof(in));
134  }
135  }
136  }
137 
138  sin.sin_port = htons( _portVal ) ;
139  pProtoEnt = getprotobyname("tcp");
140  if (!pProtoEnt) {
141  string err("Error retreiving tcp protocol information");
142  throw BESInternalError(err, __FILE__, __LINE__);
143  }
144 
145  _connected = false;
146  int descript = socket(AF_INET, SOCK_STREAM, pProtoEnt->p_proto);
147 
148  if (descript == -1) {
149  string err("getting socket descriptor: ");
150  const char* error_info = strerror(errno);
151  if (error_info) err += (string) error_info;
152  throw BESInternalError(err, __FILE__, __LINE__);
153  }
154  else {
155  long holder;
156  _socket = descript;
157 
158  //set socket to non-blocking mode
159  holder = fcntl(_socket, F_GETFL, NULL);
160  holder = holder | O_NONBLOCK;
161  fcntl(_socket, F_SETFL, holder);
162 
163  // we must set the send and receive buffer sizes before the connect call
164  setTcpRecvBufferSize();
165  setTcpSendBufferSize();
166 
167  int res = ::connect(descript, (struct sockaddr*) &sin, sizeof(sin));
168 
169  if (res == -1) {
170  if (errno == EINPROGRESS) {
171 
172  fd_set write_fd;
173  struct timeval timeout;
174  int maxfd = _socket;
175 
176  timeout.tv_sec = 5;
177  timeout.tv_usec = 0;
178 
179  FD_ZERO(&write_fd);
180  FD_SET(_socket, &write_fd);
181 
182  if (select(maxfd + 1, NULL, &write_fd, NULL, &timeout) < 0) {
183 
184  //reset socket to blocking mode
185  holder = fcntl(_socket, F_GETFL, NULL);
186  holder = holder & (~O_NONBLOCK);
187  fcntl(_socket, F_SETFL, holder);
188 
189  //throw error - select could not resolve socket
190  string err("selecting sockets: ");
191  const char *error_info = strerror(errno);
192  if (error_info) err += (string) error_info;
193  throw BESInternalError(err, __FILE__, __LINE__);
194 
195  }
196  else {
197 
198  //check socket status
199  socklen_t lon;
200  int valopt;
201  lon = sizeof(int);
202  getsockopt(_socket, SOL_SOCKET, SO_ERROR, (void*) &valopt, &lon);
203 
204  if (valopt) {
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 - did not successfully connect
212  string err("Did not successfully connect to server\n");
213  err += "Server may be down or you may be trying on the wrong port";
214  throw BESInternalError(err, __FILE__, __LINE__);
215 
216  }
217  else {
218  //reset socket to blocking mode
219  holder = fcntl(_socket, F_GETFL, NULL);
220  holder = holder & (~O_NONBLOCK);
221  fcntl(_socket, F_SETFL, holder);
222 
223  //succesful connetion to server
224  _connected = true;
225  }
226  }
227  }
228  else {
229 
230  //reset socket to blocking mode
231  holder = fcntl(_socket, F_GETFL, NULL);
232  holder = holder & (~O_NONBLOCK);
233  fcntl(_socket, F_SETFL, holder);
234 
235  //throw error - errno was not EINPROGRESS
236  string err("socket connect: ");
237  const char* error_info = strerror(errno);
238  if (error_info) err += (string) error_info;
239  throw BESInternalError(err, __FILE__, __LINE__);
240  }
241  }
242  else {
243  // The socket connect request completed immediately
244  // even that the socket was in non-blocking mode
245 
246  //reset socket to blocking mode
247  holder = fcntl(_socket, F_GETFL, NULL);
248  holder = holder & (~O_NONBLOCK);
249  fcntl(_socket, F_SETFL, holder);
250  _connected = true;
251  }
252  }
253 }
254 
256 {
257  if (_connected) {
258  string err("Socket is already connected");
259  throw BESInternalError(err, __FILE__, __LINE__);
260  }
261 
262  if (_listening) {
263  string err("Socket is already listening");
264  throw BESInternalError(err, __FILE__, __LINE__);
265  }
266 
267  int on = 1;
268  struct sockaddr_in server;
269  server.sin_family = AF_INET;
270  server.sin_addr.s_addr = INADDR_ANY ;
271  struct servent *sir = 0;
272  BESDEBUG( "ppt", "Checking /etc/services for port " << _portVal << endl );
273 
274  sir = getservbyport(htons(_portVal), 0);
275  if (sir) {
276  std::ostringstream error_oss;
277  error_oss << endl << "CONFIGURATION ERROR: The requested port (" << _portVal
278  << ") appears in the system services list. ";
279  error_oss << "Port " << _portVal << " is assigned to the service '" << sir->s_name << (string) "'";
280 
281  if (sir->s_aliases[0] != 0) {
282  error_oss << " which may also be known as: ";
283  for (int i = 0; sir->s_aliases[i] != 0; i++) {
284  if (i > 0) error_oss << " or ";
285 
286  error_oss << sir->s_aliases[i];
287  }
288  }
289 
290  error_oss << endl;
291 
292  throw BESInternalError(error_oss.str(), __FILE__, __LINE__);
293  }
294 
295  server.sin_port = htons( _portVal ) ;
296  _socket = socket(AF_INET, SOCK_STREAM, 0);
297  if (_socket != -1) {
298 
299  if (setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR, (char*) &on, sizeof(on))) {
300  std::ostringstream errMsg;
301  errMsg << endl << "ERROR: Failed to set SO_REUSEADDR on TCP socket";
302  const char* error_info = strerror(errno);
303  if (error_info) errMsg << ". Msg:: " << error_info;
304  errMsg << endl;
305  throw BESInternalError(errMsg.str(), __FILE__, __LINE__);
306  }
307 
308  BESDEBUG("besdaemon", "About to bind to port: " << _portVal << " in process: " << getpid() << endl);
309 
310  if (bind(_socket, (struct sockaddr*) &server, sizeof server) != -1) {
311  int length = sizeof(server);
312 #ifdef _GETSOCKNAME_USES_SOCKLEN_T
313  if (getsockname(_socket, (struct sockaddr *) &server, (socklen_t *) &length) == -1)
314 #else
315  if( getsockname( _socket, (struct sockaddr *)&server,
316  &length ) == -1 )
317 #endif
318  {
319  string error("getting socket name");
320  const char* error_info = strerror(errno);
321  if (error_info) error += " " + (string) error_info;
322  throw BESInternalError(error, __FILE__, __LINE__);
323  }
324 
325  // The send and receive buffer sizes must be set before the call to
326  // ::listen.
327  setTcpRecvBufferSize();
328  setTcpSendBufferSize();
329 
330  if (::listen(_socket, 5) == 0) {
331  _listening = true;
332  }
333  else {
334  string error("could not listen TCP socket");
335  const char* error_info = strerror(errno);
336  if (error_info) error += " " + (string) error_info;
337  throw BESInternalError(error, __FILE__, __LINE__);
338  }
339  }
340  else {
341  std::ostringstream error_msg;
342  error_msg << endl << "ERROR: Failed to bind TCP socket: " << _portVal;
343  const char* error_info = strerror(errno);
344  if (error_info) error_msg << ": " << error_info;
345  error_msg << endl;
346  throw BESInternalError(error_msg.str(), __FILE__, __LINE__);
347  }
348  }
349  else {
350  std::ostringstream error_oss;
351  error_oss << endl << "ERROR: Failed to create socket for port " << _portVal << endl;
352  const char *error_info = strerror(errno);
353  if (error_info) error_oss << " " << (string) error_info;
354  throw BESInternalError(error_oss.str(), __FILE__, __LINE__);
355  }
356 }
357 
376 void TcpSocket::setTcpRecvBufferSize()
377 {
378  if (!_haveRecvBufferSize) {
379  bool found = false;
380  string setit;
381  try {
382  TheBESKeys::TheKeys()->get_value("BES.SetSockRecvSize", setit, found);
383  }
384  catch (...) {
385  // ignore any exceptions caught trying to get this key. The
386  // client also calls this function.
387  setit = "No";
388  }
389  if (setit == "Yes" || setit == "yes" || setit == "Yes") {
390  found = false;
391  string sizestr;
392  TheBESKeys::TheKeys()->get_value("BES.SockRecvSize", sizestr, found);
393  istringstream sizestrm(sizestr);
394  unsigned int sizenum = 0;
395  sizestrm >> sizenum;
396  if (!sizenum) {
397  string err = "Socket Recv Size malformed: " + sizestr;
398  throw BESInternalFatalError(err, __FILE__, __LINE__);
399  }
400 
401  // call setsockopt
402  int err = setsockopt(_socket, SOL_SOCKET, SO_RCVBUF, (char *) &sizenum, (socklen_t) sizeof(sizenum));
403  int myerrno = errno;
404  if (err == -1) {
405  char *serr = strerror(myerrno);
406  string err = "Failed to set the socket receive buffer size: ";
407  if (serr)
408  err += serr;
409  else
410  err += "unknow error occurred";
411  throw BESInternalFatalError(err, __FILE__, __LINE__);
412  }
413 
414  BESDEBUG( "ppt", "Tcp receive buffer size set to "
415  << (unsigned long)sizenum << endl );
416  }
417  }
418  }
419 
438 void TcpSocket::setTcpSendBufferSize()
439 {
440  bool found = false;
441  vector<string> vals;
442  string setit;
443  try {
444  TheBESKeys::TheKeys()->get_value("BES.SetSockSendSize", setit, found);
445  }
446  catch (...) {
447  // ignore any exceptions caught trying to get this key. The
448  // client also calls this function.
449  setit = "No";
450  }
451  if (setit == "Yes" || setit == "yes" || setit == "Yes") {
452  found = false;
453  string sizestr;
454  try {
455  TheBESKeys::TheKeys()->get_value("BES.SockSendSize", sizestr, found);
456  }
457  catch (BESError &e) {
458  throw BESInternalFatalError(e.get_message(), e.get_file(), e.get_line());
459  }
460  istringstream sizestrm(sizestr);
461  unsigned int sizenum = 0;
462  sizestrm >> sizenum;
463  if (!sizenum) {
464  string err = "Socket Send Size malformed: " + sizestr;
465  throw BESInternalFatalError(err, __FILE__, __LINE__);
466  }
467 
468  // call setsockopt
469  int err = setsockopt(_socket, SOL_SOCKET, SO_SNDBUF, (char *) &sizenum, (socklen_t) sizeof(sizenum));
470  int myerrno = errno;
471  if (err == -1) {
472  char *serr = strerror(myerrno);
473  string err = "Failed to set the socket send buffer size: ";
474  if (serr)
475  err += serr;
476  else
477  err += "unknow error occurred";
478  throw BESInternalFatalError(err, __FILE__, __LINE__);
479  }
480 
481  BESDEBUG( "ppt", "Tcp send buffer size set to "
482  << (unsigned long)sizenum << endl );
483  }
484  }
485 
495 {
496  if (!_haveRecvBufferSize) {
497  // call getsockopt and set the internal variables to the result
498  unsigned int sizenum = 0;
499  socklen_t sizelen = sizeof(sizenum);
500  int err = getsockopt(_socket, SOL_SOCKET, SO_RCVBUF, (char *) &sizenum, (socklen_t *) &sizelen);
501  int myerrno = errno;
502  if (err == -1) {
503  char *serr = strerror(myerrno);
504  string err = "Failed to get the socket receive buffer size: ";
505  if (serr)
506  err += serr;
507  else
508  err += "unknow error occurred";
509  throw BESInternalFatalError(err, __FILE__, __LINE__);
510  }
511 
512  BESDEBUG( "ppt", "Tcp receive buffer size is "
513  << (unsigned long)sizenum << endl );
514 
515  _haveRecvBufferSize = true;
516  _recvBufferSize = sizenum;
517  }
518  return _recvBufferSize;
519 }
520 
530 {
531  if (!_haveSendBufferSize) {
532  // call getsockopt and set the internal variables to the result
533  unsigned int sizenum = 0;
534  socklen_t sizelen = sizeof(sizenum);
535  int err = getsockopt(_socket, SOL_SOCKET, SO_SNDBUF, (char *) &sizenum, (socklen_t *) &sizelen);
536  int myerrno = errno;
537  if (err == -1) {
538  char *serr = strerror(myerrno);
539  string err = "Failed to get the socket send buffer size: ";
540  if (serr)
541  err += serr;
542  else
543  err += "unknow error occurred";
544  throw BESInternalFatalError(err, __FILE__, __LINE__);
545  }
546 
547  BESDEBUG( "ppt", "Tcp send buffer size is "
548  << (unsigned long)sizenum << endl );
549 
550  _haveSendBufferSize = true;
551  _sendBufferSize = sizenum;
552  }
553  return _sendBufferSize;
554 }
555 
560 {
561  bool retval = true;
562 
563 #ifdef HAVE_LIBWRAP
564  struct request_info req;
565  request_init( &req, RQ_DAEMON, "besdaemon", RQ_FILE,
566  getSocketDescriptor(), 0 );
567  fromhost();
568 
569  if( STR_EQ( eval_hostname(), paranoid ) && hosts_access() )
570  {
571  retval = false;
572  }
573 #endif
574 
575  return retval;
576 }
577 
584 void TcpSocket::dump(ostream &strm) const
585 {
586  strm << BESIndent::LMarg << "TcpSocket::dump - (" << (void *) this << ")" << endl;
588  strm << BESIndent::LMarg << "host: " << _host << endl;
589  strm << BESIndent::LMarg << "port: " << _portVal << endl;
590  strm << BESIndent::LMarg << "have recv buffer size: " << _haveRecvBufferSize << endl;
591  strm << BESIndent::LMarg << "recv buffer size: " << _recvBufferSize << endl;
592  strm << BESIndent::LMarg << "have send buffer size: " << _haveSendBufferSize << endl;
593  strm << BESIndent::LMarg << "send buffer size: " << _sendBufferSize << endl;
594  Socket::dump(strm);
596 }
597 
virtual unsigned int getRecvBufferSize()
get the tcp receive buffer size using getsockopt
Definition: TcpSocket.cc:494
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:44
virtual void dump(std::ostream &strm) const
dumps information about this object
Definition: TcpSocket.cc:584
virtual void dump(ostream &strm) const
dumps information about this object
Definition: Socket.cc:134
static void Indent()
Definition: BESIndent.cc:38
bool _listening
Definition: Socket.h:46
virtual string get_file()
get the file name where the exception was thrown
Definition: BESError.h:102
virtual string get_message()
get the error message for this exception
Definition: BESError.h:94
Abstract exception class for the BES with basic string message.
Definition: BESError.h:51
virtual void connect()
Definition: TcpSocket.cc:73
virtual unsigned int getSendBufferSize()
get the tcp send buffer size using getsockopt
Definition: TcpSocket.cc:529
static ostream & LMarg(ostream &strm)
Definition: BESIndent.cc:73
virtual void listen()
Definition: TcpSocket.cc:255
virtual int getSocketDescriptor()
Definition: Socket.h:78
void get_value(const string &s, string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: BESKeys.cc:453
#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:45
static BESKeys * TheKeys()
Definition: TheBESKeys.cc:48
virtual bool allowConnection()
is there any wrapper code for unix sockets
Definition: TcpSocket.cc:559
virtual int get_line()
get the line number where the exception was thrown
Definition: BESError.h:110