bes  Updated for version 3.20.10
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.empty()) _host = "localhost";
88 
89  struct protoent *pProtoEnt;
90  struct sockaddr_in sin{};
91  struct hostent *ph;
92  if (isdigit(_host[0])) {
93  if (0 == inet_aton(_host.c_str(), &sin.sin_addr)) {
94  throw BESInternalError(string("Invalid host ip address ") + _host, __FILE__, __LINE__);
95  }
96 
97  sin.sin_family = AF_INET;
98  }
99  else {
100  // TODO Replace gethostbyname() (obsolete) with getaddrinfo() jhrg 8/11/21
101  if ((ph = gethostbyname(_host.c_str())) == nullptr) {
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  throw BESInternalError(string("getting socket descriptor: ") + strerror(errno), __FILE__, __LINE__);
150  }
151  else {
152  long holder;
153  _socket = descript;
154 
155  //set socket to non-blocking mode
156  holder = fcntl(_socket, F_GETFL, NULL);
157  holder = holder | O_NONBLOCK;
158  int status = fcntl(_socket, F_SETFL, holder);
159  if (status == -1)
160  throw BESInternalError(string("Could not reset socket to blocking mode: ") + strerror(errno), __FILE__, __LINE__);
161 
162  // we must set the send and receive buffer sizes before the connect call
163  setTcpRecvBufferSize();
164  setTcpSendBufferSize();
165 
166  int res = ::connect(descript, (struct sockaddr*) &sin, sizeof(sin));
167 
168  if (res == -1) {
169  if (errno == EINPROGRESS) {
170 
171  fd_set write_fd;
172  struct timeval timeout;
173  int maxfd = _socket;
174 
175  timeout.tv_sec = 5;
176  timeout.tv_usec = 0;
177 
178  FD_ZERO(&write_fd);
179  FD_SET(_socket, &write_fd);
180 
181  if (select(maxfd + 1, NULL, &write_fd, NULL, &timeout) < 0) {
182 
183  // reset socket to blocking mode
184  holder = fcntl(_socket, F_GETFL, NULL);
185  holder = holder & (~O_NONBLOCK);
186  int status = fcntl(_socket, F_SETFL, holder);
187  if (status == -1)
188  throw BESInternalError(string("Could not reset socket to blocking mode: ") + strerror(errno), __FILE__, __LINE__);
189 
190  //throw error - select could not resolve socket
191  throw BESInternalError(string("selecting sockets: ") + strerror(errno), __FILE__, __LINE__);
192 
193  }
194  else {
195 
196  // check socket status
197  socklen_t lon;
198  int valopt;
199  lon = sizeof(int);
200  int status = getsockopt(_socket, SOL_SOCKET, SO_ERROR, (void*) &valopt, &lon);
201  if (status == -1)
202  throw BESInternalError(string("Could not check socket status: ") + strerror(errno), __FILE__, __LINE__);
203 
204  if (valopt) {
205 
206  // reset socket to blocking mode
207  holder = fcntl(_socket, F_GETFL, NULL);
208  holder = holder & (~O_NONBLOCK);
209  int status = fcntl(_socket, F_SETFL, holder);
210  if (status == -1)
211  throw BESInternalError(string("Could not reset socket to blocking mode: ") + strerror(errno), __FILE__, __LINE__);
212 
213  //throw error - did not successfully connect
214  throw BESInternalError("Did not successfully connect to server\n"
215  "Server may be down or you may be trying on the wrong port", __FILE__, __LINE__);
216 
217  }
218  else {
219  //reset socket to blocking mode
220  holder = fcntl(_socket, F_GETFL, NULL);
221  holder = holder & (~O_NONBLOCK);
222  int status = fcntl(_socket, F_SETFL, holder);
223  if (status == -1)
224  throw BESInternalError(string("Could not reset socket to blocking mode: ") + strerror(errno), __FILE__, __LINE__);
225 
226  // succesful connetion to server
227  _connected = true;
228  }
229  }
230  }
231  else {
232  // reset socket to blocking mode
233  holder = fcntl(_socket, F_GETFL, NULL);
234  holder = holder & (~O_NONBLOCK);
235  int status = fcntl(_socket, F_SETFL, holder);
236  if (status == -1)
237  throw BESInternalError(string("Could not reset socket to blocking mode: ") + strerror(errno), __FILE__, __LINE__);
238 
239  // throw error - errno was not EINPROGRESS
240  throw BESInternalError(string("socket connect: ") + strerror(errno), __FILE__, __LINE__);
241  }
242  }
243  else {
244  // The socket connect request completed immediately
245  // even that the socket was in non-blocking mode
246 
247  // reset socket to blocking mode
248  holder = fcntl(_socket, F_GETFL, NULL);
249  holder = holder & (~O_NONBLOCK);
250  int status = fcntl(_socket, F_SETFL, holder);
251  if (status == -1)
252  throw BESInternalError(string("Could not reset socket to blocking mode: ") + strerror(errno), __FILE__, __LINE__);
253 
254  _connected = true;
255  }
256  }
257 }
258 
259 void TcpSocket::listen()
260 {
261  if (_connected) {
262  string err("Socket is already connected");
263  throw BESInternalError(err, __FILE__, __LINE__);
264  }
265 
266  if (_listening) {
267  string err("Socket is already listening");
268  throw BESInternalError(err, __FILE__, __LINE__);
269  }
270 
271  struct sockaddr_in server; // = {}; // initialize server's fields to zero
272  server.sin_family = AF_INET;
273  // If the bes.conf file specified an IP address to bind with, use that. jhrg 10/14/15
274  if (!_host.empty()) {
275  int status = inet_pton(AF_INET, _host.c_str(), &server.sin_addr.s_addr);
276  if (status < 0)
277  throw BESInternalError("Error using IP address: " + _host, __FILE__, __LINE__);
278  }
279  else {
280  server.sin_addr.s_addr = INADDR_ANY;
281  }
282 
283  BESDEBUG(MODULE, prolog << "Checking /etc/services for port " << _portVal << endl);
284  struct servent *sir = getservbyport(htons(_portVal), 0);
285  if (sir) {
286  std::ostringstream error_oss;
287  error_oss << endl << "CONFIGURATION ERROR: The requested port (" << _portVal
288  << ") appears in the system services list. ";
289  error_oss << "Port " << _portVal << " is assigned to the service '" << sir->s_name << (string) "'";
290 
291  if (sir->s_aliases[0] != 0) {
292  error_oss << " which may also be known as: ";
293  for (int i = 0; sir->s_aliases[i] != 0; i++) {
294  if (i > 0) error_oss << " or ";
295 
296  error_oss << sir->s_aliases[i];
297  }
298  }
299 
300  error_oss << endl;
301 
302  throw BESInternalError(error_oss.str(), __FILE__, __LINE__);
303  }
304 
305  server.sin_port = htons(_portVal);
306  _socket = socket(AF_INET, SOCK_STREAM, 0);
307  if (_socket != -1) {
308  int on = 1;
309  if (setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on))) {
310  std::ostringstream errMsg;
311  errMsg << endl << "ERROR: Failed to set SO_REUSEADDR on TCP socket";
312  const char* error_info = strerror(errno);
313  if (error_info) errMsg << ". Msg:: " << error_info;
314  errMsg << endl;
315  throw BESInternalError(errMsg.str(), __FILE__, __LINE__);
316  }
317 
318  BESDEBUG(MODULE, prolog << "About to bind to port: " << _portVal << " in process: " << getpid() << endl);
319 
320  if (::bind(_socket, (struct sockaddr*) &server, sizeof server) != -1) {
321  int length = sizeof(server);
322 #ifdef _GETSOCKNAME_USES_SOCKLEN_T
323  if (getsockname(_socket, (struct sockaddr *) &server, (socklen_t *) &length) == -1) {
324 #else
325  if( getsockname( _socket, (struct sockaddr *)&server, &length ) == -1 ) {
326 #endif
327  string error("getting socket name");
328  const char* error_info = strerror(errno);
329  if (error_info) error += " " + (string) error_info;
330  throw BESInternalError(error, __FILE__, __LINE__);
331  }
332 
333  // The send and receive buffer sizes must be set before the call to
334  // ::listen.
335  setTcpRecvBufferSize();
336  setTcpSendBufferSize();
337 
338  if (::listen(_socket, 5) == 0) {
339  _listening = true;
340  }
341  else {
342  string error("could not listen TCP socket");
343  const char* error_info = strerror(errno);
344  if (error_info) error += " " + (string) error_info;
345  throw BESInternalError(error, __FILE__, __LINE__);
346  }
347  }
348  else {
349  std::ostringstream error_msg;
350  error_msg << endl << "ERROR: Failed to bind TCP socket: " << _portVal;
351  const char* error_info = strerror(errno);
352  if (error_info) error_msg << ": " << error_info;
353  error_msg << endl;
354  throw BESInternalError(error_msg.str(), __FILE__, __LINE__);
355  }
356  }
357  else {
358  std::ostringstream error_oss;
359  error_oss << endl << "ERROR: Failed to create socket for port " << _portVal << endl;
360  const char *error_info = strerror(errno);
361  if (error_info) error_oss << " " << (string) error_info;
362  throw BESInternalError(error_oss.str(), __FILE__, __LINE__);
363  }
364 }
365 
384 void TcpSocket::setTcpRecvBufferSize()
385 {
386  if (!_haveRecvBufferSize) {
387  bool found = false;
388  string setit;
389  try {
390  TheBESKeys::TheKeys()->get_value("BES.SetSockRecvSize", setit, found);
391  }
392  catch (...) {
393  // ignore any exceptions caught trying to get this key. The
394  // client also calls this function.
395  setit = "No";
396  }
397  if (setit == "Yes" || setit == "yes" || setit == "Yes") {
398  found = false;
399  string sizestr;
400  TheBESKeys::TheKeys()->get_value("BES.SockRecvSize", sizestr, found);
401  istringstream sizestrm(sizestr);
402  unsigned int sizenum = 0;
403  sizestrm >> sizenum;
404  if (!sizenum) {
405  string err = "Socket Recv Size malformed: " + sizestr;
406  throw BESInternalFatalError(err, __FILE__, __LINE__);
407  }
408 
409  // call setsockopt
410  int err = setsockopt(_socket, SOL_SOCKET, SO_RCVBUF, (char *) &sizenum, (socklen_t) sizeof(sizenum));
411  int myerrno = errno;
412  if (err == -1) {
413  char *serr = strerror(myerrno);
414  string err = "Failed to set the socket receive buffer size: ";
415  if (serr)
416  err += serr;
417  else
418  err += "unknow error occurred";
419  throw BESInternalFatalError(err, __FILE__, __LINE__);
420  }
421 
422  BESDEBUG(MODULE, prolog << "Tcp receive buffer size set to " << (unsigned long)sizenum << endl);
423  }
424  }
425 }
426 
445 void TcpSocket::setTcpSendBufferSize()
446 {
447  bool found = false;
448  vector<string> vals;
449  string setit;
450  try {
451  TheBESKeys::TheKeys()->get_value("BES.SetSockSendSize", setit, found);
452  }
453  catch (...) {
454  // ignore any exceptions caught trying to get this key. The
455  // client also calls this function.
456  setit = "No";
457  }
458  if (setit == "Yes" || setit == "yes" || setit == "Yes") {
459  found = false;
460  string sizestr;
461  try {
462  TheBESKeys::TheKeys()->get_value("BES.SockSendSize", sizestr, found);
463  }
464  catch (BESError &e) {
465  throw BESInternalFatalError(e.get_message(), e.get_file(), e.get_line());
466  }
467  istringstream sizestrm(sizestr);
468  unsigned int sizenum = 0;
469  sizestrm >> sizenum;
470  if (!sizenum) {
471  string err = "Socket Send Size malformed: " + sizestr;
472  throw BESInternalFatalError(err, __FILE__, __LINE__);
473  }
474 
475  // call setsockopt
476  int err = setsockopt(_socket, SOL_SOCKET, SO_SNDBUF, (char *) &sizenum, (socklen_t) sizeof(sizenum));
477  int myerrno = errno;
478  if (err == -1) {
479  char *serr = strerror(myerrno);
480  string err = "Failed to set the socket send buffer size: ";
481  if (serr)
482  err += serr;
483  else
484  err += "unknow error occurred";
485  throw BESInternalFatalError(err, __FILE__, __LINE__);
486  }
487 
488  BESDEBUG(MODULE, prolog << "Tcp send buffer size set to " << (unsigned long)sizenum << endl);
489  }
490 }
491 
501 {
502  if (!_haveRecvBufferSize) {
503  // call getsockopt and set the internal variables to the result
504  unsigned int sizenum = 0;
505  socklen_t sizelen = sizeof(sizenum);
506  int err = getsockopt(_socket, SOL_SOCKET, SO_RCVBUF, (char *) &sizenum, (socklen_t *) &sizelen);
507  int myerrno = errno;
508  if (err == -1) {
509  char *serr = strerror(myerrno);
510  string err = "Failed to get the socket receive buffer size: ";
511  if (serr)
512  err += serr;
513  else
514  err += "unknow error occurred";
515  throw BESInternalFatalError(err, __FILE__, __LINE__);
516  }
517 
518  BESDEBUG(MODULE, prolog << "Tcp receive buffer size is " << (unsigned long)sizenum << endl);
519 
520  _haveRecvBufferSize = true;
521  _recvBufferSize = sizenum;
522  }
523  return _recvBufferSize;
524 }
525 
535 {
536  if (!_haveSendBufferSize) {
537  // call getsockopt and set the internal variables to the result
538  unsigned int sizenum = 0;
539  socklen_t sizelen = sizeof(sizenum);
540  int err = getsockopt(_socket, SOL_SOCKET, SO_SNDBUF, (char *) &sizenum, (socklen_t *) &sizelen);
541  int myerrno = errno;
542  if (err == -1) {
543  char *serr = strerror(myerrno);
544  string err = "Failed to get the socket send buffer size: ";
545  if (serr)
546  err += serr;
547  else
548  err += "unknow error occurred";
549  throw BESInternalFatalError(err, __FILE__, __LINE__);
550  }
551 
552  BESDEBUG(MODULE, prolog << "Tcp send buffer size is " << (unsigned long)sizenum << endl);
553 
554  _haveSendBufferSize = true;
555  _sendBufferSize = sizenum;
556  }
557  return _sendBufferSize;
558 }
559 
564 {
565  bool retval = true;
566 
567 #ifdef HAVE_LIBWRAP
568  struct request_info req;
569  request_init( &req, RQ_DAEMON, "besdaemon", RQ_FILE,
570  getSocketDescriptor(), 0 );
571  fromhost();
572 
573  if( STR_EQ( eval_hostname(), paranoid ) && hosts_access() )
574  {
575  retval = false;
576  }
577 #endif
578 
579  return retval;
580 }
581 
588 void TcpSocket::dump(ostream &strm) const
589 {
590  strm << BESIndent::LMarg << "TcpSocket::dump - (" << (void *) this << ")" << endl;
591  BESIndent::Indent();
592  strm << BESIndent::LMarg << "host: " << _host << endl;
593  strm << BESIndent::LMarg << "port: " << _portVal << endl;
594  strm << BESIndent::LMarg << "have recv buffer size: " << _haveRecvBufferSize << endl;
595  strm << BESIndent::LMarg << "recv buffer size: " << _recvBufferSize << endl;
596  strm << BESIndent::LMarg << "have send buffer size: " << _haveSendBufferSize << endl;
597  strm << BESIndent::LMarg << "send buffer size: " << _sendBufferSize << endl;
598  Socket::dump(strm);
599  BESIndent::UnIndent();
600 }
601 
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:500
virtual unsigned int getSendBufferSize()
get the tcp send buffer size using getsockopt
Definition: TcpSocket.cc:534
virtual bool allowConnection()
is there any wrapper code for unix sockets
Definition: TcpSocket.cc:563
virtual void dump(std::ostream &strm) const
dumps information about this object
Definition: TcpSocket.cc:588
void get_value(const std::string &s, std::string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: TheBESKeys.cc:340
static TheBESKeys * TheKeys()
Definition: TheBESKeys.cc:71