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