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.
  • 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

  • But the recv() function receives no response. 

    recv() returns 0 bytes. Does this indicate the connection is closed? If so is it related to the TLS setup? 

  • Hi,

     

    Bhargav Kinnal said:
    recv() returns 0 bytes. Does this indicate the connection is closed? If so is it related to the TLS setup? 

    Yes, that means that the connection was closed:

    https://github.com/nrfconnect/sdk-nrf/blob/main/samples/nrf9160/https_client/src/main.c#L223

     

    Can you share the log output on runtime?

    Also, can you share your .config (build/zephyr/.config) file?

     

    PS:

    I see that I previously posted configs for ncs 2.0, sorry for that! In ncs v1.8, you should set "CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN" instead of these two:

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

      

    Kind regards,

    Håkon

  • UPDATE:

    The connect() worked because the certificates were in the modem. 

    Once I remove them and use the mbedTLS only it starts to fail again. with the err: 111 Connection refused. Am I missing something in the TLS setup?

  • Is there a sample where mbedTLS is used and the TLS is not offloaded to the modem? Looks like this is the cause for this issue

Related