diff --git a/lib/common/remote.c b/lib/common/remote.c index 5cf430d93f..fa05ec6cf0 100644 --- a/lib/common/remote.c +++ b/lib/common/remote.c @@ -1,283 +1,300 @@ /* * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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); gnutls_set_default_priority(*session); gnutls_kx_set_priority (*session, tls_kx_order); 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); retry: 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"); goto retry; } else if(rc < len) { crm_debug("Only sent %d of %d bytes", rc, len); len -= rc; unsent += rc; goto retry; } } crm_free(xml_text); return NULL; } static char* cib_recv_tls(gnutls_session *session) { int rc = 0; int last = 0; char* tls_buf = NULL; int chunk_size = 1024; int len = chunk_size; if (session == NULL) { return NULL; } crm_malloc0(tls_buf, chunk_size); while(1) { rc = gnutls_record_recv(*session, tls_buf+last, chunk_size); if (rc == 0) { if(len == 0) { goto bail; } return tls_buf; } else if(rc > 0 && rc < chunk_size) { return tls_buf; } else if(rc == chunk_size) { crm_debug("Creating more space: %d += %d: %.60s", len, chunk_size, tls_buf); last = len; len += chunk_size; crm_realloc(tls_buf, len); CRM_ASSERT(tls_buf != NULL); crm_debug("New size: %d: %.60s", len, tls_buf); } if(rc < 0 && rc != GNUTLS_E_INTERRUPTED && rc != GNUTLS_E_AGAIN) { crm_perror(LOG_ERR,"Error receiving message: %d", rc); goto bail; } } bail: crm_free(tls_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); - rc = write (sock, xml_text, len); - CRM_CHECK(len == rc, - crm_warn("Wrote %d of %d bytes", rc, 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 bytes", rc, len); + len -= rc; + unsent += rc; + goto retry; + } } crm_free(xml_text); return NULL; } char* cib_recv_plaintext(int sock) { int last = 0; char* buf = NULL; int chunk_size = 512; int len = chunk_size; crm_malloc0(buf, chunk_size); while(1) { int rc = recv(sock, buf+last, chunk_size, 0); if (rc == 0) { if(len == 0) { goto bail; } return buf; } else if(rc > 0 && rc < chunk_size) { return buf; } else if(rc == chunk_size) { last = len; len += chunk_size; crm_realloc(buf, len); CRM_ASSERT(buf != NULL); } if(rc < 0 && errno != EINTR) { crm_perror(LOG_ERR,"Error receiving message: %d", rc); goto bail; } } 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"); } else { xml = string2xml(reply); if(xml == NULL) { crm_err("Couldn't parse: '%.120s'", reply); } } crm_free(reply); return xml; }