bes  Updated for version 3.20.6
TcpSocket.cc
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 #include "TcpSocket.h"
63 #include "SocketConfig.h"
64 #include "TheBESKeys.h"
65 #include "BESDebug.h"
66 #include "BESInternalError.h"
67 #include "BESInternalFatalError.h"
68 
69 using namespace std;
70 
71 void TcpSocket::connect()
72 {
73  if (_listening) {
74  string err("Socket is already listening");
75  throw BESInternalError(err, __FILE__, __LINE__);
76  }
77 
78  if (_connected) {
79  string err("Socket is already connected");
80  throw BESInternalError(err, __FILE__, __LINE__);
81  }
82 
83  if (_host == "") _host = "localhost";
84 
85  struct protoent *pProtoEnt;
86  struct sockaddr_in sin; // = {};
87  struct hostent *ph;
88 #if 0
89  long address;
90 #endif
91  if (isdigit(_host[0])) {
92  if (0 == inet_aton(_host.c_str(), &sin.sin_addr)) {
93  throw BESInternalError(string("Invalid host ip address ") + _host, __FILE__, __LINE__);
94  }
95 #if 0
96  if ((address = inet_addr(_host.c_str())) == -1) {
97  string err("Invalid host ip address ");
98  err += _host;
99  throw BESInternalError(err, __FILE__, __LINE__);
100  }
101  sin.sin_addr.s_addr = address;
102 #endif
103  sin.sin_family = AF_INET;
104  }
105  else {
106  if ((ph = gethostbyname(_host.c_str())) == NULL) {
107  switch (h_errno) {
108  case HOST_NOT_FOUND: {
109  string err("No such host ");
110  err += _host;
111  throw BESInternalError(err, __FILE__, __LINE__);
112  }
113  case TRY_AGAIN: {
114  string err("Host ");
115  err += _host + " is busy, try again later";
116  throw BESInternalError(err, __FILE__, __LINE__);
117  }
118  case NO_RECOVERY: {
119  string err("DNS error for host ");
120  err += _host;
121  throw BESInternalError(err, __FILE__, __LINE__);
122  }
123  case NO_ADDRESS: {
124  string err("No IP address for host ");
125  err += _host;
126  throw BESInternalError(err, __FILE__, __LINE__);
127  }
128  default: {
129  throw BESInternalError("unknown error", __FILE__, __LINE__);
130  }
131  }
132  }
133  else {
134  sin.sin_family = ph->h_addrtype;
135  for (char **p = ph->h_addr_list; *p != NULL; p++) {
136  struct in_addr in;
137  (void) memcpy(&in.s_addr, *p, sizeof(in.s_addr));
138  memcpy((char*) &sin.sin_addr, (char*) &in, sizeof(in));
139  }
140  }
141  }
142 
143  sin.sin_port = htons(_portVal);
144  pProtoEnt = getprotobyname("tcp");
145  if (!pProtoEnt) {
146  string err("Error retreiving tcp protocol information");
147  throw BESInternalError(err, __FILE__, __LINE__);
148  }
149 
150  _connected = false;
151  int descript = socket(AF_INET, SOCK_STREAM, pProtoEnt->p_proto);
152 
153  if (descript == -1) {
154  throw BESInternalError(string("getting socket descriptor: ") + strerror(errno), __FILE__, __LINE__);
155  }
156  else {
157  long holder;
158  _socket = descript;
159 
160  //set socket to non-blocking mode
161  holder = fcntl(_socket, F_GETFL, NULL);
162  holder = holder | O_NONBLOCK;
163  int status = fcntl(_socket, F_SETFL, holder);
164  if (status == -1)
165  throw BESInternalError(string("Could not reset socket to blocking mode: ") + strerror(errno), __FILE__, __LINE__);
166 
167  // we must set the send and receive buffer sizes before the connect call
168  setTcpRecvBufferSize();
169  setTcpSendBufferSize();
170 
171  int res = ::connect(descript, (struct sockaddr*) &sin, sizeof(sin));
172 
173  if (res == -1) {
174  if (errno == EINPROGRESS) {
175 
176  fd_set write_fd;
177  struct timeval timeout;
178  int maxfd = _socket;
179 
180  timeout.tv_sec = 5;
181  timeout.tv_usec = 0;
182 
183  FD_ZERO(&write_fd);
184  FD_SET(_socket, &write_fd);
185 
186  if (select(maxfd + 1, NULL, &write_fd, NULL, &timeout) < 0) {
187 
188  // reset socket to blocking mode
189  holder = fcntl(_socket, F_GETFL, NULL);
190  holder = holder & (~O_NONBLOCK);
191  int status = fcntl(_socket, F_SETFL, holder);
192  if (status == -1)
193  throw BESInternalError(string("Could not reset socket to blocking mode: ") + strerror(errno), __FILE__, __LINE__);
194 
195  //throw error - select could not resolve socket
196  throw BESInternalError(string("selecting sockets: ") + strerror(errno), __FILE__, __LINE__);
197 
198  }
199  else {
200 
201  // check socket status
202  socklen_t lon;
203  int valopt;
204  lon = sizeof(int);
205  int status = getsockopt(_socket, SOL_SOCKET, SO_ERROR, (void*) &valopt, &lon);
206  if (status == -1)
207  throw BESInternalError(string("Could not check socket status: ") + strerror(errno), __FILE__, __LINE__);
208 
209  if (valopt) {
210 
211  // reset socket to blocking mode
212  holder = fcntl(_socket, F_GETFL, NULL);
213  holder = holder & (~O_NONBLOCK);
214  int status = fcntl(_socket, F_SETFL, holder);
215  if (status == -1)
216  throw BESInternalError(string("Could not reset socket to blocking mode: ") + strerror(errno), __FILE__, __LINE__);
217 
218  //throw error - did not successfully connect
219  throw BESInternalError("Did not successfully connect to server\n"
220  "Server may be down or you may be trying on the wrong port", __FILE__, __LINE__);
221 
222  }
223  else {
224  //reset socket to blocking mode
225  holder = fcntl(_socket, F_GETFL, NULL);
226  holder = holder & (~O_NONBLOCK);
227  int status = fcntl(_socket, F_SETFL, holder);
228  if (status == -1)
229  throw BESInternalError(string("Could not reset socket to blocking mode: ") + strerror(errno), __FILE__, __LINE__);
230 
231  // succesful connetion to server
232  _connected = true;
233  }
234  }
235  }
236  else {
237  // reset socket to blocking mode
238  holder = fcntl(_socket, F_GETFL, NULL);
239  holder = holder & (~O_NONBLOCK);
240  int status = fcntl(_socket, F_SETFL, holder);
241  if (status == -1)
242  throw BESInternalError(string("Could not reset socket to blocking mode: ") + strerror(errno), __FILE__, __LINE__);
243 
244  // throw error - errno was not EINPROGRESS
245  throw BESInternalError(string("socket connect: ") + strerror(errno), __FILE__, __LINE__);
246  }
247  }
248  else {
249  // The socket connect request completed immediately
250  // even that the socket was in non-blocking mode
251 
252  // reset socket to blocking mode
253  holder = fcntl(_socket, F_GETFL, NULL);
254  holder = holder & (~O_NONBLOCK);
255  int status = fcntl(_socket, F_SETFL, holder);
256  if (status == -1)
257  throw BESInternalError(string("Could not reset socket to blocking mode: ") + strerror(errno), __FILE__, __LINE__);
258 
259  _connected = true;
260  }
261  }
262 }
263 
264 void TcpSocket::listen()
265 {
266  if (_connected) {
267  string err("Socket is already connected");
268  throw BESInternalError(err, __FILE__, __LINE__);
269  }
270 
271  if (_listening) {
272  string err("Socket is already listening");
273  throw BESInternalError(err, __FILE__, __LINE__);
274  }
275 
276  struct sockaddr_in server; // = {}; // initialize server's fields to zero
277  server.sin_family = AF_INET;
278  // If the bes.conf file specified an IP address to bind with, use that. jhrg 10/14/15
279  if (!_host.empty()) {
280  int status = inet_pton(AF_INET, _host.c_str(), &server.sin_addr.s_addr);
281  if (status < 0)
282  throw BESInternalError("Error using IP address: " + _host, __FILE__, __LINE__);
283  }
284  else {
285  server.sin_addr.s_addr = INADDR_ANY;
286  }
287 
288  BESDEBUG("ppt", "Checking /etc/services for port " << _portVal << endl);
289  struct servent *sir = getservbyport(htons(_portVal), 0);
290  if (sir) {
291  std::ostringstream error_oss;
292  error_oss << endl << "CONFIGURATION ERROR: The requested port (" << _portVal
293  << ") appears in the system services list. ";
294  error_oss << "Port " << _portVal << " is assigned to the service '" << sir->s_name << (string) "'";
295 
296  if (sir->s_aliases[0] != 0) {
297  error_oss << " which may also be known as: ";
298  for (int i = 0; sir->s_aliases[i] != 0; i++) {
299  if (i > 0) error_oss << " or ";
300 
301  error_oss << sir->s_aliases[i];
302  }
303  }
304 
305  error_oss << endl;
306 
307  throw BESInternalError(error_oss.str(), __FILE__, __LINE__);
308  }
309 
310  server.sin_port = htons(_portVal);
311  _socket = socket(AF_INET, SOCK_STREAM, 0);
312  if (_socket != -1) {
313  int on = 1;
314  if (setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on))) {
315  std::ostringstream errMsg;
316  errMsg << endl << "ERROR: Failed to set SO_REUSEADDR on TCP socket";
317  const char* error_info = strerror(errno);
318  if (error_info) errMsg << ". Msg:: " << error_info;
319  errMsg << endl;
320  throw BESInternalError(errMsg.str(), __FILE__, __LINE__);
321  }
322 
323  BESDEBUG("besdaemon", "About to bind to port: " << _portVal << " in process: " << getpid() << endl);
324 
325  if (::bind(_socket, (struct sockaddr*) &server, sizeof server) != -1) {
326  int length = sizeof(server);
327 #ifdef _GETSOCKNAME_USES_SOCKLEN_T
328  if (getsockname(_socket, (struct sockaddr *) &server, (socklen_t *) &length) == -1) {
329 #else
330  if( getsockname( _socket, (struct sockaddr *)&server, &length ) == -1 ) {
331 #endif
332  string error("getting socket name");
333  const char* error_info = strerror(errno);
334  if (error_info) error += " " + (string) error_info;
335  throw BESInternalError(error, __FILE__, __LINE__);
336  }
337 
338  // The send and receive buffer sizes must be set before the call to
339  // ::listen.
340  setTcpRecvBufferSize();
341  setTcpSendBufferSize();
342 
343  if (::listen(_socket, 5) == 0) {
344  _listening = true;
345  }
346  else {
347  string error("could not listen TCP socket");
348  const char* error_info = strerror(errno);
349  if (error_info) error += " " + (string) error_info;
350  throw BESInternalError(error, __FILE__, __LINE__);
351  }
352  }
353  else {
354  std::ostringstream error_msg;
355  error_msg << endl << "ERROR: Failed to bind TCP socket: " << _portVal;
356  const char* error_info = strerror(errno);
357  if (error_info) error_msg << ": " << error_info;
358  error_msg << endl;
359  throw BESInternalError(error_msg.str(), __FILE__, __LINE__);
360  }
361  }
362  else {
363  std::ostringstream error_oss;
364  error_oss << endl << "ERROR: Failed to create socket for port " << _portVal << endl;
365  const char *error_info = strerror(errno);
366  if (error_info) error_oss << " " << (string) error_info;
367  throw BESInternalError(error_oss.str(), __FILE__, __LINE__);
368  }
369 }
370 
389 void TcpSocket::setTcpRecvBufferSize()
390 {
391  if (!_haveRecvBufferSize) {
392  bool found = false;
393  string setit;
394  try {
395  TheBESKeys::TheKeys()->get_value("BES.SetSockRecvSize", setit, found);
396  }
397  catch (...) {
398  // ignore any exceptions caught trying to get this key. The
399  // client also calls this function.
400  setit = "No";
401  }
402  if (setit == "Yes" || setit == "yes" || setit == "Yes") {
403  found = false;
404  string sizestr;
405  TheBESKeys::TheKeys()->get_value("BES.SockRecvSize", sizestr, found);
406  istringstream sizestrm(sizestr);
407  unsigned int sizenum = 0;
408  sizestrm >> sizenum;
409  if (!sizenum) {
410  string err = "Socket Recv Size malformed: " + sizestr;
411  throw BESInternalFatalError(err, __FILE__, __LINE__);
412  }
413 
414  // call setsockopt
415  int err = setsockopt(_socket, SOL_SOCKET, SO_RCVBUF, (char *) &sizenum, (socklen_t) sizeof(sizenum));
416  int myerrno = errno;
417  if (err == -1) {
418  char *serr = strerror(myerrno);
419  string err = "Failed to set the socket receive buffer size: ";
420  if (serr)
421  err += serr;
422  else
423  err += "unknow error occurred";
424  throw BESInternalFatalError(err, __FILE__, __LINE__);
425  }
426 
427  BESDEBUG("ppt", "Tcp receive buffer size set to " << (unsigned long)sizenum << endl);
428  }
429  }
430 }
431 
450 void TcpSocket::setTcpSendBufferSize()
451 {
452  bool found = false;
453  vector<string> vals;
454  string setit;
455  try {
456  TheBESKeys::TheKeys()->get_value("BES.SetSockSendSize", setit, found);
457  }
458  catch (...) {
459  // ignore any exceptions caught trying to get this key. The
460  // client also calls this function.
461  setit = "No";
462  }
463  if (setit == "Yes" || setit == "yes" || setit == "Yes") {
464  found = false;
465  string sizestr;
466  try {
467  TheBESKeys::TheKeys()->get_value("BES.SockSendSize", sizestr, found);
468  }
469  catch (BESError &e) {
470  throw BESInternalFatalError(e.get_message(), e.get_file(), e.get_line());
471  }
472  istringstream sizestrm(sizestr);
473  unsigned int sizenum = 0;
474  sizestrm >> sizenum;
475  if (!sizenum) {
476  string err = "Socket Send Size malformed: " + sizestr;
477  throw BESInternalFatalError(err, __FILE__, __LINE__);
478  }
479 
480  // call setsockopt
481  int err = setsockopt(_socket, SOL_SOCKET, SO_SNDBUF, (char *) &sizenum, (socklen_t) sizeof(sizenum));
482  int myerrno = errno;
483  if (err == -1) {
484  char *serr = strerror(myerrno);
485  string err = "Failed to set the socket send buffer size: ";
486  if (serr)
487  err += serr;
488  else
489  err += "unknow error occurred";
490  throw BESInternalFatalError(err, __FILE__, __LINE__);
491  }
492 
493  BESDEBUG("ppt", "Tcp send buffer size set to " << (unsigned long)sizenum << endl);
494  }
495 }
496 
506 {
507  if (!_haveRecvBufferSize) {
508  // call getsockopt and set the internal variables to the result
509  unsigned int sizenum = 0;
510  socklen_t sizelen = sizeof(sizenum);
511  int err = getsockopt(_socket, SOL_SOCKET, SO_RCVBUF, (char *) &sizenum, (socklen_t *) &sizelen);
512  int myerrno = errno;
513  if (err == -1) {
514  char *serr = strerror(myerrno);
515  string err = "Failed to get the socket receive buffer size: ";
516  if (serr)
517  err += serr;
518  else
519  err += "unknow error occurred";
520  throw BESInternalFatalError(err, __FILE__, __LINE__);
521  }
522 
523  BESDEBUG("ppt", "Tcp receive buffer size is " << (unsigned long)sizenum << endl);
524 
525  _haveRecvBufferSize = true;
526  _recvBufferSize = sizenum;
527  }
528  return _recvBufferSize;
529 }
530 
540 {
541  if (!_haveSendBufferSize) {
542  // call getsockopt and set the internal variables to the result
543  unsigned int sizenum = 0;
544  socklen_t sizelen = sizeof(sizenum);
545  int err = getsockopt(_socket, SOL_SOCKET, SO_SNDBUF, (char *) &sizenum, (socklen_t *) &sizelen);
546  int myerrno = errno;
547  if (err == -1) {
548  char *serr = strerror(myerrno);
549  string err = "Failed to get the socket send 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 send buffer size is " << (unsigned long)sizenum << endl);
558 
559  _haveSendBufferSize = true;
560  _sendBufferSize = sizenum;
561  }
562  return _sendBufferSize;
563 }
564 
569 {
570  bool retval = true;
571 
572 #ifdef HAVE_LIBWRAP
573  struct request_info req;
574  request_init( &req, RQ_DAEMON, "besdaemon", RQ_FILE,
575  getSocketDescriptor(), 0 );
576  fromhost();
577 
578  if( STR_EQ( eval_hostname(), paranoid ) && hosts_access() )
579  {
580  retval = false;
581  }
582 #endif
583 
584  return retval;
585 }
586 
593 void TcpSocket::dump(ostream &strm) const
594 {
595  strm << BESIndent::LMarg << "TcpSocket::dump - (" << (void *) this << ")" << endl;
596  BESIndent::Indent();
597  strm << BESIndent::LMarg << "host: " << _host << endl;
598  strm << BESIndent::LMarg << "port: " << _portVal << endl;
599  strm << BESIndent::LMarg << "have recv buffer size: " << _haveRecvBufferSize << endl;
600  strm << BESIndent::LMarg << "recv buffer size: " << _recvBufferSize << endl;
601  strm << BESIndent::LMarg << "have send buffer size: " << _haveSendBufferSize << endl;
602  strm << BESIndent::LMarg << "send buffer size: " << _sendBufferSize << endl;
603  Socket::dump(strm);
604  BESIndent::UnIndent();
605 }
606 
BESError::get_line
virtual int get_line()
get the line number where the exception was thrown
Definition: BESError.h:115
BESInternalFatalError
exception thrown if an internal error is found and is fatal to the BES
Definition: BESInternalFatalError.h:43
TcpSocket::allowConnection
virtual bool allowConnection()
is there any wrapper code for unix sockets
Definition: TcpSocket.cc:568
BESError::get_message
virtual std::string get_message()
get the error message for this exception
Definition: BESError.h:99
BESError::get_file
virtual std::string get_file()
get the file name where the exception was thrown
Definition: BESError.h:107
TcpSocket::getRecvBufferSize
virtual unsigned int getRecvBufferSize()
get the tcp receive buffer size using getsockopt
Definition: TcpSocket.cc:505
TcpSocket::dump
virtual void dump(std::ostream &strm) const
dumps information about this object
Definition: TcpSocket.cc:593
Socket::dump
virtual void dump(std::ostream &strm) const
dumps information about this object
Definition: Socket.cc:137
TheBESKeys::TheKeys
static TheBESKeys * TheKeys()
Definition: TheBESKeys.cc:62
TcpSocket::getSendBufferSize
virtual unsigned int getSendBufferSize()
get the tcp send buffer size using getsockopt
Definition: TcpSocket.cc:539
BESInternalError
exception thrown if internal error encountered
Definition: BESInternalError.h:43
TheBESKeys::get_value
void get_value(const std::string &s, std::string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: TheBESKeys.cc:272
BESError
Abstract exception class for the BES with basic string message.
Definition: BESError.h:58