HTTPS client POST/PUT to azure IOT hub failing at socket connect()

The goal is to push a blob to a azure storage container using https on nRF9160. 

I am using the nRF/zephyr  socket library to do basic HTTPS POST/PUT

CONFIG_NET_SOCKETS_OFFLOAD_TLS is set to "n" meaning we are not using the certificates in the modem. 
Instead add the public and private certificates (signed by CA root) to the TLS using 
tls_credential_add(...)
While the same HTTPS requests work on a Postman together with the generated certificates. I am able to upload and append to a storage container using the same PUT/POST methods on postman. 
But doing the same using the socket library fails. I get a "connect() failed : err: 111" error which is a "connection refused" error. 

Here is the core part of the HTTP request for blob URI. 
// A https request using sockets. Derived from https_client sample from nordic.

int send_http_blob_req(const char* blobname)
{
{
	int err;
	int fd;
	char *p;
	int bytes;
	size_t off;
	struct addrinfo *res;
	struct addrinfo hints = {
		.ai_family = AF_INET,
		.ai_socktype = SOCK_STREAM,
	};

        printk("Requesting URI for \"%s\"\r\n", blobname);
        create_request_header(http_header, sizeof(http_header), HOST, DEVICE_NAME, blobname);

	err = getaddrinfo(HOST, NULL, &hints, &res);
	if (err) {
		printk("getaddrinfo() failed, err %d\n", errno);
		return err;
	}

	((struct sockaddr_in *)res->ai_addr)->sin_port = htons(HTTPS_PORT);

	fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TLS_1_2);
	if (fd == -1) {
		printk("Failed to open socket!\n");
		goto clean_up;
	}

	/* Setup TLS socket options */
	err = tls_setup(fd, HOST);
	if (err) {
		goto clean_up;
	}

	printk("Connecting to %s\n", HOST);
	err = connect(fd, res->ai_addr, sizeof(struct sockaddr_in));
	if (err) {
		printk("connect() failed, err: %d\n", errno);
		goto clean_up;
	}

	off = 0;
	do {
		bytes = send(fd, &http_header[off], strlen(http_header) - off, 0);
		if (bytes < 0) {
			printk("send() failed, err %d\n", errno);
			goto clean_up;
		}
		off += bytes;
	} while (off < strlen(http_header));

	printk("Sent %d bytes\n", off);

	off = 0;
	do {
		bytes = recv(fd, &recv_buf[off], RECV_BUF_SIZE - off, 0);
		if (bytes < 0) {
			printk("recv() failed, err %d\n", errno);
			goto clean_up;
		}
		off += bytes;
	} while (bytes != 0 /* peer closed connection */);

        // parse sasToken and correlationId
        cJSON* root = cJSON_Parse(strchr(recv_buf, '{'));

        // todo: handle failed request
        strncpy(correlationId, cJSON_GetObjectItem(root, "correlationId")->valuestring, 256);
        strncpy(sasToken, cJSON_GetObjectItem(root, "sasToken")->valuestring, 256);
        strncpy(storageHost, cJSON_GetObjectItem(root, "hostName")->valuestring, 256);
        strncpy(storageContainer, cJSON_GetObjectItem(root, "containerName")->valuestring, 64);
        strncpy(blobName, cJSON_GetObjectItem(root, "blobName")->valuestring, 64);

        printk("correlationId=%s\r\n", correlationId);
        printk("sasToken=%s\r\n", sasToken);
        printk("hostName=%s\r\n", storageHost);
        printk("containerName=%s\r\n", storageContainer);
        printk("blobName=%s\r\n", blobName);

        cJSON_Delete(root);

	printk("Received %d bytes\n", off);

	/* Print HTTP response */
	p = strstr(recv_buf, "\r\n");
	if (p) {
		off = p - recv_buf;
		recv_buf[off + 1] = '\0';
		printk("\n>\t %s\n\n", recv_buf);
	}

	printk("Finished, closing socket.\n");

clean_up:
	freeaddrinfo(res);
	(void)close(fd);

        return 0;
}

void create_request_header(char* buf, size_t bufsize, const char* hostname, 
  const char* devicename, const char* blobname)
{
  int content_length = 15 + strlen(blobname);

  snprintf(buf, bufsize, 
    "POST /devices/%s/files?api-version=2018-06-30 HTTP/1.1\r\n"
    "Host: %s\r\n"
    "Accept: application/json\r\n"
    "Content-Type: application/json\r\n"
    "Connection: close\r\n"
    "Content-Length: %d\r\n"
    "\r\n"
    "{\"blobName\":\"%s\"}",
    devicename, hostname, content_length, blobname);
}
What am I doing wrong? What could be the cause for connect() err:111? 

