Panda3D
socket_tcp_ssl.h
1 #ifndef __SOCKET_TCP_SSL_H__
2 #define __SOCKET_TCP_SSL_H__
3 
4 #include "pandabase.h"
5 #include "config_nativenet.h"
6 #include "socket_ip.h"
7 #include "numeric_types.h"
8 
9 #ifdef HAVE_OPENSSL
10 
11 #include <openssl/rsa.h> /* SSLeay stuff */
12 #include <openssl/crypto.h>
13 #include <openssl/x509.h>
14 #include <openssl/pem.h>
15 #include <openssl/ssl.h>
16 #include <openssl/err.h>
17 
18 extern EXPCL_PANDA_NATIVENET SSL_CTX *global_ssl_ctx;
19 
20 struct SSlStartup {
21  SSlStartup() {
22  const SSL_METHOD *meth;
23  SSLeay_add_ssl_algorithms();
24  // meth = SSLv23_server_method();
25  meth = SSLv23_method();
26  SSL_load_error_strings();
27  // I hate this cast, but older versions of OpenSSL need it.
28  global_ssl_ctx = SSL_CTX_new((SSL_METHOD *) meth);
29  }
30 
31  ~SSlStartup() {
32  SSL_CTX_free (global_ssl_ctx);
33  global_ssl_ctx = nullptr;
34  }
35 
36  bool isactive() { return global_ssl_ctx != nullptr; };
37 };
38 
39 /**
40  *
41  */
42 class EXPCL_PANDA_NATIVENET Socket_TCP_SSL : public Socket_IP {
43 public:
44  inline Socket_TCP_SSL(SOCKET);
45  inline Socket_TCP_SSL() : _ssl(nullptr) {}
46 
47  virtual inline ~Socket_TCP_SSL()
48  {
49  CleanSslUp();
50  }
51 
52  inline int SetNoDelay();
53  inline int SetLinger(int interval_seconds = 0);
54  inline int DontLinger();
55 
56  inline int SetSendBufferSize(int insize);
57  inline bool ActiveOpen(const Socket_Address &theaddress);
58  inline int SendData(const char *data, int size);
59  inline int RecvData(char *data, int size);
60  inline bool ErrorIs_WouldBlocking(int err);
61 
62  inline SSL *get_ssl() { return _ssl; };
63 
64  inline void DetailErrorFormat(void);
65 
66 private:
67  SSL *_ssl;
68 
69  void CleanSslUp() {
70  if (_ssl != nullptr) {
71  SSL_shutdown(_ssl);
72  SSL_free(_ssl);
73  _ssl = nullptr;
74  }
75  }
76 
77 public:
78  static TypeHandle get_class_type() {
79  return _type_handle;
80  }
81  static void init_type() {
82  Socket_IP::init_type();
83  register_type(_type_handle, "Socket_TCP_SSL",
84  Socket_IP::get_class_type());
85  }
86  virtual TypeHandle get_type() const {
87  return get_class_type();
88  }
89  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
90 
91 private:
92  static TypeHandle _type_handle;
93 };
94 
95 /**
96  *
97  */
98 inline Socket_TCP_SSL::
99 Socket_TCP_SSL(SOCKET sck) : ::Socket_IP(sck) {
100  // right know this will only work for a accepted ie a server socket ??
101  SetNonBlocking(); // maybe should be blocking?
102 
103  _ssl = SSL_new(global_ssl_ctx);
104  if (_ssl == nullptr) {
105  return;
106  }
107 
108  SSL_set_fd(_ssl, (int)GetSocket());
109 
110  SSL_accept(_ssl);
111  ERR_clear_error();
112 
113  // printf(" Ssl Accept = %d \n",err);
114 }
115 
116 /**
117  * Disable Nagle algorithm. Don't delay send to coalesce packets
118  */
119 inline int Socket_TCP_SSL::
120 SetNoDelay() {
121  int nodel = 1;
122  int ret1;
123  ret1 = setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY, (char *)&nodel, sizeof(nodel));
124 
125  if (ret1 != 0) {
126  return BASIC_ERROR;
127  }
128  return ALL_OK;
129 }
130 
131 /**
132  * will control the behavior of SO_LINGER for a TCP socket
133  */
134 int Socket_TCP_SSL::
135 SetLinger(int interval_seconds) {
136  linger ll;
137  ll.l_linger = interval_seconds;
138  ll.l_onoff = 1;
139  int ret1 = setsockopt(_socket, SOL_SOCKET, SO_LINGER, (const char *)&ll, sizeof(linger));
140  if (ret1 != 0) {
141  return BASIC_ERROR;
142  }
143  return ALL_OK;
144 }
145 
146 /**
147  * Turn off the linger flag. The socket will quickly release buffered items
148  * and free up OS resources. You may lose a stream if you use this flag and
149  * do not negotiate the close at the application layer.
150  */
151 int Socket_TCP_SSL::
152 DontLinger() {
153  linger ll;
154  ll.l_linger = 0;
155  ll.l_onoff = 0;
156  int ret1 = setsockopt(_socket, SOL_SOCKET, SO_LINGER, (const char *)&ll, sizeof(linger));
157  if (ret1 != 0) {
158  return BASIC_ERROR;
159  }
160  return ALL_OK;
161 }
162 
163 /**
164  * Just like it sounds. Sets a buffered socket recv buffer size. This
165  * function does not refuse ranges outside hard-coded OS limits
166  */
167 int Socket_TCP_SSL::
168 SetSendBufferSize(int insize) {
169  if (setsockopt(_socket, (int) SOL_SOCKET, (int) SO_SNDBUF, (char *) &insize, sizeof(int))) {
170  return BASIC_ERROR;
171  }
172  return ALL_OK;
173 }
174 
175 /**
176 * This function will try and set the socket up for active open to a specified
177 * address and port provided by the input parameter
178 */
179 bool Socket_TCP_SSL::
180 ActiveOpen(const Socket_Address &theaddress) {
181  _socket = DO_NEWTCP(theaddress.get_family());
182  if (_socket == BAD_SOCKET) {
183  return false;
184  }
185 
186  if (DO_CONNECT(_socket, &theaddress.GetAddressInfo()) != 0) {
187  return ErrorClose();
188  }
189 
190  _ssl = SSL_new(global_ssl_ctx);
191  if (_ssl == nullptr) {
192  return false;
193  }
194  SSL_set_fd(_ssl, (int)GetSocket());
195  if (SSL_connect(_ssl) == -1) {
196  return false;
197  }
198  return true;
199 
200  // return SetSslUp();
201 }
202 
203 /**
204  * Ok Lets Send the Data - if error 0 if socket closed for write or lengh is 0
205  * + bytes writen ( May be smaller than requested)
206  */
207 inline int Socket_TCP_SSL::
208 SendData(const char *data, int size) {
209  if (_ssl == nullptr) {
210  return -1;
211  }
212 
213 // ERR_clear_error();
214 
215  return SSL_write(_ssl, data, size);
216 }
217 
218 /**
219  * Read the data from the connection - if error 0 if socket closed for read or
220  * length is 0 + bytes read ( May be smaller than requested)
221  */
222 inline int Socket_TCP_SSL::
223 RecvData(char *data, int len) {
224  if (_ssl == nullptr) {
225  return -1;
226  }
227 
228  ERR_clear_error();
229 
230  return SSL_read(_ssl, data, len);
231 }
232 
233 /**
234  * Is last error a blocking error ?? True is last error was a blocking error
235  */
236 inline bool Socket_TCP_SSL::
237 ErrorIs_WouldBlocking(int err) {
238  if (_ssl == nullptr || err >= 0) {
239  nativenet_cat.warning()
240  << "Socket_TCP_SSL::ErrorIs_WouldBlocking->Called With Error number "
241  << err << " or _ssl is NULL\n";
242  return false;
243  }
244 
245  int ssl_error_code = SSL_get_error(_ssl,err);
246  bool answer = false;
247 
248  switch(ssl_error_code) {
249  case SSL_ERROR_WANT_READ:
250  case SSL_ERROR_WANT_WRITE:
251  case SSL_ERROR_WANT_CONNECT:
252 // case SSL_ERROR_WANT_ACCEPT:
253  answer = true;
254  break;
255 // hmm not sure we need this .. hmmmm
256  case SSL_ERROR_SYSCALL:
257  if(GETERROR() == LOCAL_BLOCKING_ERROR) {
258  answer = true;
259  } else {
260  DetailErrorFormat();
261 // LOGWARNING("Socket_TCP_SSL::ErrorIs_WouldBlocking-> Not A blocking Error1
262 // SSl_CODe=[%d] OS=[%d]",ssl_error_code,GETERROR());
263  }
264  break;
265  default:
266  DetailErrorFormat();
267 // LOGWARNING("Socket_TCP_SSL::ErrorIs_WouldBlocking-> Not A blocking Error2
268 // SSl_CODe=[%d] OS=[%d]",ssl_error_code,GETERROR());
269  answer = false;
270  break;
271  }
272 
273 // ERR_clear_error();
274  return answer;
275 }
276 
277 inline void Socket_TCP_SSL::
278 DetailErrorFormat(void) {
279  return; // turn on for debuging
280 
281  uint32_t l;
282  char buf[256];
283  char buf2[4096];
284  const char *file,*data;
285  int line,flags;
286  uint32_t es;
287 
288  es = CRYPTO_thread_id();
289  while ((l = ERR_get_error_line_data(&file, &line, &data, &flags)) != 0) {
290  ERR_error_string_n(l, buf, sizeof(buf));
291  BIO_snprintf(buf2, sizeof(buf2), "***%lu:%s:%s:%d:%s\n", (unsigned long) es, buf, file, line, (flags & ERR_TXT_STRING) ? data : "NoText");
292  nativenet_cat.warning()
293  << "Socket_TCP_SSL::DetailErrorFormat->[" << buf2 << "]\n";
294  }
295 }
296 
297 #endif // HAVE_OPENSSL
298 
299 #endif //__SOCKET_TCP_SSL_H__
Base functionality for a INET domain Socket This call should be the starting point for all other unix...
Definition: socket_ip.h:27
int SetNonBlocking()
this function will throw a socket into non-blocking mode
Definition: socket_ip.h:169
void register_type(TypeHandle &type_handle, const std::string &name)
This inline function is just a convenient way to call TypeRegistry::register_type(),...
Definition: register_type.I:22
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
sa_family_t get_family() const
Returns AF_INET if this is an IPv4 address, or AF_INET6 if this is an IPv6 address.
A simple place to store and manipulate tcp and port address for communication layer.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
SOCKET GetSocket()
Gets the base socket type.
Definition: socket_ip.h:237