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