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