MFEM  v4.6.0
Finite element discretization library
socketstream.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2023, 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 // Link with ws2_32.lib
34 #pragma comment(lib, "ws2_32.lib")
35 #endif
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 occurred 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 status_str;
436  gnutls_certificate_type_t type = gnutls_certificate_type_get(session);
437  ret = gnutls_certificate_verification_status_print(
438  status, type, &status_str, 0);
439  if (ret < 0)
440  {
441  mfem::out << "Error in gnutls_certificate_verification_status_print:"
442  << gnutls_strerror(ret) << std::endl;
443  return GNUTLS_E_CERTIFICATE_ERROR;
444  }
445  mfem::out << status_str.data << std::endl;
446  gnutls_free(status_str.data);
447 #endif
448 #else // --> GNUTLS_VERSION_NUMBER < 0x030104
449  int ret = gnutls_certificate_verify_peers2(session, &status);
450  if (ret < 0)
451  {
452  mfem::out << "Error in gnutls_certificate_verify_peers2:"
453  << gnutls_strerror(ret) << std::endl;
454  return GNUTLS_E_CERTIFICATE_ERROR;
455  }
456 #ifdef MFEM_DEBUG
457  mfem::out << (status ?
458  "The certificate is NOT trusted." :
459  "The certificate is trusted.") << std::endl;
460 #endif
461 #endif
462 
463  return status ? GNUTLS_E_CERTIFICATE_ERROR : 0;
464 }
465 
467  GnuTLS_global_state &state, const char *pubkey_file,
468  const char *privkey_file, const char *trustedkeys_file, unsigned int flags)
469  : state(state)
470 {
472  my_flags = status.good() ? flags : 0;
473 
474  // allocate my_cred
475  if (status.good())
476  {
478  gnutls_certificate_allocate_credentials(&my_cred));
479  status.print_on_error("gnutls_certificate_allocate_credentials");
480  }
481  if (!status.good()) { my_cred = NULL; }
482  else
483  {
484 #ifndef MFEM_USE_GNUTLS_X509
486  gnutls_certificate_set_openpgp_key_file(
487  my_cred, pubkey_file, privkey_file, GNUTLS_OPENPGP_FMT_RAW));
488  status.print_on_error("gnutls_certificate_set_openpgp_key_file");
489 #else
491  gnutls_certificate_set_x509_key_file(
492  my_cred, pubkey_file, privkey_file, GNUTLS_X509_FMT_PEM));
493  // e.g. pubkey_file, privkey_file == "cert.pem", "key.pem"
494  status.print_on_error("gnutls_certificate_set_x509_key_file");
495 #endif
496  }
497 
498  if (status.good())
499  {
500  /*
501  gnutls_certificate_set_pin_function(
502  my_cred,
503  (gnutls_pin_callback_t) fn,
504  (void *) userdata);
505  */
506  }
507 
508  if (status.good())
509  {
510 #ifndef MFEM_USE_GNUTLS_X509
512  gnutls_certificate_set_openpgp_keyring_file(
513  my_cred, trustedkeys_file, GNUTLS_OPENPGP_FMT_RAW));
514  status.print_on_error("gnutls_certificate_set_openpgp_keyring_file");
515 #else
516  int num_certs =
517  gnutls_certificate_set_x509_trust_file(
518  my_cred, trustedkeys_file, GNUTLS_X509_FMT_PEM);
519  // e.g. trustedkeys_file == "trusted-certs.pem"
520 #ifdef MFEM_USE_GNUTLS_DEBUG
521  mfem::out << "[GnuTLS_session_params::GnuTLS_session_params] "
522  "number of trusted certificates = " << num_certs << std::endl;
523 #endif
524  status.set_result(num_certs > 0 ?
525  GNUTLS_E_SUCCESS : GNUTLS_E_CERTIFICATE_ERROR);
526  status.print_on_error("gnutls_certificate_set_x509_trust_file");
527 #endif
528  }
529 
530 #if GNUTLS_VERSION_NUMBER >= 0x021000
531  if (status.good())
532  {
533  gnutls_certificate_set_verify_function(
534  my_cred, mfem_gnutls_verify_callback);
535  }
536 #endif
537 
538  if (status.good() && (flags & GNUTLS_SERVER))
539  {
540  gnutls_dh_params_t dh_params = state.get_dh_params();
542  if (status.good())
543  {
544  gnutls_certificate_set_dh_params(my_cred, dh_params);
545  }
546  }
547 }
548 
550 {
551 #ifdef MFEM_USE_GNUTLS_DEBUG
552  mfem::out << "[GnuTLS_socketbuf::handshake]" << std::endl;
553 #endif
554 
555  // Called at the end of start_session.
556  int err_flag;
557  do
558  {
559  err_flag = gnutls_handshake(session);
560  status.set_result(err_flag);
561  if (status.good())
562  {
563 #if 0
564  mfem::out << "handshake successful, TLS version is "
565  << gnutls_protocol_get_name(
566  gnutls_protocol_get_version(session)) << std::endl;
567 #endif
568  return;
569  }
570  }
571  while (err_flag == GNUTLS_E_INTERRUPTED || err_flag == GNUTLS_E_AGAIN);
572 #ifdef MFEM_DEBUG
573  status.print_on_error("gnutls_handshake");
574 #endif
575 }
576 
577 #if (defined(MSG_NOSIGNAL) && !defined(_WIN32) && !defined(__APPLE__))
578 #define MFEM_USE_GNUTLS_PUSH_FUNCTION
579 
580 static ssize_t mfem_gnutls_push_function(
581  gnutls_transport_ptr_t fd_ptr, const void *data, size_t datasize)
582 {
583  return send((int)(long)fd_ptr, data, datasize, MSG_NOSIGNAL);
584 }
585 #endif
586 
588 {
589 #ifdef MFEM_USE_GNUTLS_DEBUG
590  mfem::out << "[GnuTLS_socketbuf::start_session]" << std::endl;
591 #endif
592 
593  // check for valid 'socket_descriptor' and inactive session
594  if (!is_open() || session_started) { return; }
595 
597  if (status.good())
598  {
599 #if GNUTLS_VERSION_NUMBER >= 0x030102
600  status.set_result(gnutls_init(&session, params.get_flags()));
601 #else
603  gnutls_init(&session, (gnutls_connection_end_t) params.get_flags()));
604 #endif
605  status.print_on_error("gnutls_init");
606  }
607 
609  if (status.good())
610  {
611  const char *priorities;
612  // what is the right version here?
613  if (gnutls_check_version("2.12.0") != NULL)
614  {
615  // This works for version 2.12.23 (0x020c17) and above
616 #ifndef MFEM_USE_GNUTLS_X509
617  priorities = "NONE:+VERS-TLS1.2:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:"
618  "+COMP-ALL:+KX-ALL:+CTYPE-OPENPGP:+CURVE-ALL";
619 #else
620  priorities = "NONE:+VERS-TLS1.2:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:"
621  "+COMP-ALL:+KX-ALL:+CTYPE-X509:+CURVE-ALL";
622 #endif
623  }
624  else
625  {
626  // This works for version 2.8.5 (0x020805) and below
627 #ifndef MFEM_USE_GNUTLS_X509
628  priorities = "NORMAL:-CTYPE-X.509";
629 #else
630  priorities = "NORMAL:";
631 #endif
632  }
633  const char *err_ptr;
635  gnutls_priority_set_direct(session, priorities, &err_ptr));
636  status.print_on_error("gnutls_priority_set_direct");
637  if (!status.good())
638  {
639  mfem::out << "Error ptr = \"" << err_ptr << '"' << std::endl;
640  }
641  }
642 
643  if (status.good())
644  {
645  // set session credentials
647  gnutls_credentials_set(
648  session, GNUTLS_CRD_CERTIFICATE, my_cred));
649  status.print_on_error("gnutls_credentials_set");
650  }
651 
652  if (status.good())
653  {
654  const char *hostname = NULL; // no hostname verification
655  gnutls_session_set_ptr(session, (void*)hostname);
656  if (params.get_flags() & GNUTLS_SERVER)
657  {
658  // require clients to send certificate:
659  gnutls_certificate_server_set_request(session, GNUTLS_CERT_REQUIRE);
660  }
661 #if GNUTLS_VERSION_NUMBER >= 0x030100
662  gnutls_handshake_set_timeout(
663  session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
664 #endif
665  }
666 
667  if (status.good())
668  {
669 #if GNUTLS_VERSION_NUMBER >= 0x030109
670  gnutls_transport_set_int(session, socket_descriptor);
671 #else
672  gnutls_transport_set_ptr(session,
673  (gnutls_transport_ptr_t) socket_descriptor);
674 #endif
675 
676  handshake();
677  }
678 
679 #if GNUTLS_VERSION_NUMBER < 0x021000
680  if (status.good())
681  {
682  status.set_result(mfem_gnutls_verify_callback(session));
683  if (!status.good())
684  {
685  int err_flag;
686  do
687  {
688  // Close the connection without waiting for close reply, i.e. we
689  // use GNUTLS_SHUT_WR.
690  err_flag = gnutls_bye(session, GNUTLS_SHUT_WR);
691  }
692  while (err_flag == GNUTLS_E_AGAIN || err_flag == GNUTLS_E_INTERRUPTED);
693  }
694  }
695 #endif
696 
697 #ifdef MFEM_USE_GNUTLS_PUSH_FUNCTION
698  if (status.good())
699  {
700  gnutls_transport_set_push_function(session, mfem_gnutls_push_function);
701  }
702 #endif
703 
704  if (!status.good())
705  {
706  if (session_started) { gnutls_deinit(session); }
707  session_started = false;
709  }
710 }
711 
713 {
714 #ifdef MFEM_USE_GNUTLS_DEBUG
715  mfem::out << "[GnuTLS_socketbuf::end_session]" << std::endl;
716 #endif
717 
718  // check for valid 'socket_descriptor'
719  if (!session_started) { return; }
720 
721  if (is_open() && status.good())
722  {
723  pubsync();
724 #ifdef MFEM_USE_GNUTLS_DEBUG
725  mfem::out << "[GnuTLS_socketbuf::end_session: gnutls_bye]" << std::endl;
726 #endif
727  int err_flag;
728  do
729  {
730  // err_flag = gnutls_bye(session, GNUTLS_SHUT_RDWR);
731  err_flag = gnutls_bye(session,
732  GNUTLS_SHUT_WR); // does not wait for reply
733  status.set_result(err_flag);
734  }
735  while (err_flag == GNUTLS_E_AGAIN || err_flag == GNUTLS_E_INTERRUPTED);
736  status.print_on_error("gnutls_bye");
737  }
738 
739  gnutls_deinit(session);
740  session_started = false;
741 }
742 
744 {
745 #ifdef MFEM_USE_GNUTLS_DEBUG
746  mfem::out << "[GnuTLS_socketbuf::attach]" << std::endl;
747 #endif
748 
749  end_session();
750 
751  int old_sd = socketbuf::attach(sd);
752 
753  start_session();
754 
755  return old_sd;
756 }
757 
758 int GnuTLS_socketbuf::open(const char hostname[], int port)
759 {
760 #ifdef MFEM_USE_GNUTLS_DEBUG
761  mfem::out << "[GnuTLS_socketbuf::open]" << std::endl;
762 #endif
763 
764  int err_flag = socketbuf::open(hostname, port); // calls close()
765  if (err_flag) { return err_flag; }
766 
767  start_session();
768 
769  return status.good() ? 0 : -100;
770 }
771 
773 {
774 #ifdef MFEM_USE_GNUTLS_DEBUG
775  mfem::out << "[GnuTLS_socketbuf::close]" << std::endl;
776 #endif
777 
778  end_session();
779 
780  int err_flag = socketbuf::close();
781 
782  return status.good() ? err_flag : -100;
783 }
784 
786 {
787  ssize_t bw, n = pptr() - pbase();
788 #ifdef MFEM_USE_GNUTLS_DEBUG
789  mfem::out << "[GnuTLS_socketbuf::sync n=" << n << ']' << std::endl;
790 #endif
791  if (!session_started || !status.good()) { return -1; }
792  while (n > 0)
793  {
794  bw = gnutls_record_send(session, pptr() - n, n);
795  if (bw == GNUTLS_E_INTERRUPTED || bw == GNUTLS_E_AGAIN) { continue; }
796  if (bw < 0)
797  {
798  status.set_result((int)bw);
799 #ifdef MFEM_DEBUG
800  status.print_on_error("gnutls_record_send");
801 #endif
802  setp(pptr() - n, obuf + buflen);
803  pbump(n);
804  return -1;
805  }
806  n -= bw;
807  }
808  setp(obuf, obuf + buflen);
809  return 0;
810 }
811 
812 GnuTLS_socketbuf::int_type GnuTLS_socketbuf::underflow()
813 {
814 #ifdef MFEM_USE_GNUTLS_DEBUG
815  mfem::out << "[GnuTLS_socketbuf::underflow ...]" << std::endl;
816 #endif
817  if (!session_started || !status.good()) { return traits_type::eof(); }
818 
819  ssize_t br;
820  do
821  {
822  br = gnutls_record_recv(session, ibuf, buflen);
823  if (br == GNUTLS_E_REHANDSHAKE)
824  {
825  continue; // TODO: replace with re-handshake
826  }
827  }
828  while (br == GNUTLS_E_INTERRUPTED || br == GNUTLS_E_AGAIN);
829 #ifdef MFEM_USE_GNUTLS_DEBUG
830  mfem::out << "[GnuTLS_socketbuf::underflow br=" << br << ']' << std::endl;
831 #endif
832 
833  if (br <= 0)
834  {
835  if (br < 0)
836  {
837  status.set_result((int)br);
838 #ifdef MFEM_DEBUG
839  status.print_on_error("gnutls_record_recv");
840 #endif
841  }
842  setg(NULL, NULL, NULL);
843  return traits_type::eof();
844  }
845  setg(ibuf, ibuf, ibuf + br);
846  return traits_type::to_int_type(*ibuf);
847 }
848 
849 std::streamsize GnuTLS_socketbuf::xsgetn(char_type *s__, std::streamsize n__)
850 {
851 #ifdef MFEM_USE_GNUTLS_DEBUG
852  mfem::out << "[GnuTLS_socketbuf::xsgetn n__=" << n__ << ']' << std::endl;
853 #endif
854  if (!session_started || !status.good()) { return 0; }
855 
856  const std::streamsize bn = egptr() - gptr();
857  if (n__ <= bn)
858  {
859  traits_type::copy(s__, gptr(), n__);
860  gbump(n__);
861  return n__;
862  }
863  traits_type::copy(s__, gptr(), bn);
864  setg(NULL, NULL, NULL);
865  std::streamsize remain = n__ - bn;
866  char_type *end = s__ + n__;
867  ssize_t br;
868  while (remain > 0)
869  {
870  do
871  {
872  br = gnutls_record_recv(session, end - remain, remain);
873  if (br == GNUTLS_E_REHANDSHAKE)
874  {
875  continue; // TODO: replace with re-handshake
876  }
877  }
878  while (br == GNUTLS_E_INTERRUPTED || br == GNUTLS_E_AGAIN);
879  if (br <= 0)
880  {
881  if (br < 0)
882  {
883  status.set_result((int)br);
884 #ifdef MFEM_DEBUG
885  status.print_on_error("gnutls_record_recv");
886 #endif
887  }
888  return (n__ - remain);
889  }
890  remain -= br;
891  }
892  return n__;
893 }
894 
895 std::streamsize GnuTLS_socketbuf::xsputn(const char_type *s__,
896  std::streamsize n__)
897 {
898 #ifdef MFEM_USE_GNUTLS_DEBUG
899  mfem::out << "[GnuTLS_socketbuf::xsputn n__=" << n__ << ']' << std::endl;
900 #endif
901  if (!session_started || !status.good()) { return 0; }
902 
903  if (pptr() + n__ <= epptr())
904  {
905  traits_type::copy(pptr(), s__, n__);
906  pbump(n__);
907  return n__;
908  }
909  if (sync() < 0)
910  {
911  return 0;
912  }
913  ssize_t bw;
914  std::streamsize remain = n__;
915  const char_type *end = s__ + n__;
916  while (remain > buflen)
917  {
918  bw = gnutls_record_send(session, end - remain, remain);
919  if (bw == GNUTLS_E_INTERRUPTED || bw == GNUTLS_E_AGAIN) { continue; }
920 #ifdef MFEM_USE_GNUTLS_DEBUG
921  mfem::out << "[GnuTLS_socketbuf::xsputn bw=" << bw << ']' << std::endl;
922 #endif
923  if (bw < 0)
924  {
925  status.set_result((int)bw);
926 #ifdef MFEM_DEBUG
927  status.print_on_error("gnutls_record_send");
928 #endif
929  return (n__ - remain);
930  }
931  remain -= bw;
932  }
933  if (remain > 0)
934  {
935  traits_type::copy(pptr(), end - remain, remain);
936  pbump(remain);
937  }
938  return n__;
939 }
940 
941 
945 
946 // static method
948 {
949  if (num_glvis_sockets == 0)
950  {
952  // state->set_log_level(1000);
953  std::string home_dir(getenv("HOME"));
954  std::string client_dir = home_dir + "/.config/glvis/client/";
955 #ifndef MFEM_USE_GNUTLS_X509
956  std::string pubkey = client_dir + "pubring.gpg";
957  std::string privkey = client_dir + "secring.gpg";
958  std::string trustedkeys = client_dir + "trusted-servers.gpg";
959 #else
960  std::string pubkey = client_dir + "cert.pem";
961  std::string privkey = client_dir + "key.pem";
962  std::string trustedkeys = client_dir + "trusted-servers.pem";
963 #endif
965  *state, pubkey.c_str(), privkey.c_str(), trustedkeys.c_str(),
966  GNUTLS_CLIENT);
967  if (!params->status.good())
968  {
969  mfem::out << " public key = " << pubkey << '\n'
970  << " private key = " << privkey << '\n'
971  << " trusted keys = " << trustedkeys << std::endl;
972  mfem::out << "Error setting GLVis client parameters.\n"
973  "Use the following GLVis script to create your GLVis keys:\n"
974  " bash glvis-keygen.sh [\"Your Name\"] [\"Your Email\"]"
975  << std::endl;
976  }
977  }
979  return *params;
980 }
981 
982 // static method
984 {
985  if (num_glvis_sockets > 0)
986  {
988  if (num_glvis_sockets == 0)
989  {
990  delete params; params = NULL;
991  delete state; state = NULL;
992  }
993  }
994 }
995 
997 {
998  buf__ = new GnuTLS_socketbuf(p);
999  std::iostream::rdbuf(buf__);
1000 }
1001 
1003  : std::iostream(0), glvis_client(false)
1004 {
1007 }
1008 
1009 #endif // MFEM_USE_GNUTLS
1010 
1011 void socketstream::set_socket(bool secure)
1012 {
1013  glvis_client = secure;
1014  if (secure)
1015  {
1016 #ifdef MFEM_USE_GNUTLS
1018 #else
1019  mfem_error("The secure option in class mfem::socketstream can only\n"
1020  "be used when GnuTLS support is enabled.");
1021 #endif
1022  }
1023  else
1024  {
1025  buf__ = new socketbuf;
1026  std::iostream::rdbuf(buf__);
1027  }
1028 }
1029 
1031 {
1032 #ifdef MFEM_USE_GNUTLS
1033  if (((GnuTLS_socketbuf*)buf__)->gnutls_good()) { clear(); }
1034  else { setstate(std::ios::failbit); }
1035 #endif
1036 }
1037 
1038 socketstream::socketstream(bool secure) : std::iostream(0)
1039 {
1040  set_socket(secure);
1041  if (secure) { check_secure_socket(); }
1042 }
1043 
1044 socketstream::socketstream(int s, bool secure) : std::iostream(0)
1045 {
1046  set_socket(secure);
1047  buf__->attach(s);
1048  if (secure) { check_secure_socket(); }
1049 }
1050 
1051 int socketstream::open(const char hostname[], int port)
1052 {
1053  int err_flag = buf__->open(hostname, port);
1054  if (err_flag)
1055  {
1056  setstate(std::ios::failbit);
1057  }
1058  else
1059  {
1060  clear();
1061  }
1062  return err_flag;
1063 }
1064 
1066 {
1067  delete buf__;
1068 #ifdef MFEM_USE_GNUTLS
1069  if (glvis_client) { remove_socket(); }
1070 #endif
1071 }
1072 
1073 } // 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_session_t session
gnutls_certificate_credentials_t my_cred
void set_socket(bool secure)
gnutls_dh_params_t get_dh_params()
STL namespace.
gnutls_dh_params_t dh_params
virtual std::streamsize xsgetn(char_type *s__, std::streamsize n__)
unsigned int get_flags() 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]
int get_result() const
virtual int_type underflow()
void print_on_error(const char *msg) const
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
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 xsgetn(char_type *s__, std::streamsize n__)
GnuTLS_global_state & state
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...
int ssize_t
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
RefCoord s[3]
static GnuTLS_global_state * state
virtual std::streamsize xsputn(const char_type *s__, std::streamsize n__)