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