MFEM  v4.4.0
Finite element discretization library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
socketstream.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2022, Lawrence Livermore National Security, LLC. Produced
2 // at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3 // LICENSE and NOTICE for details. LLNL-CODE-806117.
4 //
5 // This file is part of the MFEM library. For more information and source code
6 // availability visit https://mfem.org.
7 //
8 // MFEM is free software; you can redistribute it and/or modify it under the
9 // terms of the BSD-3 license. We welcome feedback and contributions, see file
10 // CONTRIBUTING.md for details.
11 
12 #ifdef _WIN32
13 // Turn off CRT deprecation warnings for strerror (VS 2013)
14 #define _CRT_SECURE_NO_WARNINGS
15 #endif
16 
17 #include "socketstream.hpp"
18 
19 #include <cstring> // memset, memcpy, strerror
20 #include <cerrno> // errno
21 #ifndef _WIN32
22 #include <netdb.h> // gethostbyname
23 #include <arpa/inet.h> // htons
24 #include <sys/types.h> // socket, setsockopt, connect, recv, send
25 #include <sys/socket.h> // socket, setsockopt, connect, recv, send
26 #include <unistd.h> // close
27 #include <netinet/in.h> // sockaddr_in
28 #define closesocket (::close)
29 #else
30 #include <winsock.h>
31 #ifdef _MSC_VER
32 typedef int ssize_t;
33 #endif
34 // Link with ws2_32.lib
35 #pragma comment(lib, "ws2_32.lib")
36 #endif
37 
38 #ifdef MFEM_USE_GNUTLS
39 #include <cstdlib> // getenv
40 #ifndef MFEM_USE_GNUTLS_X509
41 #include <gnutls/openpgp.h>
42 #endif
43 // Enable debug messages from GnuTLS_* classes
44 // #define MFEM_USE_GNUTLS_DEBUG
45 #endif
46 
47 namespace mfem
48 {
49 
50 // Helper class for handling Winsock initialization calls.
51 class WinsockWrapper
52 {
53 public:
54  WinsockWrapper()
55  {
56 #ifdef _WIN32
57  WSADATA wsaData;
58  int err_flag = WSAStartup(MAKEWORD(2,2), &wsaData);
59  if (err_flag != 0)
60  {
61  mfem::out << "Error occured during initialization of WinSock."
62  << std::endl;
63  return;
64  }
65 #endif
66  initialized = true;
67  }
68 
69 #ifdef _WIN32
70  ~WinsockWrapper() { WSACleanup(); }
71 #endif
72 
73  WinsockWrapper(const WinsockWrapper&) = delete;
74  WinsockWrapper& operator=(const WinsockWrapper&) = delete;
75 
76  bool Initialized() { return initialized; }
77 private:
78  bool initialized = false;
79 };
80 
81 // If available, Winsock is initialized when this object is constructed.
82 static WinsockWrapper wsInit_;
83 
84 int socketbuf::attach(int sd)
85 {
86  int old_sd = socket_descriptor;
87  pubsync();
88  socket_descriptor = sd;
89  setg(NULL, NULL, NULL);
90  setp(obuf, obuf + buflen);
91  return old_sd;
92 }
93 
94 int socketbuf::open(const char hostname[], int port)
95 {
96  struct sockaddr_in sa;
97  struct hostent *hp;
98 
99  if (!wsInit_.Initialized())
100  {
101  mfem_error("Attempting to open socket, but Winsock not initialized.");
102  }
103 
104  close();
105  setg(NULL, NULL, NULL);
106  setp(obuf, obuf + buflen);
107 
108  hp = gethostbyname(hostname);
109  if (hp == NULL)
110  {
111  socket_descriptor = -3;
112  return -1;
113  }
114  memset(&sa, 0, sizeof(sa));
115  memcpy((char *)&sa.sin_addr, hp->h_addr, hp->h_length);
116  sa.sin_family = hp->h_addrtype;
117  sa.sin_port = htons(port);
118  socket_descriptor = socket(hp->h_addrtype, SOCK_STREAM, 0);
119  if (socket_descriptor < 0)
120  {
121  return -1;
122  }
123 
124 #if defined __APPLE__
125  // OS X does not support the MSG_NOSIGNAL option of send().
126  // Instead we can use the SO_NOSIGPIPE socket option.
127  int on = 1;
128  if (setsockopt(socket_descriptor, SOL_SOCKET, SO_NOSIGPIPE,
129  (char *)(&on), sizeof(on)) < 0)
130  {
131  closesocket(socket_descriptor);
132  socket_descriptor = -2;
133  return -1;
134  }
135 #endif
136 
137  if (connect(socket_descriptor,
138  (const struct sockaddr *)&sa, sizeof(sa)) < 0)
139  {
140  closesocket(socket_descriptor);
141  socket_descriptor = -2;
142  return -1;
143  }
144  return 0;
145 }
146 
148 {
149  if (is_open())
150  {
151  pubsync();
152  int err_flag = closesocket(socket_descriptor);
153  socket_descriptor = -1;
154  return err_flag;
155  }
156  return 0;
157 }
158 
160 {
161  ssize_t bw, n = pptr() - pbase();
162  // mfem::out << "[socketbuf::sync n=" << n << ']' << std::endl;
163  while (n > 0)
164  {
165 #ifdef MSG_NOSIGNAL
166  bw = send(socket_descriptor, pptr() - n, n, MSG_NOSIGNAL);
167 #else
168  bw = send(socket_descriptor, pptr() - n, n, 0);
169 #endif
170  if (bw < 0)
171  {
172 #ifdef MFEM_DEBUG
173  mfem::out << "Error in send(): " << strerror(errno) << std::endl;
174 #endif
175  setp(pptr() - n, obuf + buflen);
176  pbump(n);
177  return -1;
178  }
179  n -= bw;
180  }
181  setp(obuf, obuf + buflen);
182  return 0;
183 }
184 
185 socketbuf::int_type socketbuf::underflow()
186 {
187  // assuming (gptr() < egptr()) is false
188  ssize_t br = recv(socket_descriptor, ibuf, buflen, 0);
189  // mfem::out << "[socketbuf::underflow br=" << br << ']'
190  // << std::endl;
191  if (br <= 0)
192  {
193 #ifdef MFEM_DEBUG
194  if (br < 0)
195  {
196  mfem::out << "Error in recv(): " << strerror(errno) << std::endl;
197  }
198 #endif
199  setg(NULL, NULL, NULL);
200  return traits_type::eof();
201  }
202  setg(ibuf, ibuf, ibuf + br);
203  return traits_type::to_int_type(*ibuf);
204 }
205 
206 socketbuf::int_type socketbuf::overflow(int_type c)
207 {
208  if (sync() < 0)
209  {
210  return traits_type::eof();
211  }
212  if (traits_type::eq_int_type(c, traits_type::eof()))
213  {
214  return traits_type::not_eof(c);
215  }
216  *pptr() = traits_type::to_char_type(c);
217  pbump(1);
218  return c;
219 }
220 
221 std::streamsize socketbuf::xsgetn(char_type *s__, std::streamsize n__)
222 {
223  // mfem::out << "[socketbuf::xsgetn n__=" << n__ << ']'
224  // << std::endl;
225  const std::streamsize bn = egptr() - gptr();
226  if (n__ <= bn)
227  {
228  traits_type::copy(s__, gptr(), n__);
229  gbump(n__);
230  return n__;
231  }
232  traits_type::copy(s__, gptr(), bn);
233  setg(NULL, NULL, NULL);
234  std::streamsize remain = n__ - bn;
235  char_type *end = s__ + n__;
236  ssize_t br;
237  while (remain > 0)
238  {
239  br = recv(socket_descriptor, end - remain, remain, 0);
240  if (br <= 0)
241  {
242 #ifdef MFEM_DEBUG
243  if (br < 0)
244  {
245  mfem::out << "Error in recv(): " << strerror(errno) << std::endl;
246  }
247 #endif
248  return (n__ - remain);
249  }
250  remain -= br;
251  }
252  return n__;
253 }
254 
255 std::streamsize socketbuf::xsputn(const char_type *s__, std::streamsize n__)
256 {
257  // mfem::out << "[socketbuf::xsputn n__=" << n__ << ']'
258  // << std::endl;
259  if (pptr() + n__ <= epptr())
260  {
261  traits_type::copy(pptr(), s__, n__);
262  pbump(n__);
263  return n__;
264  }
265  if (sync() < 0)
266  {
267  return 0;
268  }
269  ssize_t bw;
270  std::streamsize remain = n__;
271  const char_type *end = s__ + n__;
272  while (remain > buflen)
273  {
274 #ifdef MSG_NOSIGNAL
275  bw = send(socket_descriptor, end - remain, remain, MSG_NOSIGNAL);
276 #else
277  bw = send(socket_descriptor, end - remain, remain, 0);
278 #endif
279  if (bw < 0)
280  {
281 #ifdef MFEM_DEBUG
282  mfem::out << "Error in send(): " << strerror(errno) << std::endl;
283 #endif
284  return (n__ - remain);
285  }
286  remain -= bw;
287  }
288  if (remain > 0)
289  {
290  traits_type::copy(pptr(), end - remain, remain);
291  pbump(remain);
292  }
293  return n__;
294 }
295 
296 
297 socketserver::socketserver(int port, int backlog)
298 {
299  listen_socket = socket(PF_INET, SOCK_STREAM, 0); // tcp socket
300  if (listen_socket < 0)
301  {
302  return;
303  }
304  int on = 1;
305  if (setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR,
306  (char *)(&on), sizeof(on)) < 0)
307  {
308  closesocket(listen_socket);
309  listen_socket = -2;
310  return;
311  }
312  struct sockaddr_in sa;
313  memset(&sa, 0, sizeof(sa));
314  sa.sin_family = AF_INET;
315  sa.sin_port = htons(port);
316  sa.sin_addr.s_addr = INADDR_ANY;
317  if (bind(listen_socket, (const struct sockaddr *)&sa, sizeof(sa)))
318  {
319  closesocket(listen_socket);
320  listen_socket = -3;
321  return;
322  }
323 
324  if (listen(listen_socket, backlog) < 0)
325  {
326  closesocket(listen_socket);
327  listen_socket = -4;
328  return;
329  }
330 }
331 
333 {
334  if (!good())
335  {
336  return 0;
337  }
338  int err_flag = closesocket(listen_socket);
339  listen_socket = -1;
340  return err_flag;
341 }
342 
344 {
345  return good() ? ::accept(listen_socket, NULL, NULL) : -1;
346 }
347 
349 {
350  if (!good())
351  {
352  return -1;
353  }
354  int socketd = ::accept(listen_socket, NULL, NULL);
355  if (socketd >= 0)
356  {
357  sockstr.rdbuf()->close();
358  sockstr.rdbuf()->attach(socketd);
359  return sockstr.rdbuf()->getsocketdescriptor();
360  }
361  return socketd;
362 }
363 
364 #ifdef MFEM_USE_GNUTLS
365 
366 static void mfem_gnutls_log_func(int level, const char *str)
367 {
368  mfem::out << "GnuTLS <" << level << "> " << str << std::flush;
369 }
370 
372 {
373  status.set_result(gnutls_global_init());
374  status.print_on_error("gnutls_global_init");
376 
377  if (status.good())
378  {
379  gnutls_global_set_log_function(mfem_gnutls_log_func);
380  }
381 
382  dh_params = NULL;
383 }
384 
386 {
387  gnutls_dh_params_deinit(dh_params);
388  if (glob_init_ok) { gnutls_global_deinit(); }
389 }
390 
392 {
393  if (status.good())
394  {
395  status.set_result(gnutls_dh_params_init(&dh_params));
396  status.print_on_error("gnutls_dh_params_init");
397  if (!status.good()) { dh_params = NULL; }
398  else
399  {
400 #if GNUTLS_VERSION_NUMBER >= 0x021200
401  unsigned bits =
402  gnutls_sec_param_to_pk_bits(
403  GNUTLS_PK_DH, GNUTLS_SEC_PARAM_LEGACY);
404 #else
405  unsigned bits = 1024;
406 #endif
407  mfem::out << "Generating DH params (" << bits << " bits) ..."
408  << std::flush;
409  status.set_result(gnutls_dh_params_generate2(dh_params, bits));
410  mfem::out << " done." << std::endl;
411  status.print_on_error("gnutls_dh_params_generate2");
412  if (!status.good())
413  {
414  gnutls_dh_params_deinit(dh_params);
415  dh_params = NULL;
416  }
417  }
418  }
419 }
420 
421 static int mfem_gnutls_verify_callback(gnutls_session_t session)
422 {
423  unsigned int status;
424 #if GNUTLS_VERSION_NUMBER >= 0x030104
425  const char *hostname = (const char *) gnutls_session_get_ptr(session);
426  int ret = gnutls_certificate_verify_peers3(session, hostname, &status);
427  if (ret < 0)
428  {
429  mfem::out << "Error in gnutls_certificate_verify_peers3:"
430  << gnutls_strerror(ret) << std::endl;
431  return GNUTLS_E_CERTIFICATE_ERROR;
432  }
433 
434 #ifdef MFEM_DEBUG
435  gnutls_datum_t out;
436  gnutls_certificate_type_t type = gnutls_certificate_type_get(session);
437  ret = gnutls_certificate_verification_status_print(status, type, &out, 0);
438  if (ret < 0)
439  {
440  mfem::out << "Error in gnutls_certificate_verification_status_print:"
441  << gnutls_strerror(ret) << std::endl;
442  return GNUTLS_E_CERTIFICATE_ERROR;
443  }
444  mfem::out << out.data << std::endl;
445  gnutls_free(out.data);
446 #endif
447 #else // --> GNUTLS_VERSION_NUMBER < 0x030104
448  int ret = gnutls_certificate_verify_peers2(session, &status);
449  if (ret < 0)
450  {
451  mfem::out << "Error in gnutls_certificate_verify_peers2:"
452  << gnutls_strerror(ret) << std::endl;
453  return GNUTLS_E_CERTIFICATE_ERROR;
454  }
455 #ifdef MFEM_DEBUG
456  mfem::out << (status ?
457  "The certificate is NOT trusted." :
458  "The certificate is trusted.") << std::endl;
459 #endif
460 #endif
461 
462  return status ? GNUTLS_E_CERTIFICATE_ERROR : 0;
463 }
464 
466  GnuTLS_global_state &state, const char *pubkey_file,
467  const char *privkey_file, const char *trustedkeys_file, unsigned int flags)
468  : state(state)
469 {
470  status.set_result(state.status.get_result());
471  my_flags = status.good() ? flags : 0;
472 
473  // allocate my_cred
474  if (status.good())
475  {
476  status.set_result(
477  gnutls_certificate_allocate_credentials(&my_cred));
478  status.print_on_error("gnutls_certificate_allocate_credentials");
479  }
480  if (!status.good()) { my_cred = NULL; }
481  else
482  {
483 #ifndef MFEM_USE_GNUTLS_X509
484  status.set_result(
485  gnutls_certificate_set_openpgp_key_file(
486  my_cred, pubkey_file, privkey_file, GNUTLS_OPENPGP_FMT_RAW));
487  status.print_on_error("gnutls_certificate_set_openpgp_key_file");
488 #else
489  status.set_result(
490  gnutls_certificate_set_x509_key_file(
491  my_cred, pubkey_file, privkey_file, GNUTLS_X509_FMT_PEM));
492  // e.g. pubkey_file, privkey_file == "cert.pem", "key.pem"
493  status.print_on_error("gnutls_certificate_set_x509_key_file");
494 #endif
495  }
496 
497  if (status.good())
498  {
499  /*
500  gnutls_certificate_set_pin_function(
501  my_cred,
502  (gnutls_pin_callback_t) fn,
503  (void *) userdata);
504  */
505  }
506 
507  if (status.good())
508  {
509 #ifndef MFEM_USE_GNUTLS_X509
510  status.set_result(
511  gnutls_certificate_set_openpgp_keyring_file(
512  my_cred, trustedkeys_file, GNUTLS_OPENPGP_FMT_RAW));
513  status.print_on_error("gnutls_certificate_set_openpgp_keyring_file");
514 #else
515  int num_certs =
516  gnutls_certificate_set_x509_trust_file(
517  my_cred, trustedkeys_file, GNUTLS_X509_FMT_PEM);
518  // e.g. trustedkeys_file == "trusted-certs.pem"
519 #ifdef MFEM_USE_GNUTLS_DEBUG
520  mfem::out << "[GnuTLS_session_params::GnuTLS_session_params] "
521  "number of trusted certificates = " << num_certs << std::endl;
522 #endif
523  status.set_result(num_certs > 0 ?
524  GNUTLS_E_SUCCESS : GNUTLS_E_CERTIFICATE_ERROR);
525  status.print_on_error("gnutls_certificate_set_x509_trust_file");
526 #endif
527  }
528 
529 #if GNUTLS_VERSION_NUMBER >= 0x021000
530  if (status.good())
531  {
532  gnutls_certificate_set_verify_function(
533  my_cred, mfem_gnutls_verify_callback);
534  }
535 #endif
536 
537  if (status.good() && (flags & GNUTLS_SERVER))
538  {
539  gnutls_dh_params_t dh_params = state.get_dh_params();
540  status.set_result(state.status.get_result());
541  if (status.good())
542  {
543  gnutls_certificate_set_dh_params(my_cred, dh_params);
544  }
545  }
546 }
547 
549 {
550 #ifdef MFEM_USE_GNUTLS_DEBUG
551  mfem::out << "[GnuTLS_socketbuf::handshake]" << std::endl;
552 #endif
553 
554  // Called at the end of start_session.
555  int err_flag;
556  do
557  {
558  err_flag = gnutls_handshake(session);
559  status.set_result(err_flag);
560  if (status.good())
561  {
562 #if 0
563  mfem::out << "handshake successful, TLS version is "
564  << gnutls_protocol_get_name(
565  gnutls_protocol_get_version(session)) << std::endl;
566 #endif
567  return;
568  }
569  }
570  while (err_flag == GNUTLS_E_INTERRUPTED || err_flag == GNUTLS_E_AGAIN);
571 #ifdef MFEM_DEBUG
572  status.print_on_error("gnutls_handshake");
573 #endif
574 }
575 
576 #if (defined(MSG_NOSIGNAL) && !defined(_WIN32) && !defined(__APPLE__))
577 #define MFEM_USE_GNUTLS_PUSH_FUNCTION
578 
579 static ssize_t mfem_gnutls_push_function(
580  gnutls_transport_ptr_t fd_ptr, const void *data, size_t datasize)
581 {
582  return send((int)(long)fd_ptr, data, datasize, MSG_NOSIGNAL);
583 }
584 #endif
585 
587 {
588 #ifdef MFEM_USE_GNUTLS_DEBUG
589  mfem::out << "[GnuTLS_socketbuf::start_session]" << std::endl;
590 #endif
591 
592  // check for valid 'socket_descriptor' and inactive session
593  if (!is_open() || session_started) { return; }
594 
595  status.set_result(params.status.get_result());
596  if (status.good())
597  {
598 #if GNUTLS_VERSION_NUMBER >= 0x030102
599  status.set_result(gnutls_init(&session, params.get_flags()));
600 #else
601  status.set_result(
602  gnutls_init(&session, (gnutls_connection_end_t) params.get_flags()));
603 #endif
604  status.print_on_error("gnutls_init");
605  }
606 
607  session_started = status.good();
608  if (status.good())
609  {
610  const char *priorities;
611  // what is the right version here?
612  if (gnutls_check_version("2.12.0") != NULL)
613  {
614  // This works for version 2.12.23 (0x020c17) and above
615 #ifndef MFEM_USE_GNUTLS_X509
616  priorities = "NONE:+VERS-TLS1.2:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:"
617  "+COMP-ALL:+KX-ALL:+CTYPE-OPENPGP:+CURVE-ALL";
618 #else
619  priorities = "NONE:+VERS-TLS1.2:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:"
620  "+COMP-ALL:+KX-ALL:+CTYPE-X509:+CURVE-ALL";
621 #endif
622  }
623  else
624  {
625  // This works for version 2.8.5 (0x020805) and below
626 #ifndef MFEM_USE_GNUTLS_X509
627  priorities = "NORMAL:-CTYPE-X.509";
628 #else
629  priorities = "NORMAL:";
630 #endif
631  }
632  const char *err_ptr;
633  status.set_result(
634  gnutls_priority_set_direct(session, priorities, &err_ptr));
635  status.print_on_error("gnutls_priority_set_direct");
636  if (!status.good())
637  {
638  mfem::out << "Error ptr = \"" << err_ptr << '"' << std::endl;
639  }
640  }
641 
642  if (status.good())
643  {
644  // set session credentials
645  status.set_result(
646  gnutls_credentials_set(
647  session, GNUTLS_CRD_CERTIFICATE, my_cred));
648  status.print_on_error("gnutls_credentials_set");
649  }
650 
651  if (status.good())
652  {
653  const char *hostname = NULL; // no hostname verification
654  gnutls_session_set_ptr(session, (void*)hostname);
655  if (params.get_flags() & GNUTLS_SERVER)
656  {
657  // require clients to send certificate:
658  gnutls_certificate_server_set_request(session, GNUTLS_CERT_REQUIRE);
659  }
660 #if GNUTLS_VERSION_NUMBER >= 0x030100
661  gnutls_handshake_set_timeout(
662  session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
663 #endif
664  }
665 
666  if (status.good())
667  {
668 #if GNUTLS_VERSION_NUMBER >= 0x030109
669  gnutls_transport_set_int(session, socket_descriptor);
670 #else
671  gnutls_transport_set_ptr(session,
672  (gnutls_transport_ptr_t) socket_descriptor);
673 #endif
674 
675  handshake();
676  }
677 
678 #if GNUTLS_VERSION_NUMBER < 0x021000
679  if (status.good())
680  {
681  status.set_result(mfem_gnutls_verify_callback(session));
682  if (!status.good())
683  {
684  int err_flag;
685  do
686  {
687  // Close the connection without waiting for close reply, i.e. we
688  // use GNUTLS_SHUT_WR.
689  err_flag = gnutls_bye(session, GNUTLS_SHUT_WR);
690  }
691  while (err_flag == GNUTLS_E_AGAIN || err_flag == GNUTLS_E_INTERRUPTED);
692  }
693  }
694 #endif
695 
696 #ifdef MFEM_USE_GNUTLS_PUSH_FUNCTION
697  if (status.good())
698  {
699  gnutls_transport_set_push_function(session, mfem_gnutls_push_function);
700  }
701 #endif
702 
703  if (!status.good())
704  {
705  if (session_started) { gnutls_deinit(session); }
706  session_started = false;
708  }
709 }
710 
712 {
713 #ifdef MFEM_USE_GNUTLS_DEBUG
714  mfem::out << "[GnuTLS_socketbuf::end_session]" << std::endl;
715 #endif
716 
717  // check for valid 'socket_descriptor'
718  if (!session_started) { return; }
719 
720  if (is_open() && status.good())
721  {
722  pubsync();
723 #ifdef MFEM_USE_GNUTLS_DEBUG
724  mfem::out << "[GnuTLS_socketbuf::end_session: gnutls_bye]" << std::endl;
725 #endif
726  int err_flag;
727  do
728  {
729  // err_flag = gnutls_bye(session, GNUTLS_SHUT_RDWR);
730  err_flag = gnutls_bye(session,
731  GNUTLS_SHUT_WR); // does not wait for reply
732  status.set_result(err_flag);
733  }
734  while (err_flag == GNUTLS_E_AGAIN || err_flag == GNUTLS_E_INTERRUPTED);
735  status.print_on_error("gnutls_bye");
736  }
737 
738  gnutls_deinit(session);
739  session_started = false;
740 }
741 
743 {
744 #ifdef MFEM_USE_GNUTLS_DEBUG
745  mfem::out << "[GnuTLS_socketbuf::attach]" << std::endl;
746 #endif
747 
748  end_session();
749 
750  int old_sd = socketbuf::attach(sd);
751 
752  start_session();
753 
754  return old_sd;
755 }
756 
757 int GnuTLS_socketbuf::open(const char hostname[], int port)
758 {
759 #ifdef MFEM_USE_GNUTLS_DEBUG
760  mfem::out << "[GnuTLS_socketbuf::open]" << std::endl;
761 #endif
762 
763  int err_flag = socketbuf::open(hostname, port); // calls close()
764  if (err_flag) { return err_flag; }
765 
766  start_session();
767 
768  return status.good() ? 0 : -100;
769 }
770 
772 {
773 #ifdef MFEM_USE_GNUTLS_DEBUG
774  mfem::out << "[GnuTLS_socketbuf::close]" << std::endl;
775 #endif
776 
777  end_session();
778 
779  int err_flag = socketbuf::close();
780 
781  return status.good() ? err_flag : -100;
782 }
783 
785 {
786  ssize_t bw, n = pptr() - pbase();
787 #ifdef MFEM_USE_GNUTLS_DEBUG
788  mfem::out << "[GnuTLS_socketbuf::sync n=" << n << ']' << std::endl;
789 #endif
790  if (!session_started || !status.good()) { return -1; }
791  while (n > 0)
792  {
793  bw = gnutls_record_send(session, pptr() - n, n);
794  if (bw == GNUTLS_E_INTERRUPTED || bw == GNUTLS_E_AGAIN) { continue; }
795  if (bw < 0)
796  {
797  status.set_result((int)bw);
798 #ifdef MFEM_DEBUG
799  status.print_on_error("gnutls_record_send");
800 #endif
801  setp(pptr() - n, obuf + buflen);
802  pbump(n);
803  return -1;
804  }
805  n -= bw;
806  }
807  setp(obuf, obuf + buflen);
808  return 0;
809 }
810 
811 GnuTLS_socketbuf::int_type GnuTLS_socketbuf::underflow()
812 {
813 #ifdef MFEM_USE_GNUTLS_DEBUG
814  mfem::out << "[GnuTLS_socketbuf::underflow ...]" << std::endl;
815 #endif
816  if (!session_started || !status.good()) { return traits_type::eof(); }
817 
818  ssize_t br;
819  do
820  {
821  br = gnutls_record_recv(session, ibuf, buflen);
822  if (br == GNUTLS_E_REHANDSHAKE)
823  {
824  continue; // TODO: replace with re-handshake
825  }
826  }
827  while (br == GNUTLS_E_INTERRUPTED || br == GNUTLS_E_AGAIN);
828 #ifdef MFEM_USE_GNUTLS_DEBUG
829  mfem::out << "[GnuTLS_socketbuf::underflow br=" << br << ']' << std::endl;
830 #endif
831 
832  if (br <= 0)
833  {
834  if (br < 0)
835  {
836  status.set_result((int)br);
837 #ifdef MFEM_DEBUG
838  status.print_on_error("gnutls_record_recv");
839 #endif
840  }
841  setg(NULL, NULL, NULL);
842  return traits_type::eof();
843  }
844  setg(ibuf, ibuf, ibuf + br);
845  return traits_type::to_int_type(*ibuf);
846 }
847 
848 std::streamsize GnuTLS_socketbuf::xsgetn(char_type *s__, std::streamsize n__)
849 {
850 #ifdef MFEM_USE_GNUTLS_DEBUG
851  mfem::out << "[GnuTLS_socketbuf::xsgetn n__=" << n__ << ']' << std::endl;
852 #endif
853  if (!session_started || !status.good()) { return 0; }
854 
855  const std::streamsize bn = egptr() - gptr();
856  if (n__ <= bn)
857  {
858  traits_type::copy(s__, gptr(), n__);
859  gbump(n__);
860  return n__;
861  }
862  traits_type::copy(s__, gptr(), bn);
863  setg(NULL, NULL, NULL);
864  std::streamsize remain = n__ - bn;
865  char_type *end = s__ + n__;
866  ssize_t br;
867  while (remain > 0)
868  {
869  do
870  {
871  br = gnutls_record_recv(session, end - remain, remain);
872  if (br == GNUTLS_E_REHANDSHAKE)
873  {
874  continue; // TODO: replace with re-handshake
875  }
876  }
877  while (br == GNUTLS_E_INTERRUPTED || br == GNUTLS_E_AGAIN);
878  if (br <= 0)
879  {
880  if (br < 0)
881  {
882  status.set_result((int)br);
883 #ifdef MFEM_DEBUG
884  status.print_on_error("gnutls_record_recv");
885 #endif
886  }
887  return (n__ - remain);
888  }
889  remain -= br;
890  }
891  return n__;
892 }
893 
894 std::streamsize GnuTLS_socketbuf::xsputn(const char_type *s__,
895  std::streamsize n__)
896 {
897 #ifdef MFEM_USE_GNUTLS_DEBUG
898  mfem::out << "[GnuTLS_socketbuf::xsputn n__=" << n__ << ']' << std::endl;
899 #endif
900  if (!session_started || !status.good()) { return 0; }
901 
902  if (pptr() + n__ <= epptr())
903  {
904  traits_type::copy(pptr(), s__, n__);
905  pbump(n__);
906  return n__;
907  }
908  if (sync() < 0)
909  {
910  return 0;
911  }
912  ssize_t bw;
913  std::streamsize remain = n__;
914  const char_type *end = s__ + n__;
915  while (remain > buflen)
916  {
917  bw = gnutls_record_send(session, end - remain, remain);
918  if (bw == GNUTLS_E_INTERRUPTED || bw == GNUTLS_E_AGAIN) { continue; }
919 #ifdef MFEM_USE_GNUTLS_DEBUG
920  mfem::out << "[GnuTLS_socketbuf::xsputn bw=" << bw << ']' << std::endl;
921 #endif
922  if (bw < 0)
923  {
924  status.set_result((int)bw);
925 #ifdef MFEM_DEBUG
926  status.print_on_error("gnutls_record_send");
927 #endif
928  return (n__ - remain);
929  }
930  remain -= bw;
931  }
932  if (remain > 0)
933  {
934  traits_type::copy(pptr(), end - remain, remain);
935  pbump(remain);
936  }
937  return n__;
938 }
939 
940 
944 
945 // static method
947 {
948  if (num_glvis_sockets == 0)
949  {
951  // state->set_log_level(1000);
952  std::string home_dir(getenv("HOME"));
953  std::string client_dir = home_dir + "/.config/glvis/client/";
954 #ifndef MFEM_USE_GNUTLS_X509
955  std::string pubkey = client_dir + "pubring.gpg";
956  std::string privkey = client_dir + "secring.gpg";
957  std::string trustedkeys = client_dir + "trusted-servers.gpg";
958 #else
959  std::string pubkey = client_dir + "cert.pem";
960  std::string privkey = client_dir + "key.pem";
961  std::string trustedkeys = client_dir + "trusted-servers.pem";
962 #endif
964  *state, pubkey.c_str(), privkey.c_str(), trustedkeys.c_str(),
965  GNUTLS_CLIENT);
966  if (!params->status.good())
967  {
968  mfem::out << " public key = " << pubkey << '\n'
969  << " private key = " << privkey << '\n'
970  << " trusted keys = " << trustedkeys << std::endl;
971  mfem::out << "Error setting GLVis client parameters.\n"
972  "Use the following GLVis script to create your GLVis keys:\n"
973  " bash glvis-keygen.sh [\"Your Name\"] [\"Your Email\"]"
974  << std::endl;
975  }
976  }
978  return *params;
979 }
980 
981 // static method
983 {
984  if (num_glvis_sockets > 0)
985  {
987  if (num_glvis_sockets == 0)
988  {
989  delete params; params = NULL;
990  delete state; state = NULL;
991  }
992  }
993 }
994 
996 {
997  buf__ = new GnuTLS_socketbuf(p);
998  std::iostream::rdbuf(buf__);
999 }
1000 
1002  : std::iostream(0), glvis_client(false)
1003 {
1004  set_secure_socket(p);
1006 }
1007 
1008 #endif // MFEM_USE_GNUTLS
1009 
1010 void socketstream::set_socket(bool secure)
1011 {
1012  glvis_client = secure;
1013  if (secure)
1014  {
1015 #ifdef MFEM_USE_GNUTLS
1017 #else
1018  mfem_error("The secure option in class mfem::socketstream can only\n"
1019  "be used when GnuTLS support is enabled.");
1020 #endif
1021  }
1022  else
1023  {
1024  buf__ = new socketbuf;
1025  std::iostream::rdbuf(buf__);
1026  }
1027 }
1028 
1030 {
1031 #ifdef MFEM_USE_GNUTLS
1032  if (((GnuTLS_socketbuf*)buf__)->gnutls_good()) { clear(); }
1033  else { setstate(std::ios::failbit); }
1034 #endif
1035 }
1036 
1037 socketstream::socketstream(bool secure) : std::iostream(0)
1038 {
1039  set_socket(secure);
1040  if (secure) { check_secure_socket(); }
1041 }
1042 
1043 socketstream::socketstream(int s, bool secure) : std::iostream(0)
1044 {
1045  set_socket(secure);
1046  buf__->attach(s);
1047  if (secure) { check_secure_socket(); }
1048 }
1049 
1050 int socketstream::open(const char hostname[], int port)
1051 {
1052  int err_flag = buf__->open(hostname, port);
1053  if (err_flag)
1054  {
1055  setstate(std::ios::failbit);
1056  }
1057  else
1058  {
1059  clear();
1060  }
1061  return err_flag;
1062 }
1063 
1065 {
1066  delete buf__;
1067 #ifdef MFEM_USE_GNUTLS
1068  if (glvis_client) { remove_socket(); }
1069 #endif
1070 }
1071 
1072 } // namespace mfem
void set_result(int result)
static GnuTLS_session_params * params
virtual int attach(int sd)
virtual int_type underflow()
static int num_glvis_sockets
gnutls_certificate_credentials_t my_cred
char ibuf[buflen]
bool is_open()
Returns true if the socket is open and has a valid socket descriptor. Otherwise returns false...
GnuTLS_session_params(GnuTLS_global_state &state, const char *pubkey_file, const char *privkey_file, const char *trustedkeys_file, unsigned int flags)
socketbuf * rdbuf()
int getsocketdescriptor()
Returns the attached socket descriptor.
gnutls_certificate_credentials_t my_cred
void set_socket(bool secure)
gnutls_dh_params_t get_dh_params()
int get_result() const
gnutls_dh_params_t dh_params
virtual std::streamsize xsgetn(char_type *s__, std::streamsize n__)
bool good() const
virtual std::streamsize xsputn(const char_type *s__, std::streamsize n__)
virtual int close()
Close the current socket descriptor.
virtual int open(const char hostname[], int port)
Open a socket on the &#39;port&#39; at &#39;hostname&#39; and store the socket descriptor. Returns 0 if there is no e...
socketstream(bool secure=secure_default)
Create a socket stream without connecting to a host.
void mfem_error(const char *msg)
Function called when an error is encountered. Used by the macros MFEM_ABORT, MFEM_ASSERT, MFEM_VERIFY.
Definition: error.cpp:154
char obuf[buflen]
virtual int_type underflow()
void set_secure_socket(const GnuTLS_session_params &p)
static GnuTLS_session_params & add_socket()
virtual int_type overflow(int_type c=traits_type::eof())
virtual int attach(int sd)
Attach a new socket descriptor to the socketbuf. Returns the old socket descriptor which is NOT close...
int ssize_t
Definition: isockstream.cpp:27
virtual std::streamsize xsgetn(char_type *s__, std::streamsize n__)
void print_on_error(const char *msg) const
virtual int sync()
virtual int open(const char hostname[], int port)
Open a socket on the &#39;port&#39; at &#39;hostname&#39; and store the socket descriptor. Returns 0 if there is no e...
static const int buflen
socketserver(int port, int backlog=4)
int open(const char hostname[], int port)
Open the socket stream on &#39;port&#39; at &#39;hostname&#39;.
static void remove_socket()
virtual int close()
Close the current socket descriptor.
const GnuTLS_session_params & params
unsigned int get_flags() const
RefCoord s[3]
static GnuTLS_global_state * state
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
Definition: globals.hpp:66
virtual std::streamsize xsputn(const char_type *s__, std::streamsize n__)