bes  Updated for version 3.20.10
PPTServer.cc
1 // PPTServer.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 <unistd.h>
36 #include <string>
37 #include <sstream>
38 #include <cstdlib>
39 
40 #include "PPTServer.h"
41 #include "ServerExitConditions.h"
42 #include "BESInternalError.h"
43 #include "BESInternalFatalError.h"
44 #include "BESSyntaxUserError.h"
45 #include "PPTProtocolNames.h"
46 #include "SocketListener.h"
47 #include "ServerHandler.h"
48 #include "Socket.h"
49 #include "TheBESKeys.h"
50 #include "BESLog.h"
51 #include "BESDebug.h"
52 
53 using std::string;
54 using std::ostringstream;
55 using std::endl;
56 using std::ostream;
57 
58 
59 #if defined HAVE_OPENSSL && defined NOTTHERE
60 #include "SSLServer.h"
61 #endif
62 #define MODULE "ppt"
63 #define prolog string("PPTServer::").append(__func__).append("() - ")
64 
65 #define PPT_SERVER_DEFAULT_TIMEOUT 1
66 
67 PPTServer::PPTServer(ServerHandler *handler, SocketListener *listener, bool isSecure) :
68  PPTConnection(PPT_SERVER_DEFAULT_TIMEOUT), _handler(handler), _listener(listener), _secure(isSecure),
69  _securePort(0), d_num_children(0)
70 {
71  if (!handler) {
72  string err("Null handler passed to PPTServer");
73  throw BESInternalError(err, __FILE__, __LINE__);
74  }
75  if (!listener) {
76  string err("Null listener passed to PPTServer");
77  throw BESInternalError(err, __FILE__, __LINE__);
78  }
79 #if !defined HAVE_OPENSSL && defined NOTTHERE
80  if( _secure )
81  {
82  string err("Server requested to be secure but OpenSSL is not built in");
83  throw BESInternalError( err, __FILE__, __LINE__ );
84  }
85 #endif
86 
87  // get the certificate and key file information
88  if (_secure) {
89  get_secure_files();
90  }
91 }
92 
93 PPTServer::~PPTServer()
94 {
95 }
96 
97 void PPTServer::get_secure_files()
98 {
99  bool found = false;
100  TheBESKeys::TheKeys()->get_value("BES.ServerCertFile", _cfile, found);
101  if (!found || _cfile.empty()) {
102  string err = "Unable to determine server certificate file.";
103  throw BESSyntaxUserError(err, __FILE__, __LINE__);
104  }
105 
106  found = false;
107  TheBESKeys::TheKeys()->get_value("BES.ServerCertAuthFile", _cafile, found);
108  if (!found || _cafile.empty()) {
109  string err = "Unable to determine server certificate authority file.";
110  throw BESSyntaxUserError(err, __FILE__, __LINE__);
111  }
112 
113  found = false;
114  TheBESKeys::TheKeys()->get_value("BES.ServerKeyFile", _kfile, found);
115  if (!found || _kfile.empty()) {
116  string err = "Unable to determine server key file.";
117  throw BESSyntaxUserError(err, __FILE__, __LINE__);
118  }
119 
120  found = false;
121  string portstr;
122  TheBESKeys::TheKeys()->get_value("BES.ServerSecurePort", portstr, found);
123  if (!found || portstr.empty()) {
124  string err = "Unable to determine secure connection port.";
125  throw BESSyntaxUserError(err, __FILE__, __LINE__);
126  }
127  _securePort = atoi(portstr.c_str());
128  if (!_securePort) {
129  string err = (string) "Unable to determine secure connection port " + "from string " + portstr;
130  throw BESSyntaxUserError(err, __FILE__, __LINE__);
131  }
132 }
133 
140 {
141  _mySock = _listener->accept();
142 
143  if (_mySock) {
144  if (_mySock->allowConnection() == true) {
145  // welcome the client
146  BESDEBUG(MODULE, prolog << "Calling welcomeClient()" << endl);
147  if (welcomeClient() != -1) {
148 
149  incr_num_children();
150  BESDEBUG(MODULE, prolog << "number of children: " << get_num_children() << endl);
151 
152  // now hand it off to the handler
153  _handler->handle(this);
154 
155  // Added this call to close - when the PPTServer class is used by
156  // a server that gets a number of connections on the same port,
157  // one per command, not closing the sockets after a command results
158  // in lots of sockets in the 'CLOSE_WAIT' status.
159  _mySock->close();
160  }
161  }
162  else {
163  BESDEBUG(MODULE, prolog << "allowConnection() is FALSE! Closing Socket. " << endl);
164  _mySock->close();
165  }
166  }
167 }
168 
169 void PPTServer::closeConnection()
170 {
171  if (_mySock) _mySock->close();
172 }
173 
174 int PPTServer::welcomeClient()
175 {
176  const unsigned int ppt_buffer_size = 64;
177  char inBuff[ppt_buffer_size + 1];
178 
179  // Doing a non blocking read in case the connection is being initiated
180  // by a non-bes client. Don't want this to block. pcw - 3/5/07
181  // int bytesRead = _mySock->receive( inBuff, ppt_buffer_size ) ;
182  //
183  // We are receiving handshaking tokens, so the buffer doesn't need to be
184  // all that big. pcw - 05/31/08
185 
186  // The non-blocking read user here was the cause of problems reported in
187  // tickets #823, #1525, #1551, #2023 and #2025. Using a blocking read
188  // fixed the problem. 2/27/14 jhrg.
189  //
190  // int bytesRead = readBufferNonBlocking(inBuff, ppt_buffer_size);
191 
192  int bytesRead = readBuffer(inBuff, ppt_buffer_size);
193 
194  BESDEBUG(MODULE, prolog << "bytesRead: " << bytesRead << endl);
195 
196  // if the read of the initial connection fails or blocks, then return
197  if (bytesRead == -1) {
198  _mySock->close();
199  return -1;
200  }
201 
202  string status(inBuff, bytesRead);
203 
204  if (status != PPT_CLIENT_TESTING_CONNECTION) {
205  /* If cannot negotiate with the client then we don't want to exit
206  * by throwing an exception, we want to return and let the caller
207  * clean up the connection
208  */
209 
210  string err = "PPT cannot negotiate, client started the connection with " + status;
211  send(err);
212  BESDEBUG(MODULE, prolog << "Sent '" << err << "' to PPT client." << endl);
213 
214  // I think it would be better to send back a previously defined
215  // constant like this... but I don't want to break client code.
216  // jhrg 2/27/14
217  // send(PPT_PROTOCOL_UNDEFINED);
218  // BESDEBUG(MODULE, prolog << "Sent " << PPT_PROTOCOL_UNDEFINED << " to PPT client." << endl);
219 
220  _mySock->close();
221  return -1;
222  }
223 
224  if (!_secure) {
225  send(PPT_SERVER_CONNECTION_OK);
226  BESDEBUG(MODULE, prolog << "Sent " << PPT_SERVER_CONNECTION_OK << " to PPT client." << endl);
227  }
228  else {
229  authenticateClient();
230  }
231 
232  return 0;
233 }
234 
235 void PPTServer::authenticateClient()
236 {
237 #if defined HAVE_OPENSSL && defined NOTTHERE
238  BESDEBUG( MODULE, prolog << "Requiring secure connection: port = " << _securePort << endl );
239  // let the client know that it needs to authenticate
240  send(PPT_SERVER_AUTHENTICATE );
241 
242  // wait for the client request for the secure port
243  // We are waiting for a ppt tocken requesting the secure port number.
244  // The buffer doesn't need to be all that big. pcw - 05/31/08
245  const unsigned int ppt_buffer_size = 64;
246  // char *inBuff = new char[ppt_buffer_size]; jhrg 3/5/14
247  char inBuff[ppt_buffer_size];
248  int bytesRead = _mySock->receive( inBuff, ppt_buffer_size );
249  string portRequest( inBuff, bytesRead );
250  // delete [] inBuff; jhrg 3/5/14
251  if( portRequest != PPT_CLIENT_REQUEST_AUTHPORT )
252  throw BESInternalError( string("Secure connection ... expecting request for port client requested ") + portRequest, __FILE__, __LINE__ );
253 
254  // send the secure port number back to the client
255  ostringstream portResponse;
256  portResponse << _securePort << PPT_COMPLETE_DATA_TRANSMISSION;
257  send( portResponse.str() );
258 
259  // create a secure server object and authenticate
260  SSLServer server( _securePort, _cfile, _cafile, _kfile );
261  server.initConnection();
262  server.closeConnection();
263 
264  // if it authenticates, good, if not, an exception is thrown, no need to
265  // do anything else here.
266 #else
267  throw BESInternalError("Authentication requested for this server but OpenSSL is not built into the server", __FILE__, __LINE__);
268 #endif
269 }
270 
277 void PPTServer::dump(ostream &strm) const
278 {
279  strm << BESIndent::LMarg << "PPTServer::dump - (" << (void *) this << ")" << endl;
280  BESIndent::Indent();
281  if (_handler) {
282  strm << BESIndent::LMarg << "server handler:" << endl;
283  BESIndent::Indent();
284  _handler->dump(strm);
285  BESIndent::UnIndent();
286  }
287  else {
288  strm << BESIndent::LMarg << "server handler: null" << endl;
289  }
290  if (_listener) {
291  strm << BESIndent::LMarg << "listener:" << endl;
292  BESIndent::Indent();
293  _listener->dump(strm);
294  BESIndent::UnIndent();
295  }
296  else {
297  strm << BESIndent::LMarg << "listener: null" << endl;
298  }
299  strm << BESIndent::LMarg << "secure? " << _secure << endl;
300  if (_secure) {
301  BESIndent::Indent();
302  strm << BESIndent::LMarg << "cert file: " << _cfile << endl;
303  strm << BESIndent::LMarg << "cert authority file: " << _cafile << endl;
304  strm << BESIndent::LMarg << "key file: " << _kfile << endl;
305  strm << BESIndent::LMarg << "secure port: " << _securePort << endl;
306  BESIndent::UnIndent();
307  }
308  PPTConnection::dump(strm);
309  BESIndent::UnIndent();
310 }
311 
exception thrown if internal error encountered
error thrown if there is a user syntax error in the request or any other user error
virtual int readBuffer(char *inBuff, const unsigned int buff_size)
read a buffer of data from the socket
virtual void send(const std::string &buffer)
sends the buffer to the socket
virtual void dump(std::ostream &strm) const
dumps information about this object
virtual void initConnection()
Definition: PPTServer.cc:139
virtual void dump(std::ostream &strm) const
dumps information about this object
Definition: PPTServer.cc:277
virtual void dump(std::ostream &strm) const =0
dump the contents of this object to the specified ostream
virtual void dump(std::ostream &strm) const
dumps information about this object
virtual Socket * accept()
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