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