bes Updated for version 3.20.13
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
50extern "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
71using namespace std;
72#define MODULE "ppt"
73#define prolog string("TcpSocket::").append(__func__).append("() - ")
74
75void 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
259void 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
384void 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
445void 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) {
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
588void 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
Base exception class for the BES with basic string message.
Definition: BESError.h:59
unsigned int get_line() const
get the line number where the exception was thrown
Definition: BESError.h:129
std::string get_file() const
get the file name where the exception was thrown
Definition: BESError.h:120
std::string get_message() const
get the error message for this exception
Definition: BESError.h:111
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