diff --git a/include/crm_internal.h b/include/crm_internal.h index 388af5931f..664e3e0b7f 100644 --- a/include/crm_internal.h +++ b/include/crm_internal.h @@ -1,290 +1,301 @@ /* crm_internal.h */ /* * Copyright (C) 2006 - 2008 * Andrew Beekhof * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU 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 */ #ifndef CRM_INTERNAL__H # define CRM_INTERNAL__H # include # include # include # include # include # include # include /* Dynamic loading of libraries */ void *find_library_function(void **handle, const char *lib, const char *fn, int fatal); void *convert_const_pointer(const void *ptr); /* For ACLs */ char *uid2username(uid_t uid); void determine_request_user(char *user, xmlNode * request, const char *field); # if ENABLE_ACL # include static inline gboolean is_privileged(const char *user) { if (user == NULL) { return FALSE; } else if (strcmp(user, CRM_DAEMON_USER) == 0) { return TRUE; } else if (strcmp(user, "root") == 0) { return TRUE; } return FALSE; } # endif /* CLI option processing*/ # ifdef HAVE_GETOPT_H # include # else # define no_argument 0 # define required_argument 1 # endif # define pcmk_option_default 0x00000 # define pcmk_option_hidden 0x00001 # define pcmk_option_paragraph 0x00002 # define pcmk_option_example 0x00004 struct crm_option { /* Fields from 'struct option' in getopt.h */ /* name of long option */ const char *name; /* * one of no_argument, required_argument, and optional_argument: * whether option takes an argument */ int has_arg; /* if not NULL, set *flag to val when option found */ int *flag; /* if flag not NULL, value to set *flag to; else return value */ int val; /* Custom fields */ const char *desc; long flags; }; void crm_set_options(const char *short_options, const char *usage, struct crm_option *long_options, const char *app_desc); int crm_get_option(int argc, char **argv, int *index); int crm_get_option_long(int argc, char **argv, int *index, const char **longname); void crm_help(char cmd, int exit_code); /* Cluster Option Processing */ typedef struct pe_cluster_option_s { const char *name; const char *alt_name; const char *type; const char *values; const char *default_value; gboolean(*is_valid) (const char *); const char *description_short; const char *description_long; } pe_cluster_option; const char *cluster_option(GHashTable * options, gboolean(*validate) (const char *), const char *name, const char *old_name, const char *def_value); const char *get_cluster_pref(GHashTable * options, pe_cluster_option * option_list, int len, const char *name); void config_metadata(const char *name, const char *version, const char *desc_short, const char *desc_long, pe_cluster_option * option_list, int len); void verify_all_options(GHashTable * options, pe_cluster_option * option_list, int len); gboolean check_time(const char *value); gboolean check_timer(const char *value); gboolean check_boolean(const char *value); gboolean check_number(const char *value); /* Shared PE/crmd functionality */ void filter_action_parameters(xmlNode * param_set, const char *version); void filter_reload_parameters(xmlNode * param_set, const char *restart_string); /* Resource operation updates */ xmlNode *create_operation_update(xmlNode * parent, lrmd_event_data_t *event, const char *caller_version, int target_rc, const char *origin, int level); /* char2score */ extern int node_score_red; extern int node_score_green; extern int node_score_yellow; extern int node_score_infinity; /* Assorted convenience functions */ static inline int crm_strlen_zero(const char *s) { return !s || *s == '\0'; } char *add_list_element(char *list, const char *value); char *generate_series_filename(const char *directory, const char *series, int sequence, gboolean bzip); int get_last_sequence(const char *directory, const char *series); void write_last_sequence(const char *directory, const char *series, int sequence, int max); int crm_pid_active(long pid); void crm_make_daemon(const char *name, gboolean daemonize, const char *pidfile); gboolean crm_is_writable(const char *dir, const char *file, const char *user, const char *group, gboolean need_both); char *generate_op_key(const char *rsc_id, const char *op_type, int interval); char *generate_notify_key(const char *rsc_id, const char *notify_type, const char *op_type); char *generate_transition_magic_v202(const char *transition_key, int op_status); char *generate_transition_magic(const char *transition_key, int op_status, int op_rc); char *generate_transition_key(int action, int transition_id, int target_rc, const char *node); static inline long long crm_clear_bit(const char *function, const char *target, long long word, long long bit) { long long rc = (word & ~bit); if(rc == word) { /* Unchanged */ } else if (target) { crm_trace("Bit 0x%.8llx for %s cleared by %s", bit, target, function); } else { crm_trace("Bit 0x%.8llx cleared by %s", bit, function); } return rc; } static inline long long crm_set_bit(const char *function, const char *target, long long word, long long bit) { long long rc = (word|bit); if(rc == word) { /* Unchanged */ } else if (target) { crm_trace("Bit 0x%.8llx for %s set by %s", bit, target, function); } else { crm_trace("Bit 0x%.8llx set by %s", bit, function); } return rc; } # define set_bit(word, bit) word = crm_set_bit(__PRETTY_FUNCTION__, NULL, word, bit) # define clear_bit(word, bit) word = crm_clear_bit(__PRETTY_FUNCTION__, NULL, word, bit) void g_hash_destroy_str(gpointer data); long long crm_int_helper(const char *text, char **end_text); char *crm_concat(const char *prefix, const char *suffix, char join); char *generate_hash_key(const char *crm_msg_reference, const char *sys); /*! remote tcp/tls helper functions */ gboolean crm_recv_remote_msg(void *session, char **recv_buf, gboolean encrypted, int total_timeout_ms, int *disconnected); char *crm_recv_remote_raw(void *data, gboolean encrypted, size_t max_recv, size_t *recv_len, int *disconnected); int crm_send_remote_msg(void *session, xmlNode * msg, gboolean encrypted); int crm_recv_remote_ready(void *session, gboolean encrypted, int timeout_ms); xmlNode *crm_parse_remote_buffer(char **msg_buf); int crm_remote_tcp_connect(const char *host, int port); #ifdef HAVE_GNUTLS_GNUTLS_H /*! * \internal * \brief Initiate the client handshake after establishing the tcp socket. * \note This is a blocking function, it will block until the entire handshake * is complete or until the timeout period is reached. * \retval 0 success * \retval negative, failure */ int crm_initiate_client_tls_handshake(void *session_data, int timeout_ms); /*! * \internal * \brief Create client or server session for anon DH encryption credentials * \param sock, the socket the session will use for transport * \param type, GNUTLS_SERVER or GNUTLS_CLIENT * \param credentials, gnutls_anon_server_credentials_t or gnutls_anon_client_credentials_t * * \retval gnutls_session * on success * \retval NULL on failure */ void *crm_create_anon_tls_session(int sock, int type, void *credentials); +/*! + * \internal + * \brief Create client or server session for PSK credentials + * \param sock, the socket the session will use for transport + * \param type, GNUTLS_SERVER or GNUTLS_CLIENT + * \param credentials, gnutls_psk_server_credentials_t or gnutls_osk_client_credentials_t + * + * \retval gnutls_session * on success + * \retval NULL on failure + */ +void *create_psk_tls_session(int csock, int type, void *credentials); #endif #define REMOTE_MSG_TERMINATOR "\r\n\r\n" const char *daemon_option(const char *option); void set_daemon_option(const char *option, const char *value); gboolean daemon_option_enabled(const char *daemon, const char *option); void strip_text_nodes(xmlNode *xml); # define crm_config_err(fmt...) { crm_config_error = TRUE; crm_err(fmt); } # define crm_config_warn(fmt...) { crm_config_warning = TRUE; crm_warn(fmt); } # define attrd_channel T_ATTRD # define F_ATTRD_KEY "attr_key" # define F_ATTRD_ATTRIBUTE "attr_name" # define F_ATTRD_TASK "task" # define F_ATTRD_VALUE "attr_value" # define F_ATTRD_SET "attr_set" # define F_ATTRD_SECTION "attr_section" # define F_ATTRD_DAMPEN "attr_dampening" # define F_ATTRD_IGNORE_LOCALLY "attr_ignore_locally" # define F_ATTRD_HOST "attr_host" # define F_ATTRD_USER "attr_user" # if SUPPORT_COROSYNC # if CS_USES_LIBQB # include # include typedef struct qb_ipc_request_header cs_ipc_header_request_t; typedef struct qb_ipc_response_header cs_ipc_header_response_t; # else # include # include # include static inline int qb_to_cs_error(int a) { return a; } typedef coroipc_request_header_t cs_ipc_header_request_t; typedef coroipc_response_header_t cs_ipc_header_response_t; # endif # else typedef struct { int size __attribute__ ((aligned(8))); int id __attribute__ ((aligned(8))); } __attribute__ ((aligned(8))) cs_ipc_header_request_t; typedef struct { int size __attribute__ ((aligned(8))); int id __attribute__ ((aligned(8))); int error __attribute__ ((aligned(8))); } __attribute__ ((aligned(8))) cs_ipc_header_response_t; # endif #endif /* CRM_INTERNAL__H */ diff --git a/lib/common/remote.c b/lib/common/remote.c index ae61481bc5..9f05a5e682 100644 --- a/lib/common/remote.c +++ b/lib/common/remote.c @@ -1,705 +1,735 @@ /* * 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 #include #include #ifdef HAVE_GNUTLS_GNUTLS_H # undef KEYFILE # include #endif #ifdef HAVE_GNUTLS_GNUTLS_H +const int psk_tls_kx_order[] = { + GNUTLS_KX_DHE_PSK, + GNUTLS_KX_PSK, +}; const int anon_tls_kx_order[] = { GNUTLS_KX_ANON_DH, GNUTLS_KX_DHE_RSA, GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, 0 }; int crm_initiate_client_tls_handshake(void *session_data, int timeout_ms) { int rc = 0; int pollrc = 0; time_t start = time(NULL); gnutls_session *session = session_data; do { rc = gnutls_handshake(*session); if (rc == GNUTLS_E_INTERRUPTED || rc == GNUTLS_E_AGAIN) { pollrc = crm_recv_remote_ready(session, TRUE, 1000); if (pollrc < 0) { /* poll returned error, there is no hope */ rc = -1; } } } while (((time(NULL) - start) < (timeout_ms/1000)) && (rc == GNUTLS_E_INTERRUPTED || rc == GNUTLS_E_AGAIN)); return rc; } void * crm_create_anon_tls_session(int csock, int type /* GNUTLS_SERVER, GNUTLS_CLIENT */, void *credentials) { 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, anon_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, (gnutls_anon_server_credentials_t) credentials); break; case GNUTLS_CLIENT: gnutls_credentials_set(*session, GNUTLS_CRD_ANON, (gnutls_anon_client_credentials_t) credentials); break; } return session; } +void * +create_psk_tls_session(int csock, int type /* GNUTLS_SERVER, GNUTLS_CLIENT */, void *credentials) +{ + gnutls_session *session = gnutls_malloc(sizeof(gnutls_session)); + + gnutls_init(session, type); +# ifdef HAVE_GNUTLS_PRIORITY_SET_DIRECT + gnutls_priority_set_direct(*session, "NORMAL:+DHE-PSK:+PSK", NULL); +# else + gnutls_set_default_priority(*session); + gnutls_kx_set_priority(*session, psk_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_PSK, (gnutls_psk_server_credentials_t) credentials); + break; + case GNUTLS_CLIENT: + gnutls_credentials_set(*session, GNUTLS_CRD_PSK, (gnutls_psk_client_credentials_t) credentials); + break; + } + + return session; +} + + static int crm_send_tls(gnutls_session * session, const char *buf, size_t len) { const char *unsent = buf; int rc = 0; int total_send; if (buf == NULL) { return -1; } total_send = len; crm_trace("Message size: %d", len); while (TRUE) { rc = gnutls_record_send(*session, unsent, len); if (rc == GNUTLS_E_INTERRUPTED || rc == GNUTLS_E_AGAIN) { crm_debug("Retry"); } else if (rc < 0) { crm_err("Connection terminated rc = %d", rc); break; } else if (rc < len) { crm_debug("Only sent %d of %d bytes", rc, len); len -= rc; unsent += rc; } else { crm_debug("Sent %d bytes", rc); break; } } return rc < 0 ? rc : total_send; } /*! * \internal * \brief Read bytes off non blocking tls session. * * \param session - tls session to read * \param max_size - max bytes allowed to read for buffer. 0 assumes no limit * * \note only use with NON-Blocking sockets. Should only be used after polling socket. * This function will return once max_size is met, the socket read buffer * is empty, or an error is encountered. * * \retval '\0' terminated buffer on success */ static char * crm_recv_tls(gnutls_session * session, size_t max_size, size_t *recv_len, int *disconnected) { char *buf = NULL; int rc = 0; size_t len = 0; size_t chunk_size = max_size ? max_size : 1024; size_t buf_size = 0; size_t read_size = 0; if (session == NULL) { if (disconnected) { *disconnected = 1; } goto done; } buf = calloc(1, chunk_size + 1); buf_size = chunk_size; while (TRUE) { read_size = buf_size - len; /* automatically grow the buffer when needed if max_size is not set.*/ if (!max_size && (read_size < (chunk_size / 2))) { buf_size += chunk_size; crm_trace("Grow buffer by %d more bytes. buf is now %d bytes", (int)chunk_size, buf_size); buf = realloc(buf, buf_size + 1); CRM_ASSERT(buf != NULL); read_size = buf_size - len; } rc = gnutls_record_recv(*session, buf + len, read_size); if (rc > 0) { crm_trace("Got %d more bytes.", rc); len += rc; /* always null terminate buffer, the +1 to alloc always allows for this.*/ buf[len] = '\0'; } if (max_size && (max_size == read_size)) { crm_trace("Buffer max read size %d met" , max_size); goto done; } /* process any errors. */ if (rc == GNUTLS_E_INTERRUPTED) { crm_trace("EINTR encoutered, retry tls read"); } else if (rc == GNUTLS_E_AGAIN) { crm_trace("non-blocking, exiting read on rc = %d", rc); goto done; } else if (rc <= 0) { if (rc == 0) { crm_debug("EOF encoutered during TLS read"); } else { crm_debug("Error receiving message: %s (%d)", gnutls_strerror(rc), rc); } if (disconnected) { *disconnected = 1; } goto done; } } done: if (recv_len) { *recv_len = len; } if (!len) { free(buf); buf = NULL; } return buf; } #endif static int crm_send_plaintext(int sock, const char *buf, size_t len) { int rc = 0; const char *unsent = buf; int total_send; if (buf == NULL) { return -1; } total_send = len; crm_trace("Message on socket %d: size=%d", sock, len); retry: rc = write(sock, unsent, len); if (rc < 0) { switch (errno) { case EINTR: case EAGAIN: crm_trace("Retry"); goto retry; default: crm_perror(LOG_ERR, "Could only write %d of the remaining %d bytes", rc, (int) len); break; } } else if (rc < len) { crm_trace("Only sent %d of %d remaining bytes", rc, len); len -= rc; unsent += rc; goto retry; } else { crm_trace("Sent %d bytes: %.100s", rc, buf); } return rc < 0 ? rc : total_send; } /*! * \internal * \brief Read bytes off non blocking socket. * * \param session - tls session to read * \param max_size - max bytes allowed to read for buffer. 0 assumes no limit * * \note only use with NON-Blocking sockets. Should only be used after polling socket. * This function will return once max_size is met, the socket read buffer * is empty, or an error is encountered. * * \retval '\0' terminated buffer on success */ static char * crm_recv_plaintext(int sock, size_t max_size, size_t *recv_len, int *disconnected) { char *buf = NULL; ssize_t rc = 0; ssize_t len = 0; ssize_t chunk_size = max_size ? max_size : 1024; size_t buf_size = 0; size_t read_size = 0; if (sock <= 0) { if (disconnected) { *disconnected = 1; } goto done; } buf = calloc(1, chunk_size + 1); buf_size = chunk_size; while (TRUE) { errno = 0; read_size = buf_size - len; /* automatically grow the buffer when needed if max_size is not set.*/ if (!max_size && (read_size < (chunk_size / 2))) { buf_size += chunk_size; crm_trace("Grow buffer by %d more bytes. buf is now %d bytes", (int)chunk_size, buf_size); buf = realloc(buf, buf_size + 1); CRM_ASSERT(buf != NULL); read_size = buf_size - len; } rc = read(sock, buf + len, chunk_size); if (rc > 0) { crm_trace("Got %d more bytes. errno=%d", (int)rc, errno); len += rc; /* always null terminate buffer, the +1 to alloc always allows for this.*/ buf[len] = '\0'; } if (max_size && (max_size == read_size)) { crm_trace("Buffer max read size %d met" , max_size); goto done; } if (rc > 0) { continue; } else if (rc == 0) { if (disconnected) { *disconnected = 1; } crm_trace("EOF encoutered during read"); goto done; } /* process errors */ if (errno == EINTR) { crm_trace("EINTER encoutered, retry socket read."); } else if (errno == EAGAIN) { crm_trace("non-blocking, exiting read on rc = %d", rc); goto done; } else if (errno <= 0) { if (disconnected) { *disconnected = 1; } crm_debug("Error receiving message: %d", (int)rc); goto done; } } done: if (recv_len) { *recv_len = len; } if (!len) { free(buf); buf = NULL; } return buf; } static int crm_send_remote_msg_raw(void *session, const char *buf, size_t len, gboolean encrypted) { int rc = -1; if (encrypted) { #ifdef HAVE_GNUTLS_GNUTLS_H rc = crm_send_tls(session, buf, len); #else CRM_ASSERT(encrypted == FALSE); #endif } else { rc = crm_send_plaintext(GPOINTER_TO_INT(session), buf, len); } return rc; } int crm_send_remote_msg(void *session, xmlNode * msg, gboolean encrypted) { int rc = -1; char *xml_text = NULL; int len = 0; xml_text = dump_xml_unformatted(msg); if (xml_text) { len = strlen(xml_text); } else { crm_err("Invalid XML, can not send msg"); return -1; } rc = crm_send_remote_msg_raw(session, xml_text, len, encrypted); if (rc < 0) { goto done; } rc = crm_send_remote_msg_raw(session, REMOTE_MSG_TERMINATOR, strlen(REMOTE_MSG_TERMINATOR), encrypted); done: if (rc < 0) { crm_err("Failed to send remote msg, rc = %d", rc); } free(xml_text); return rc; } /*! * \internal * \brief handles the recv buffer and parsing out msgs. * \note new_data is owned by this function once it is passed in. */ xmlNode * crm_parse_remote_buffer(char **msg_buf) { char *buf = NULL; char *start = NULL; char *end = NULL; xmlNode *xml = NULL; if (*msg_buf == NULL) { return NULL; } /* take ownership of the buffer */ buf = *msg_buf; *msg_buf = NULL; /* MSGS are separated by a '\r\n\r\n'. Split a message off the buffer and return it. */ start = buf; end = strstr(start, REMOTE_MSG_TERMINATOR); while (!xml && end) { /* grab the message */ end[0] = '\0'; end += strlen(REMOTE_MSG_TERMINATOR); xml = string2xml(start); if (xml == NULL) { crm_err("Couldn't parse: '%.120s'", start); } start = end; end = strstr(start, REMOTE_MSG_TERMINATOR); } if (xml && start) { /* we have msgs left over, save it until next time */ *msg_buf = strdup(start); free(buf); } else if (!xml) { /* no msg present */ *msg_buf = buf; } return xml; } /*! * \internal * \brief Determine if a remote session has data to read * * \retval 0, timeout occured. * \retval positive, data is ready to be read * \retval negative, session has ended */ int crm_recv_remote_ready(void *session, gboolean encrypted, int timeout /* ms */) { struct pollfd fds = { 0, }; int sock = 0; void *sock_ptr = NULL; int rc = 0; time_t start; if (encrypted) { #ifdef HAVE_GNUTLS_GNUTLS_H gnutls_session *tls_session = session; sock_ptr = gnutls_transport_get_ptr(*tls_session); #else CRM_ASSERT(encrypted == FALSE); #endif } else { sock_ptr = session; } sock = GPOINTER_TO_INT(sock_ptr); if (sock <= 0) { return -ENOTCONN; } start = time(NULL); errno = 0; do { fds.fd = sock; fds.events = POLLIN; /* If we got an EINTR while polling, and we have a * specific timeout we are trying to honor, attempt * to adjust the timeout to the closest second. */ if (errno == EINTR && (timeout > 0)) { timeout = timeout - ((time(NULL) - start) * 1000); if (timeout < 1000) { timeout = 1000; } } rc = poll(&fds, 1, timeout); } while (rc < 0 && errno == EINTR); return rc; } char * crm_recv_remote_raw(void *session, gboolean encrypted, size_t max_recv, size_t *recv_len, int *disconnected) { char *reply = NULL; if (recv_len) { *recv_len = 0; } if (disconnected) { *disconnected = 0; } if (encrypted) { #ifdef HAVE_GNUTLS_GNUTLS_H reply = crm_recv_tls(session, max_recv, recv_len, disconnected); #else CRM_ASSERT(encrypted == FALSE); #endif } else { reply = crm_recv_plaintext(GPOINTER_TO_INT(session), max_recv, recv_len, disconnected); } if (reply == NULL || strlen(reply) == 0) { crm_trace("Empty reply"); } return reply; } /*! * \internal * \brief Read data off the socket until at least one full message is present or timeout occures. * \retval TRUE message read * \retval FALSE full message not read */ gboolean crm_recv_remote_msg(void *session, char **recv_buf, gboolean encrypted, int total_timeout /*ms */, int *disconnected) { int ret; size_t request_len = 0; time_t start = time(NULL); char *raw_request = NULL; int remaining_timeout = 0; if (total_timeout == 0) { total_timeout = 10000; } else if (total_timeout < 0) { total_timeout = 60000; } *disconnected = 0; remaining_timeout = total_timeout; while ((remaining_timeout > 0) && !(*disconnected)) { /* read some more off the tls buffer if we still have time left. */ crm_trace("waiting to receive remote msg, starting timeout %d, remaining_timeout %d", total_timeout, remaining_timeout); ret = crm_recv_remote_ready(session, encrypted, remaining_timeout); raw_request = NULL; if (ret == 0) { crm_err("poll timed out (%d ms) while waiting to receive msg", remaining_timeout); return FALSE; } else if (ret < 0) { if (errno != EINTR) { crm_debug("poll returned error while waiting for msg, rc: %d, errno: %d", ret, errno); *disconnected = 1; return FALSE; } crm_debug("poll EINTR encountered during poll, retrying"); } else { raw_request = crm_recv_remote_raw(session, encrypted, 0, &request_len, disconnected); } remaining_timeout = remaining_timeout - ((time(NULL) - start) * 1000); if (!raw_request) { crm_debug("Empty msg received after poll"); continue; } if (*recv_buf) { int old_len = strlen(*recv_buf); crm_trace("Expanding recv buffer from %d to %d", old_len, old_len+request_len); *recv_buf = realloc(*recv_buf, old_len + request_len + 1); memcpy(*recv_buf + old_len, raw_request, request_len); *(*recv_buf+old_len+request_len) = '\0'; free(raw_request); } else { *recv_buf = raw_request; } if (strstr(*recv_buf, REMOTE_MSG_TERMINATOR)) { return TRUE; } } return FALSE; } /*! * \internal * \brief tcp connection to server at specified port * \retval positive, socket fd. * \retval negative, failed to connect. */ int crm_remote_tcp_connect(const char *host, int port) { struct addrinfo *res; struct addrinfo *rp; struct addrinfo hints; const char *server = host; int ret_ga; int sock; /* getaddrinfo */ memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_CANONNAME; crm_debug("Looking up %s", server); ret_ga = getaddrinfo(server, NULL, &hints, &res); if (ret_ga) { crm_err("getaddrinfo: %s", gai_strerror(ret_ga)); return -1; } if (!res || !res->ai_addr) { crm_err("getaddrinfo failed"); return -1; } for (rp = res; rp != NULL; rp = rp->ai_next) { struct sockaddr *addr = rp->ai_addr; int flag = 0; if (!addr) { continue; } if (rp->ai_canonname) { server = res->ai_canonname; } crm_debug("Got address %s for %s", server, host); /* create socket */ sock = socket(rp->ai_family, SOCK_STREAM, IPPROTO_TCP); if (sock == -1) { crm_err("Socket creation failed for remote client connection."); continue; } if (addr->sa_family == AF_INET6) { struct sockaddr_in6 *addr_in = (struct sockaddr_in6 *) addr; addr_in->sin6_port = htons(port); } else { struct sockaddr_in *addr_in = (struct sockaddr_in *) addr; addr_in->sin_port = htons(port); crm_info("Attempting to connect to remote server at %s:%d", inet_ntoa(addr_in->sin_addr), port); } if (connect(sock, rp->ai_addr, rp->ai_addrlen) == 0) { if ((flag = fcntl(sock, F_GETFL)) >= 0) { if (fcntl(sock, F_SETFL, flag | O_NONBLOCK) < 0) { crm_err( "fcntl() write failed"); close(sock); sock = -1; continue; } } break; /* Success */ } close(sock); sock = -1; } freeaddrinfo(res); return sock; }