|
|
@ -14,12 +14,15 @@ Contributors:
|
|
|
|
Roger Light - initial implementation and documentation.
|
|
|
|
Roger Light - initial implementation and documentation.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <string.h>
|
|
|
|
#ifndef WIN32
|
|
|
|
#ifndef WIN32
|
|
|
|
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#include <netdb.h>
|
|
|
|
#include <netdb.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <unistd.h>
|
|
|
@ -268,6 +271,88 @@ static unsigned int psk_client_callback(SSL *ssl, const char *hint,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if defined(WITH_BROKER) && defined(__linux__)
|
|
|
|
|
|
|
|
/* Async connect, part 1 (dns lookup) */
|
|
|
|
|
|
|
|
int _mosquitto_try_connect_step1(struct mosquitto *mosq, const char *host)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
int s;
|
|
|
|
|
|
|
|
void *sevp = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(mosq->adns){
|
|
|
|
|
|
|
|
_mosquitto_free(mosq->adns);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
mosq->adns = _mosquitto_calloc(1, sizeof(struct gaicb));
|
|
|
|
|
|
|
|
if(!mosq->adns){
|
|
|
|
|
|
|
|
return MOSQ_ERR_NOMEM;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
mosq->adns->ar_name = host;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
s = getaddrinfo_a(GAI_NOWAIT, &mosq->adns, 1, sevp);
|
|
|
|
|
|
|
|
if(s){
|
|
|
|
|
|
|
|
errno = s;
|
|
|
|
|
|
|
|
return MOSQ_ERR_EAI;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return MOSQ_ERR_SUCCESS;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Async connect part 2, the connection. */
|
|
|
|
|
|
|
|
int _mosquitto_try_connect_step2(struct mosquitto *mosq, uint16_t port, mosq_sock_t *sock)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
struct addrinfo *ainfo, *rp;
|
|
|
|
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ainfo = mosq->adns->ar_result;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for(rp = ainfo; rp != NULL; rp = rp->ai_next){
|
|
|
|
|
|
|
|
*sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
|
|
|
|
|
|
|
if(*sock == INVALID_SOCKET) continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(rp->ai_family == PF_INET){
|
|
|
|
|
|
|
|
((struct sockaddr_in *)rp->ai_addr)->sin_port = htons(port);
|
|
|
|
|
|
|
|
}else if(rp->ai_family == PF_INET6){
|
|
|
|
|
|
|
|
((struct sockaddr_in6 *)rp->ai_addr)->sin6_port = htons(port);
|
|
|
|
|
|
|
|
}else{
|
|
|
|
|
|
|
|
COMPAT_CLOSE(*sock);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Set non-blocking */
|
|
|
|
|
|
|
|
if(_mosquitto_socket_nonblock(*sock)){
|
|
|
|
|
|
|
|
COMPAT_CLOSE(*sock);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rc = connect(*sock, rp->ai_addr, rp->ai_addrlen);
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
|
|
|
|
errno = WSAGetLastError();
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if(rc == 0 || errno == EINPROGRESS || errno == COMPAT_EWOULDBLOCK){
|
|
|
|
|
|
|
|
if(rc < 0 && (errno == EINPROGRESS || errno == COMPAT_EWOULDBLOCK)){
|
|
|
|
|
|
|
|
rc = MOSQ_ERR_CONN_PENDING;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Set non-blocking */
|
|
|
|
|
|
|
|
if(_mosquitto_socket_nonblock(*sock)){
|
|
|
|
|
|
|
|
COMPAT_CLOSE(*sock);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
COMPAT_CLOSE(*sock);
|
|
|
|
|
|
|
|
*sock = INVALID_SOCKET;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
freeaddrinfo(ainfo);
|
|
|
|
|
|
|
|
if(!rp){
|
|
|
|
|
|
|
|
return MOSQ_ERR_ERRNO;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int _mosquitto_try_connect(struct mosquitto *mosq, const char *host, uint16_t port, mosq_sock_t *sock, const char *bind_address, bool blocking)
|
|
|
|
int _mosquitto_try_connect(struct mosquitto *mosq, const char *host, uint16_t port, mosq_sock_t *sock, const char *bind_address, bool blocking)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
struct addrinfo hints;
|
|
|
|
struct addrinfo hints;
|
|
|
@ -396,24 +481,13 @@ int mosquitto__socket_connect_tls(struct mosquitto *mosq)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
/* Create a socket and connect it to 'ip' on port 'port'.
|
|
|
|
int _mosquitto_socket_connect_step3(struct mosquitto *mosq, const char *host, uint16_t port, const char *bind_address, bool blocking)
|
|
|
|
* Returns -1 on failure (ip is NULL, socket creation/connection error)
|
|
|
|
|
|
|
|
* Returns sock number on success.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
int _mosquitto_socket_connect(struct mosquitto *mosq, const char *host, uint16_t port, const char *bind_address, bool blocking)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
mosq_sock_t sock = INVALID_SOCKET;
|
|
|
|
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
#ifdef WITH_TLS
|
|
|
|
#ifdef WITH_TLS
|
|
|
|
int ret;
|
|
|
|
int ret;
|
|
|
|
BIO *bio;
|
|
|
|
BIO *bio;
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
if(!mosq || !host || !port) return MOSQ_ERR_INVAL;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rc = _mosquitto_try_connect(mosq, host, port, &sock, bind_address, blocking);
|
|
|
|
|
|
|
|
if(rc > 0) return rc;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef WITH_TLS
|
|
|
|
#ifdef WITH_TLS
|
|
|
|
if(mosq->tls_cafile || mosq->tls_capath || mosq->tls_psk){
|
|
|
|
if(mosq->tls_cafile || mosq->tls_capath || mosq->tls_psk){
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
|
|
|
@ -425,7 +499,7 @@ int _mosquitto_socket_connect(struct mosquitto *mosq, const char *host, uint16_t
|
|
|
|
mosq->ssl_ctx = SSL_CTX_new(TLSv1_client_method());
|
|
|
|
mosq->ssl_ctx = SSL_CTX_new(TLSv1_client_method());
|
|
|
|
}else{
|
|
|
|
}else{
|
|
|
|
_mosquitto_log_printf(mosq, MOSQ_LOG_ERR, "Error: Protocol %s not supported.", mosq->tls_version);
|
|
|
|
_mosquitto_log_printf(mosq, MOSQ_LOG_ERR, "Error: Protocol %s not supported.", mosq->tls_version);
|
|
|
|
COMPAT_CLOSE(sock);
|
|
|
|
COMPAT_CLOSE(mosq->sock);
|
|
|
|
return MOSQ_ERR_INVAL;
|
|
|
|
return MOSQ_ERR_INVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#else
|
|
|
@ -433,13 +507,13 @@ int _mosquitto_socket_connect(struct mosquitto *mosq, const char *host, uint16_t
|
|
|
|
mosq->ssl_ctx = SSL_CTX_new(TLSv1_client_method());
|
|
|
|
mosq->ssl_ctx = SSL_CTX_new(TLSv1_client_method());
|
|
|
|
}else{
|
|
|
|
}else{
|
|
|
|
_mosquitto_log_printf(mosq, MOSQ_LOG_ERR, "Error: Protocol %s not supported.", mosq->tls_version);
|
|
|
|
_mosquitto_log_printf(mosq, MOSQ_LOG_ERR, "Error: Protocol %s not supported.", mosq->tls_version);
|
|
|
|
COMPAT_CLOSE(sock);
|
|
|
|
COMPAT_CLOSE(mosq->sock);
|
|
|
|
return MOSQ_ERR_INVAL;
|
|
|
|
return MOSQ_ERR_INVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
if(!mosq->ssl_ctx){
|
|
|
|
if(!mosq->ssl_ctx){
|
|
|
|
_mosquitto_log_printf(mosq, MOSQ_LOG_ERR, "Error: Unable to create TLS context.");
|
|
|
|
_mosquitto_log_printf(mosq, MOSQ_LOG_ERR, "Error: Unable to create TLS context.");
|
|
|
|
COMPAT_CLOSE(sock);
|
|
|
|
COMPAT_CLOSE(mosq->sock);
|
|
|
|
return MOSQ_ERR_TLS;
|
|
|
|
return MOSQ_ERR_TLS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -456,7 +530,7 @@ int _mosquitto_socket_connect(struct mosquitto *mosq, const char *host, uint16_t
|
|
|
|
ret = SSL_CTX_set_cipher_list(mosq->ssl_ctx, mosq->tls_ciphers);
|
|
|
|
ret = SSL_CTX_set_cipher_list(mosq->ssl_ctx, mosq->tls_ciphers);
|
|
|
|
if(ret == 0){
|
|
|
|
if(ret == 0){
|
|
|
|
_mosquitto_log_printf(mosq, MOSQ_LOG_ERR, "Error: Unable to set TLS ciphers. Check cipher list \"%s\".", mosq->tls_ciphers);
|
|
|
|
_mosquitto_log_printf(mosq, MOSQ_LOG_ERR, "Error: Unable to set TLS ciphers. Check cipher list \"%s\".", mosq->tls_ciphers);
|
|
|
|
COMPAT_CLOSE(sock);
|
|
|
|
COMPAT_CLOSE(mosq->sock);
|
|
|
|
return MOSQ_ERR_TLS;
|
|
|
|
return MOSQ_ERR_TLS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -480,7 +554,7 @@ int _mosquitto_socket_connect(struct mosquitto *mosq, const char *host, uint16_t
|
|
|
|
_mosquitto_log_printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check capath \"%s\".", mosq->tls_capath);
|
|
|
|
_mosquitto_log_printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check capath \"%s\".", mosq->tls_capath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
COMPAT_CLOSE(sock);
|
|
|
|
COMPAT_CLOSE(mosq->sock);
|
|
|
|
return MOSQ_ERR_TLS;
|
|
|
|
return MOSQ_ERR_TLS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(mosq->tls_cert_reqs == 0){
|
|
|
|
if(mosq->tls_cert_reqs == 0){
|
|
|
@ -502,7 +576,7 @@ int _mosquitto_socket_connect(struct mosquitto *mosq, const char *host, uint16_t
|
|
|
|
#else
|
|
|
|
#else
|
|
|
|
_mosquitto_log_printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load client certificate \"%s\".", mosq->tls_certfile);
|
|
|
|
_mosquitto_log_printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load client certificate \"%s\".", mosq->tls_certfile);
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
COMPAT_CLOSE(sock);
|
|
|
|
COMPAT_CLOSE(mosq->sock);
|
|
|
|
return MOSQ_ERR_TLS;
|
|
|
|
return MOSQ_ERR_TLS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -514,13 +588,13 @@ int _mosquitto_socket_connect(struct mosquitto *mosq, const char *host, uint16_t
|
|
|
|
#else
|
|
|
|
#else
|
|
|
|
_mosquitto_log_printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load client key file \"%s\".", mosq->tls_keyfile);
|
|
|
|
_mosquitto_log_printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load client key file \"%s\".", mosq->tls_keyfile);
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
COMPAT_CLOSE(sock);
|
|
|
|
COMPAT_CLOSE(mosq->sock);
|
|
|
|
return MOSQ_ERR_TLS;
|
|
|
|
return MOSQ_ERR_TLS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ret = SSL_CTX_check_private_key(mosq->ssl_ctx);
|
|
|
|
ret = SSL_CTX_check_private_key(mosq->ssl_ctx);
|
|
|
|
if(ret != 1){
|
|
|
|
if(ret != 1){
|
|
|
|
_mosquitto_log_printf(mosq, MOSQ_LOG_ERR, "Error: Client certificate/key are inconsistent.");
|
|
|
|
_mosquitto_log_printf(mosq, MOSQ_LOG_ERR, "Error: Client certificate/key are inconsistent.");
|
|
|
|
COMPAT_CLOSE(sock);
|
|
|
|
COMPAT_CLOSE(mosq->sock);
|
|
|
|
return MOSQ_ERR_TLS;
|
|
|
|
return MOSQ_ERR_TLS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -532,27 +606,43 @@ int _mosquitto_socket_connect(struct mosquitto *mosq, const char *host, uint16_t
|
|
|
|
|
|
|
|
|
|
|
|
mosq->ssl = SSL_new(mosq->ssl_ctx);
|
|
|
|
mosq->ssl = SSL_new(mosq->ssl_ctx);
|
|
|
|
if(!mosq->ssl){
|
|
|
|
if(!mosq->ssl){
|
|
|
|
COMPAT_CLOSE(sock);
|
|
|
|
COMPAT_CLOSE(mosq->sock);
|
|
|
|
return MOSQ_ERR_TLS;
|
|
|
|
return MOSQ_ERR_TLS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SSL_set_ex_data(mosq->ssl, tls_ex_index_mosq, mosq);
|
|
|
|
SSL_set_ex_data(mosq->ssl, tls_ex_index_mosq, mosq);
|
|
|
|
bio = BIO_new_socket(sock, BIO_NOCLOSE);
|
|
|
|
bio = BIO_new_socket(mosq->sock, BIO_NOCLOSE);
|
|
|
|
if(!bio){
|
|
|
|
if(!bio){
|
|
|
|
COMPAT_CLOSE(sock);
|
|
|
|
COMPAT_CLOSE(mosq->sock);
|
|
|
|
return MOSQ_ERR_TLS;
|
|
|
|
return MOSQ_ERR_TLS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SSL_set_bio(mosq->ssl, bio, bio);
|
|
|
|
SSL_set_bio(mosq->ssl, bio, bio);
|
|
|
|
|
|
|
|
|
|
|
|
mosq->sock = sock;
|
|
|
|
|
|
|
|
if(mosquitto__socket_connect_tls(mosq)){
|
|
|
|
if(mosquitto__socket_connect_tls(mosq)){
|
|
|
|
return MOSQ_ERR_TLS;
|
|
|
|
return MOSQ_ERR_TLS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return MOSQ_ERR_SUCCESS;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Create a socket and connect it to 'ip' on port 'port'.
|
|
|
|
|
|
|
|
* Returns -1 on failure (ip is NULL, socket creation/connection error)
|
|
|
|
|
|
|
|
* Returns sock number on success.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
int _mosquitto_socket_connect(struct mosquitto *mosq, const char *host, uint16_t port, const char *bind_address, bool blocking)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
mosq_sock_t sock = INVALID_SOCKET;
|
|
|
|
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(!mosq || !host || !port) return MOSQ_ERR_INVAL;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rc = _mosquitto_try_connect(mosq, host, port, &sock, bind_address, blocking);
|
|
|
|
|
|
|
|
if(rc > 0) return rc;
|
|
|
|
|
|
|
|
|
|
|
|
mosq->sock = sock;
|
|
|
|
mosq->sock = sock;
|
|
|
|
|
|
|
|
rc = _mosquitto_socket_connect_step3(mosq, host, port, bind_address, blocking);
|
|
|
|
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|