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