I am using ncs version1.8.0.
Parents Reply Children
  • Hi, 

    I am using the Baltimore CyberTrust Root CA from here 

    www.digicert.com/.../digicert-root-certificates.html

    and the domain is xxxx.azure-devices.net

    Should I use a different root CA? may be the one generated during step 2 of the process here

    github.com/.../CACertificateOverview.md

    These are the steps I use to generate certificates (Public and private) for the azure IOT

    So I use the Baltimore CA and the certificates generated from above steps to setup TLS.  

    Do you see any issue with this?

  • Hi,

     

    Bhargav Kinnal said:
    I am using the Baltimore CyberTrust Root CA

    That shall be the correct one for azure.

     

    Could you try to use the azure iot hub example that we have in the sdk to see if this works? This uses the modems TLS stack by default.

    https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/samples/nrf9160/azure_iot_hub/README.html

     

    Kind regards,

    Håkon

  • Hi, 

    Thanks for your suggestion. 

    I did try the sample for azure IOT hub. And it works completely fine. 

    I did load the provisioned certificates into the modem. So that is different than how I set it up. 

    Does the Azure IOT sample library still use the same socket library for HTTP/MQTT communication?

    If so, what could I be missing? From what I see the certificates are just fine with the azure_iot_hub sample

  • Hi,

     

    I do not know how you've configured and setup mbedTLS in the application space, but https_client has a configuration overlay file that shows an example of this:

    https://github.com/nrfconnect/sdk-nrf/blob/v2.0.2/samples/nrf9160/https_client/overlay-tfm_mbedtls.conf

     

    In addition, you should enable SNI and SSL renegotiation:

    CONFIG_MBEDTLS_SSL_RENEGOTIATION=y

    CONFIG_MBEDTLS_SSL_SERVER_NAME_INDICATION=y

     

    Could you check your local configuration to see if these are enabled on your side?

     

    Kind regards,

    Håkon

  • Hi Håkon, 

    I double checked the config to the overlay you shared. It looks similar to me. 

    The SSL and SNI renegotiation are enabled. 

    The connect() now works. But the recv() function receives no response. 

    I have attached the TLS setup I do below. 

    int cert_provision()
    {
    	err = tls_credential_add(TLS_SEC_TAG, TLS_CREDENTIAL_PRIVATE_KEY,
    				 private_key, sizeof(private_key));
    	if (err) {
    		return err;
    	}
    
    	err = tls_credential_add(TLS_SEC_TAG, TLS_CREDENTIAL_SERVER_CERTIFICATE,
    				 client_cert, sizeof(client_cert));
    	if (err) {
    		return err;
    	}
    
    	return tls_credential_add(TLS_SEC_TAG, TLS_CREDENTIAL_CA_CERTIFICATE,
    				  cert, sizeof(cert));
    }
    
    
    /* Setup TLS options on a given socket */
    int tls_setup(int fd, const char* hostname)
    {
    	int err;
    	int verify;
    
    	/* Security tag that we have provisioned the certificate with */
    	const sec_tag_t tls_sec_tag[] = {
    		TLS_SEC_TAG,
    	};
    
    	/* Set up TLS peer verification */
    	enum {
    		NONE = 0,
    		OPTIONAL = 1,
    		REQUIRED = 2,
    	};
    
    	verify = REQUIRED;
    
    	err = setsockopt(fd, SOL_TLS, TLS_PEER_VERIFY, &verify, sizeof(verify));
    	if (err) {
    		printk("Failed to setup peer verification, err %d\n", errno);
    		return err;
    	}
    
    	/* Associate the socket with the security tag
    	 * we have provisioned the certificate with.
    	 */
    	err = setsockopt(fd, SOL_TLS, TLS_SEC_TAG_LIST, tls_sec_tag,
    			 sizeof(tls_sec_tag));
    	if (err) {
    		printk("Failed to setup TLS sec tag, err %d\n", errno);
    		return err;
    	}
    
    	err = setsockopt(fd, SOL_TLS, TLS_HOSTNAME, hostname, strlen(hostname));
    	if (err) {
    		 printk("Failed to setup TLS host name, err %d\n", errno);
    		 return err;
    	}
    
    	return 0;
    }

    CONFIG_NRF_MODEM_LIB=y
    #CONFIG_NRF_MODEM_LIB_SYS_INIT=n
    
    CONFIG_NETWORKING=y
    CONFIG_NET_SOCKETS=y
    CONFIG_NET_SOCKETS_POSIX_NAMES=y
    CONFIG_NET_NATIVE=n
    
    CONFIG_HEAP_MEM_POOL_SIZE=4096
    CONFIG_MAIN_STACK_SIZE=4096
    
    CONFIG_MODEM_KEY_MGMT=y
    CONFIG_LTE_LINK_CONTROL=y
    CONFIG_LTE_AUTO_INIT_AND_CONNECT=n
    
    CONFIG_NEWLIB_LIBC=y
    CONFIG_CJSON_LIB=y
    
    CONFIG_ASSERT=y
    CONFIG_RING_BUFFER=y
    
    ######################
    
    
    CONFIG_NET_SOCKETS_OFFLOAD_TLS=n
    CONFIG_NET_SOCKETS_SOCKOPT_TLS=y
    
    CONFIG_MBEDTLS=y
    CONFIG_MBEDTLS_LIBRARY=y
    CONFIG_MBEDTLS_ENABLE_HEAP=y
    CONFIG_MBEDTLS_HEAP_SIZE=65536
    CONFIG_MBEDTLS_TLS_LIBRARY=y
    CONFIG_MBEDTLS_X509_LIBRARY=y
    CONFIG_MBEDTLS_PKCS1_V15=y
    CONFIG_MBEDTLS_SSL_RENEGOTIATION=y
    CONFIG_MBEDTLS_SSL_SERVER_NAME_INDICATION=y
    CONFIG_MBEDTLS_INSTALL_PATH="DUMMY"
    
    CONFIG_NORDIC_SECURITY_BACKEND=y
    CONFIG_NRF_SECURITY_ADVANCED=y
    CONFIG_NRF_SECURITY_RNG=y
    CONFIG_ENTROPY_GENERATOR=y
    
    
    CONFIG_OBERON_BACKEND=y
    
    CONFIG_UART_NRFX=y
    CONFIG_SERIAL=y
    #CONFIG_UART_CONSOLE_ON_DEV_NAME="UART_1"
    CONFIG_UART_INTERRUPT_DRIVEN=y
    
    CONFIG_DEBUG_OPTIMIZATIONS=y
    
    CONFIG_LOG=y
    CONFIG_LTE_LINK_CONTROL_LOG_LEVEL_DBG=y

Related