diff --git a/lib/common/remote.c b/lib/common/remote.c index c008c756c3..8a073c2f56 100644 --- a/lib/common/remote.c +++ b/lib/common/remote.c @@ -1,332 +1,336 @@ /* * Copyright (c) 2008 Andrew Beekhof * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_GNUTLS_GNUTLS_H # undef KEYFILE # include #endif #ifdef HAVE_GNUTLS_GNUTLS_H const int tls_kx_order[] = { GNUTLS_KX_ANON_DH, GNUTLS_KX_DHE_RSA, GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, 0 }; gnutls_anon_client_credentials anon_cred_c; gnutls_anon_server_credentials anon_cred_s; static char *cib_send_tls(gnutls_session *session, xmlNode *msg); static char *cib_recv_tls(gnutls_session *session); #endif char *cib_recv_plaintext(int sock); char *cib_send_plaintext(int sock, xmlNode *msg); #ifdef HAVE_GNUTLS_GNUTLS_H gnutls_session *create_tls_session(int csock, int type); gnutls_session * create_tls_session(int csock, int type /* GNUTLS_SERVER, GNUTLS_CLIENT */) { int rc = 0; gnutls_session *session = gnutls_malloc(sizeof(gnutls_session)); gnutls_init(session, type); #ifdef HAVE_GNUTLS_PRIORITY_SET_DIRECT /* http://www.manpagez.com/info/gnutls/gnutls-2.10.4/gnutls_81.php#Echo-Server-with-anonymous-authentication */ gnutls_priority_set_direct (*session, "NORMAL:+ANON-DH", NULL); /* gnutls_priority_set_direct (*session, "NONE:+VERS-TLS-ALL:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+ANON-DH", NULL); */ #else gnutls_set_default_priority(*session); gnutls_kx_set_priority (*session, tls_kx_order); #endif gnutls_transport_set_ptr(*session, (gnutls_transport_ptr) GINT_TO_POINTER(csock)); switch(type) { case GNUTLS_SERVER: gnutls_credentials_set(*session, GNUTLS_CRD_ANON, anon_cred_s); break; case GNUTLS_CLIENT: gnutls_credentials_set(*session, GNUTLS_CRD_ANON, anon_cred_c); break; } do { rc = gnutls_handshake (*session); } while (rc == GNUTLS_E_INTERRUPTED || rc == GNUTLS_E_AGAIN); if (rc < 0) { crm_err("Handshake failed: %s", gnutls_strerror(rc)); gnutls_deinit(*session); gnutls_free(session); return NULL; } return session; } static char* cib_send_tls(gnutls_session *session, xmlNode *msg) { char *xml_text = NULL; #if 0 const char *name = crm_element_name(msg); if(safe_str_neq(name, "cib_command")) { xmlNodeSetName(msg, "cib_result"); } #endif xml_text = dump_xml_unformatted(msg); if(xml_text != NULL) { char *unsent = xml_text; int len = strlen(xml_text); int rc = 0; len++; /* null char */ crm_debug_3("Message size: %d", len); while(TRUE) { rc = gnutls_record_send (*session, unsent, len); crm_debug("Sent %d bytes", rc); if(rc == GNUTLS_E_INTERRUPTED || rc == GNUTLS_E_AGAIN) { crm_debug("Retry"); } else if(rc < 0) { crm_debug("Connection terminated"); break; } else if(rc < len) { crm_debug("Only sent %d of %d bytes", rc, len); len -= rc; unsent += rc; } else { break; } } } crm_free(xml_text); return NULL; } static char* cib_recv_tls(gnutls_session *session) { char* buf = NULL; int rc = 0; int len = 0; int chunk_size = 1024; if (session == NULL) { return NULL; } crm_malloc0(buf, chunk_size); while(TRUE) { errno = 0; rc = gnutls_record_recv(*session, buf+len, chunk_size); crm_debug_2("Got %d more bytes. errno=%d", rc, errno); if(rc == GNUTLS_E_INTERRUPTED || rc == GNUTLS_E_AGAIN) { crm_debug_2("Retry"); + } else if(rc == GNUTLS_E_UNEXPECTED_PACKET_LENGTH) { + crm_trace("Session disconnected"); + goto bail; + } else if(rc < 0) { crm_err("Error receiving message: %s (%d)", gnutls_strerror(rc), rc); goto bail; } else if(rc == chunk_size) { len += rc; chunk_size *= 2; crm_realloc(buf, len + chunk_size); crm_debug_2("Retry with %d more bytes", (int)chunk_size); CRM_ASSERT(buf != NULL); } else if(buf[len+rc-1] != 0) { crm_debug_2("Last char is %d '%c'", buf[len+rc-1], buf[len+rc-1]); crm_debug_2("Retry with %d more bytes", (int)chunk_size); len += rc; crm_realloc(buf, len + chunk_size); CRM_ASSERT(buf != NULL); } else { crm_debug_2("Got %d more bytes", (int)rc); return buf; } } bail: crm_free(buf); return NULL; } #endif char* cib_send_plaintext(int sock, xmlNode *msg) { char *xml_text = dump_xml_unformatted(msg); if(xml_text != NULL) { int rc = 0; char *unsent = xml_text; int len = strlen(xml_text); len++; /* null char */ crm_debug_3("Message on socket %d: size=%d", sock, len); retry: rc = write (sock, unsent, len); if(rc < 0) { switch(errno) { case EINTR: case EAGAIN: crm_debug_2("Retry"); goto retry; default: crm_perror(LOG_ERR, "Could only write %d of the remaining %d bytes", rc, len); break; } } else if(rc < len) { crm_debug_2("Only sent %d of %d remaining bytes", rc, len); len -= rc; unsent += rc; goto retry; } else { crm_debug_2("Sent %d bytes: %.100s", rc, xml_text); } } crm_free(xml_text); return NULL; } char* cib_recv_plaintext(int sock) { char* buf = NULL; ssize_t rc = 0; ssize_t len = 0; ssize_t chunk_size = 512; crm_malloc0(buf, chunk_size); while(1) { errno = 0; rc = read(sock, buf+len, chunk_size); crm_debug_2("Got %d more bytes. errno=%d", (int)rc, errno); if(errno == EINTR || errno == EAGAIN) { crm_debug_2("Retry: %d", (int)rc); if(rc > 0) { len += rc; crm_realloc(buf, len + chunk_size); CRM_ASSERT(buf != NULL); } } else if(rc < 0) { crm_perror(LOG_ERR,"Error receiving message: %d", (int)rc); goto bail; } else if(rc == chunk_size) { len += rc; chunk_size *= 2; crm_realloc(buf, len + chunk_size); crm_debug_2("Retry with %d more bytes", (int)chunk_size); CRM_ASSERT(buf != NULL); } else if(buf[len+rc-1] != 0) { crm_debug_2("Last char is %d '%c'", buf[len+rc-1], buf[len+rc-1]); crm_debug_2("Retry with %d more bytes", (int)chunk_size); len += rc; crm_realloc(buf, len + chunk_size); CRM_ASSERT(buf != NULL); } else { return buf; } } bail: crm_free(buf); return NULL; } void cib_send_remote_msg(void *session, xmlNode *msg, gboolean encrypted) { if(encrypted) { #ifdef HAVE_GNUTLS_GNUTLS_H cib_send_tls(session, msg); #else CRM_ASSERT(encrypted == FALSE); #endif } else { cib_send_plaintext(GPOINTER_TO_INT(session), msg); } } xmlNode* cib_recv_remote_msg(void *session, gboolean encrypted) { char *reply = NULL; xmlNode *xml = NULL; if(encrypted) { #ifdef HAVE_GNUTLS_GNUTLS_H reply = cib_recv_tls(session); #else CRM_ASSERT(encrypted == FALSE); #endif } else { reply = cib_recv_plaintext(GPOINTER_TO_INT(session)); } if(reply == NULL || strlen(reply) == 0) { - crm_err("Empty reply"); + crm_trace("Empty reply"); } else { xml = string2xml(reply); if(xml == NULL) { crm_err("Couldn't parse: '%.120s'", reply); } } crm_free(reply); return xml; }