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