MFEM  v3.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  // std::cout << "[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  std::cout << "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  // std::cout << "[socketbuf::underflow br=" << br << ']'
147  // << std::endl;
148  if (br <= 0)
149  {
150 #ifdef MFEM_DEBUG
151  if (br < 0)
152  {
153  std::cout << "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  // std::cout << "[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  std::cout << "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  // std::cout << "[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  std::cout << "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 
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  const int backlog = 4;
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  std::cout << "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  std::cout << "Generating DH params (" << bits << " bits) ..."
365  << std::flush;
366  status.set_result(gnutls_dh_params_generate2(dh_params, bits));
367  std::cout << " 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  std::cout << "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  std::cout << "Error in gnutls_certificate_verification_status_print:"
398  << gnutls_strerror(ret) << std::endl;
399  return GNUTLS_E_CERTIFICATE_ERROR;
400  }
401  std::cout << 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  std::cout << "Error in gnutls_certificate_verify_peers2:"
409  << gnutls_strerror(ret) << std::endl;
410  return GNUTLS_E_CERTIFICATE_ERROR;
411  }
412 #ifdef MFEM_DEBUG
413  std::cout << (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  std::cout << "[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  std::cout << "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  std::cout << "[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 #if GNUTLS_VERSION_NUMBER >= 0x030000 // what is the right version here?
546  const char *priorities =
547  "NONE:+VERS-TLS1.2:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:"
548  "+KX-ALL:+CTYPE-OPENPGP:+CURVE-ALL";
549 #else
550  const char *priorities = "NORMAL:-CTYPE-X.509";
551 #endif
552  const char *err_ptr;
553  status.set_result(
554  gnutls_priority_set_direct(session, priorities, &err_ptr));
555  status.print_on_error("gnutls_priority_set_direct");
556  if (!status.good())
557  {
558  std::cout << "Error ptr = \"" << err_ptr << '"' << std::endl;
559  }
560  }
561 
562  if (status.good())
563  {
564  // set session credentials
565  status.set_result(
566  gnutls_credentials_set(
567  session, GNUTLS_CRD_CERTIFICATE, my_cred));
568  status.print_on_error("gnutls_credentials_set");
569  }
570 
571  if (status.good())
572  {
573  const char *hostname = NULL; // no hostname verification
574  gnutls_session_set_ptr(session, (void*)hostname);
575  if (params.get_flags() & GNUTLS_SERVER)
576  {
577  // require clients to send certificate:
578  gnutls_certificate_server_set_request(session, GNUTLS_CERT_REQUIRE);
579  }
580 #if GNUTLS_VERSION_NUMBER >= 0x030100
581  gnutls_handshake_set_timeout(
582  session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
583 #endif
584  }
585 
586  if (status.good())
587  {
588 #if GNUTLS_VERSION_NUMBER >= 0x030109
589  gnutls_transport_set_int(session, socket_descriptor);
590 #else
591  gnutls_transport_set_ptr(session,
592  (gnutls_transport_ptr_t) socket_descriptor);
593 #endif
594 
595  handshake();
596  }
597 
598 #if GNUTLS_VERSION_NUMBER < 0x021000
599  if (status.good())
600  {
601  status.set_result(mfem_gnutls_verify_callback(session));
602  if (!status.good())
603  {
604  int err;
605  do
606  {
607  // Close the connection without waiting for close reply, i.e. we
608  // use GNUTLS_SHUT_WR.
609  err = gnutls_bye(session, GNUTLS_SHUT_WR);
610  }
611  while (err == GNUTLS_E_AGAIN || err == GNUTLS_E_INTERRUPTED);
612  }
613  }
614 #endif
615 
616 #ifdef MFEM_USE_GNUTLS_PUSH_FUNCTION
617  if (status.good())
618  {
619  gnutls_transport_set_push_function(session, mfem_gnutls_push_function);
620  }
621 #endif
622 
623  if (!status.good())
624  {
625  if (session_started) { gnutls_deinit(session); }
626  session_started = false;
628  }
629 }
630 
632 {
633 #ifdef MFEM_USE_GNUTLS_DEBUG
634  std::cout << "[GnuTLS_socketbuf::end_session]" << std::endl;
635 #endif
636 
637  // check for valid 'socket_descriptor'
638  if (!session_started) { return; }
639 
640  if (is_open() && status.good())
641  {
642 #ifdef MFEM_USE_GNUTLS_DEBUG
643  std::cout << "[GnuTLS_socketbuf::end_session: gnutls_bye]" << std::endl;
644 #endif
645  int err;
646  do
647  {
648  // err = gnutls_bye(session, GNUTLS_SHUT_RDWR);
649  err = gnutls_bye(session, GNUTLS_SHUT_WR); // does not wait for reply
650  status.set_result(err);
651  }
652  while (err == GNUTLS_E_AGAIN || err == GNUTLS_E_INTERRUPTED);
653  status.print_on_error("gnutls_bye");
654  }
655 
656  gnutls_deinit(session);
657  session_started = false;
658 }
659 
661 {
662 #ifdef MFEM_USE_GNUTLS_DEBUG
663  std::cout << "[GnuTLS_socketbuf::attach]" << std::endl;
664 #endif
665 
666  end_session();
667 
668  int old_sd = socketbuf::attach(sd);
669 
670  start_session();
671 
672  return old_sd;
673 }
674 
675 int GnuTLS_socketbuf::open(const char hostname[], int port)
676 {
677 #ifdef MFEM_USE_GNUTLS_DEBUG
678  std::cout << "[GnuTLS_socketbuf::open]" << std::endl;
679 #endif
680 
681  int err = socketbuf::open(hostname, port); // calls close()
682  if (err) { return err; }
683 
684  start_session();
685 
686  return status.good() ? 0 : -100;
687 }
688 
690 {
691 #ifdef MFEM_USE_GNUTLS_DEBUG
692  std::cout << "[GnuTLS_socketbuf::close]" << std::endl;
693 #endif
694 
695  end_session();
696 
697  int err = socketbuf::close();
698 
699  return status.good() ? err : -100;
700 }
701 
703 {
704  ssize_t bw, n = pptr() - pbase();
705 #ifdef MFEM_USE_GNUTLS_DEBUG
706  std::cout << "[GnuTLS_socketbuf::sync n=" << n << ']' << std::endl;
707 #endif
708  if (!session_started || !status.good()) { return -1; }
709  while (n > 0)
710  {
711  bw = gnutls_record_send(session, pptr() - n, n);
712  if (bw == GNUTLS_E_INTERRUPTED || bw == GNUTLS_E_AGAIN) { continue; }
713  if (bw < 0)
714  {
715  status.set_result((int)bw);
716 #ifdef MFEM_DEBUG
717  status.print_on_error("gnutls_record_send");
718 #endif
719  setp(pptr() - n, obuf + buflen);
720  pbump(n);
721  return -1;
722  }
723  n -= bw;
724  }
725  setp(obuf, obuf + buflen);
726  return 0;
727 }
728 
729 GnuTLS_socketbuf::int_type GnuTLS_socketbuf::underflow()
730 {
731 #ifdef MFEM_USE_GNUTLS_DEBUG
732  std::cout << "[GnuTLS_socketbuf::underflow ...]" << std::endl;
733 #endif
734  if (!session_started || !status.good()) { return traits_type::eof(); }
735 
736  ssize_t br;
737  do
738  {
739  br = gnutls_record_recv(session, ibuf, buflen);
740  if (br == GNUTLS_E_REHANDSHAKE)
741  {
742  continue; // TODO: replace with re-handshake
743  }
744  }
745  while (br == GNUTLS_E_INTERRUPTED || br == GNUTLS_E_AGAIN);
746 #ifdef MFEM_USE_GNUTLS_DEBUG
747  std::cout << "[GnuTLS_socketbuf::underflow br=" << br << ']' << std::endl;
748 #endif
749 
750  if (br <= 0)
751  {
752  if (br < 0)
753  {
754  status.set_result((int)br);
755 #ifdef MFEM_DEBUG
756  status.print_on_error("gnutls_record_recv");
757 #endif
758  }
759  setg(NULL, NULL, NULL);
760  return traits_type::eof();
761  }
762  setg(ibuf, ibuf, ibuf + br);
763  return traits_type::to_int_type(*ibuf);
764 }
765 
766 std::streamsize GnuTLS_socketbuf::xsgetn(char_type *__s, std::streamsize __n)
767 {
768 #ifdef MFEM_USE_GNUTLS_DEBUG
769  std::cout << "[GnuTLS_socketbuf::xsgetn __n=" << __n << ']' << std::endl;
770 #endif
771  if (!session_started || !status.good()) { return 0; }
772 
773  const std::streamsize bn = egptr() - gptr();
774  if (__n <= bn)
775  {
776  traits_type::copy(__s, gptr(), __n);
777  gbump(__n);
778  return __n;
779  }
780  traits_type::copy(__s, gptr(), bn);
781  setg(NULL, NULL, NULL);
782  std::streamsize remain = __n - bn;
783  char_type *end = __s + __n;
784  ssize_t br;
785  while (remain > 0)
786  {
787  do
788  {
789  br = gnutls_record_recv(session, end - remain, remain);
790  if (br == GNUTLS_E_REHANDSHAKE)
791  {
792  continue; // TODO: replace with re-handshake
793  }
794  }
795  while (br == GNUTLS_E_INTERRUPTED || br == GNUTLS_E_AGAIN);
796  if (br <= 0)
797  {
798  if (br < 0)
799  {
800  status.set_result((int)br);
801 #ifdef MFEM_DEBUG
802  status.print_on_error("gnutls_record_recv");
803 #endif
804  }
805  return (__n - remain);
806  }
807  remain -= br;
808  }
809  return __n;
810 }
811 
812 std::streamsize GnuTLS_socketbuf::xsputn(const char_type *__s,
813  std::streamsize __n)
814 {
815 #ifdef MFEM_USE_GNUTLS_DEBUG
816  std::cout << "[GnuTLS_socketbuf::xsputn __n=" << __n << ']' << std::endl;
817 #endif
818  if (!session_started || !status.good()) { return 0; }
819 
820  if (pptr() + __n <= epptr())
821  {
822  traits_type::copy(pptr(), __s, __n);
823  pbump(__n);
824  return __n;
825  }
826  if (sync() < 0)
827  {
828  return 0;
829  }
830  ssize_t bw;
831  std::streamsize remain = __n;
832  const char_type *end = __s + __n;
833  while (remain > buflen)
834  {
835  bw = gnutls_record_send(session, end - remain, remain);
836  if (bw == GNUTLS_E_INTERRUPTED || bw == GNUTLS_E_AGAIN) { continue; }
837 #ifdef MFEM_USE_GNUTLS_DEBUG
838  std::cout << "[GnuTLS_socketbuf::xsputn bw=" << bw << ']' << std::endl;
839 #endif
840  if (bw < 0)
841  {
842  status.set_result((int)bw);
843 #ifdef MFEM_DEBUG
844  status.print_on_error("gnutls_record_send");
845 #endif
846  return (__n - remain);
847  }
848  remain -= bw;
849  }
850  if (remain > 0)
851  {
852  traits_type::copy(pptr(), end - remain, remain);
853  pbump(remain);
854  }
855  return __n;
856 }
857 
858 
862 
863 // static method
865 {
866  if (num_glvis_sockets == 0)
867  {
869  // state->set_log_level(1000);
870  std::string home_dir(getenv("HOME"));
871  std::string client_dir = home_dir + "/.config/glvis/client/";
872  std::string pubkey = client_dir + "pubring.gpg";
873  std::string privkey = client_dir + "secring.gpg";
874  std::string trustedkeys = client_dir + "trusted-servers.gpg";
876  *state, pubkey.c_str(), privkey.c_str(), trustedkeys.c_str(),
877  GNUTLS_CLIENT);
878  if (!params->status.good())
879  {
880  std::cout << " public key = " << pubkey << '\n'
881  << " private key = " << privkey << '\n'
882  << " trusted keys = " << trustedkeys << std::endl;
883  std::cout << "Error setting GLVis client parameters.\n"
884  "Use the following GLVis script to create your GLVis keys:\n"
885  " bash glvis-keygen.sh [\"Your Name\"] [\"Your Email\"]"
886  << std::endl;
887  }
888  }
890  return *params;
891 }
892 
893 // static method
895 {
896  if (num_glvis_sockets > 0)
897  {
899  if (num_glvis_sockets == 0)
900  {
901  delete params; params = NULL;
902  delete state; state = NULL;
903  }
904  }
905 }
906 
908 {
909  buf__ = new GnuTLS_socketbuf(p);
910  std::iostream::rdbuf(buf__);
911 }
912 
914  : std::iostream(0), glvis_client(false)
915 {
918 }
919 
920 #endif // MFEM_USE_GNUTLS
921 
922 void socketstream::set_socket(bool secure)
923 {
924  glvis_client = secure;
925  if (secure)
926  {
927 #ifdef MFEM_USE_GNUTLS
929 #else
930  mfem_error("The secure option in class mfem::socketstream can only\n"
931  "be used when GnuTLS support is enabled.");
932 #endif
933  }
934  else
935  {
936  buf__ = new socketbuf;
937  std::iostream::rdbuf(buf__);
938  }
939 }
940 
942 {
943 #ifdef MFEM_USE_GNUTLS
944  if (((GnuTLS_socketbuf*)buf__)->gnutls_good()) { clear(); }
945  else { setstate(std::ios::failbit); }
946 #endif
947 }
948 
949 socketstream::socketstream(bool secure) : std::iostream(0)
950 {
951  set_socket(secure);
952  if (secure) { check_secure_socket(); }
953 }
954 
955 socketstream::socketstream(int s, bool secure) : std::iostream(0)
956 {
957  set_socket(secure);
958  buf__->attach(s);
959  if (secure) { check_secure_socket(); }
960 }
961 
962 int socketstream::open(const char hostname[], int port)
963 {
964  int err = buf__->open(hostname, port);
965  if (err)
966  {
967  setstate(std::ios::failbit);
968  }
969  else
970  {
971  clear();
972  }
973  return err;
974 }
975 
977 {
978 #ifdef MFEM_USE_GNUTLS
979  if (glvis_client) { remove_socket(); }
980 #endif
981  delete buf__;
982 }
983 
984 } // 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:25
void mfem_error(const char *msg)
Definition: error.cpp:23
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
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