MFEM  v3.3
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  pubsync();
643 #ifdef MFEM_USE_GNUTLS_DEBUG
644  std::cout << "[GnuTLS_socketbuf::end_session: gnutls_bye]" << std::endl;
645 #endif
646  int err;
647  do
648  {
649  // err = gnutls_bye(session, GNUTLS_SHUT_RDWR);
650  err = gnutls_bye(session, GNUTLS_SHUT_WR); // does not wait for reply
651  status.set_result(err);
652  }
653  while (err == GNUTLS_E_AGAIN || err == GNUTLS_E_INTERRUPTED);
654  status.print_on_error("gnutls_bye");
655  }
656 
657  gnutls_deinit(session);
658  session_started = false;
659 }
660 
662 {
663 #ifdef MFEM_USE_GNUTLS_DEBUG
664  std::cout << "[GnuTLS_socketbuf::attach]" << std::endl;
665 #endif
666 
667  end_session();
668 
669  int old_sd = socketbuf::attach(sd);
670 
671  start_session();
672 
673  return old_sd;
674 }
675 
676 int GnuTLS_socketbuf::open(const char hostname[], int port)
677 {
678 #ifdef MFEM_USE_GNUTLS_DEBUG
679  std::cout << "[GnuTLS_socketbuf::open]" << std::endl;
680 #endif
681 
682  int err = socketbuf::open(hostname, port); // calls close()
683  if (err) { return err; }
684 
685  start_session();
686 
687  return status.good() ? 0 : -100;
688 }
689 
691 {
692 #ifdef MFEM_USE_GNUTLS_DEBUG
693  std::cout << "[GnuTLS_socketbuf::close]" << std::endl;
694 #endif
695 
696  end_session();
697 
698  int err = socketbuf::close();
699 
700  return status.good() ? err : -100;
701 }
702 
704 {
705  ssize_t bw, n = pptr() - pbase();
706 #ifdef MFEM_USE_GNUTLS_DEBUG
707  std::cout << "[GnuTLS_socketbuf::sync n=" << n << ']' << std::endl;
708 #endif
709  if (!session_started || !status.good()) { return -1; }
710  while (n > 0)
711  {
712  bw = gnutls_record_send(session, pptr() - n, n);
713  if (bw == GNUTLS_E_INTERRUPTED || bw == GNUTLS_E_AGAIN) { continue; }
714  if (bw < 0)
715  {
716  status.set_result((int)bw);
717 #ifdef MFEM_DEBUG
718  status.print_on_error("gnutls_record_send");
719 #endif
720  setp(pptr() - n, obuf + buflen);
721  pbump(n);
722  return -1;
723  }
724  n -= bw;
725  }
726  setp(obuf, obuf + buflen);
727  return 0;
728 }
729 
730 GnuTLS_socketbuf::int_type GnuTLS_socketbuf::underflow()
731 {
732 #ifdef MFEM_USE_GNUTLS_DEBUG
733  std::cout << "[GnuTLS_socketbuf::underflow ...]" << std::endl;
734 #endif
735  if (!session_started || !status.good()) { return traits_type::eof(); }
736 
737  ssize_t br;
738  do
739  {
740  br = gnutls_record_recv(session, ibuf, buflen);
741  if (br == GNUTLS_E_REHANDSHAKE)
742  {
743  continue; // TODO: replace with re-handshake
744  }
745  }
746  while (br == GNUTLS_E_INTERRUPTED || br == GNUTLS_E_AGAIN);
747 #ifdef MFEM_USE_GNUTLS_DEBUG
748  std::cout << "[GnuTLS_socketbuf::underflow br=" << br << ']' << std::endl;
749 #endif
750 
751  if (br <= 0)
752  {
753  if (br < 0)
754  {
755  status.set_result((int)br);
756 #ifdef MFEM_DEBUG
757  status.print_on_error("gnutls_record_recv");
758 #endif
759  }
760  setg(NULL, NULL, NULL);
761  return traits_type::eof();
762  }
763  setg(ibuf, ibuf, ibuf + br);
764  return traits_type::to_int_type(*ibuf);
765 }
766 
767 std::streamsize GnuTLS_socketbuf::xsgetn(char_type *__s, std::streamsize __n)
768 {
769 #ifdef MFEM_USE_GNUTLS_DEBUG
770  std::cout << "[GnuTLS_socketbuf::xsgetn __n=" << __n << ']' << std::endl;
771 #endif
772  if (!session_started || !status.good()) { return 0; }
773 
774  const std::streamsize bn = egptr() - gptr();
775  if (__n <= bn)
776  {
777  traits_type::copy(__s, gptr(), __n);
778  gbump(__n);
779  return __n;
780  }
781  traits_type::copy(__s, gptr(), bn);
782  setg(NULL, NULL, NULL);
783  std::streamsize remain = __n - bn;
784  char_type *end = __s + __n;
785  ssize_t br;
786  while (remain > 0)
787  {
788  do
789  {
790  br = gnutls_record_recv(session, end - remain, remain);
791  if (br == GNUTLS_E_REHANDSHAKE)
792  {
793  continue; // TODO: replace with re-handshake
794  }
795  }
796  while (br == GNUTLS_E_INTERRUPTED || br == GNUTLS_E_AGAIN);
797  if (br <= 0)
798  {
799  if (br < 0)
800  {
801  status.set_result((int)br);
802 #ifdef MFEM_DEBUG
803  status.print_on_error("gnutls_record_recv");
804 #endif
805  }
806  return (__n - remain);
807  }
808  remain -= br;
809  }
810  return __n;
811 }
812 
813 std::streamsize GnuTLS_socketbuf::xsputn(const char_type *__s,
814  std::streamsize __n)
815 {
816 #ifdef MFEM_USE_GNUTLS_DEBUG
817  std::cout << "[GnuTLS_socketbuf::xsputn __n=" << __n << ']' << std::endl;
818 #endif
819  if (!session_started || !status.good()) { return 0; }
820 
821  if (pptr() + __n <= epptr())
822  {
823  traits_type::copy(pptr(), __s, __n);
824  pbump(__n);
825  return __n;
826  }
827  if (sync() < 0)
828  {
829  return 0;
830  }
831  ssize_t bw;
832  std::streamsize remain = __n;
833  const char_type *end = __s + __n;
834  while (remain > buflen)
835  {
836  bw = gnutls_record_send(session, end - remain, remain);
837  if (bw == GNUTLS_E_INTERRUPTED || bw == GNUTLS_E_AGAIN) { continue; }
838 #ifdef MFEM_USE_GNUTLS_DEBUG
839  std::cout << "[GnuTLS_socketbuf::xsputn bw=" << bw << ']' << std::endl;
840 #endif
841  if (bw < 0)
842  {
843  status.set_result((int)bw);
844 #ifdef MFEM_DEBUG
845  status.print_on_error("gnutls_record_send");
846 #endif
847  return (__n - remain);
848  }
849  remain -= bw;
850  }
851  if (remain > 0)
852  {
853  traits_type::copy(pptr(), end - remain, remain);
854  pbump(remain);
855  }
856  return __n;
857 }
858 
859 
863 
864 // static method
866 {
867  if (num_glvis_sockets == 0)
868  {
870  // state->set_log_level(1000);
871  std::string home_dir(getenv("HOME"));
872  std::string client_dir = home_dir + "/.config/glvis/client/";
873  std::string pubkey = client_dir + "pubring.gpg";
874  std::string privkey = client_dir + "secring.gpg";
875  std::string trustedkeys = client_dir + "trusted-servers.gpg";
877  *state, pubkey.c_str(), privkey.c_str(), trustedkeys.c_str(),
878  GNUTLS_CLIENT);
879  if (!params->status.good())
880  {
881  std::cout << " public key = " << pubkey << '\n'
882  << " private key = " << privkey << '\n'
883  << " trusted keys = " << trustedkeys << std::endl;
884  std::cout << "Error setting GLVis client parameters.\n"
885  "Use the following GLVis script to create your GLVis keys:\n"
886  " bash glvis-keygen.sh [\"Your Name\"] [\"Your Email\"]"
887  << std::endl;
888  }
889  }
891  return *params;
892 }
893 
894 // static method
896 {
897  if (num_glvis_sockets > 0)
898  {
900  if (num_glvis_sockets == 0)
901  {
902  delete params; params = NULL;
903  delete state; state = NULL;
904  }
905  }
906 }
907 
909 {
910  buf__ = new GnuTLS_socketbuf(p);
911  std::iostream::rdbuf(buf__);
912 }
913 
915  : std::iostream(0), glvis_client(false)
916 {
919 }
920 
921 #endif // MFEM_USE_GNUTLS
922 
923 void socketstream::set_socket(bool secure)
924 {
925  glvis_client = secure;
926  if (secure)
927  {
928 #ifdef MFEM_USE_GNUTLS
930 #else
931  mfem_error("The secure option in class mfem::socketstream can only\n"
932  "be used when GnuTLS support is enabled.");
933 #endif
934  }
935  else
936  {
937  buf__ = new socketbuf;
938  std::iostream::rdbuf(buf__);
939  }
940 }
941 
943 {
944 #ifdef MFEM_USE_GNUTLS
945  if (((GnuTLS_socketbuf*)buf__)->gnutls_good()) { clear(); }
946  else { setstate(std::ios::failbit); }
947 #endif
948 }
949 
950 socketstream::socketstream(bool secure) : std::iostream(0)
951 {
952  set_socket(secure);
953  if (secure) { check_secure_socket(); }
954 }
955 
956 socketstream::socketstream(int s, bool secure) : std::iostream(0)
957 {
958  set_socket(secure);
959  buf__->attach(s);
960  if (secure) { check_secure_socket(); }
961 }
962 
963 int socketstream::open(const char hostname[], int port)
964 {
965  int err = buf__->open(hostname, port);
966  if (err)
967  {
968  setstate(std::ios::failbit);
969  }
970  else
971  {
972  clear();
973  }
974  return err;
975 }
976 
978 {
979  delete buf__;
980 #ifdef MFEM_USE_GNUTLS
981  if (glvis_client) { remove_socket(); }
982 #endif
983 }
984 
985 } // 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:106
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