HTTPS Get request with nrf9160. TSL problems.

Hello,

For a while I have been trying to alter the HTTPS client example to access another example. 
No matter what I do, when I try to connect to any other host like google.com or my own website, I always get a connection refused error (111).

I have tried to provide the proper certificates using SSLabs as specified in your documentation, but with no luck. 

/*
 * Copyright (c) 2020 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
 */

#include <string.h>
#include <zephyr.h>
#include <stdlib.h>
#include <net/socket.h>
#include <modem/nrf_modem_lib.h>
#include <net/tls_credentials.h>
#include <modem/lte_lc.h>
#include <modem/modem_key_mgmt.h>

#define HTTPS_PORT 443

#define HTTPS_HOSTNAME "elsys2.tangane.no"
//#define HTTPS_HOSTNAME "example.com"
//#define HTTPS_HOSTNAME "google.no"

#define HTTP_HEAD                                                              \
	"GET / HTTP/1.1\r\n"                                                  \
	"Host: " HTTPS_HOSTNAME ":443\r\n"                                     \
	"Connection: close\r\n\r\n"

#define HTTP_HEAD_LEN (sizeof(HTTP_HEAD) - 1)

#define HTTP_HDR_END "\r\n\r\n"

#define RECV_BUF_SIZE 2048
#define TLS_SEC_TAG 42

static const char send_buf[] = HTTP_HEAD;
static char recv_buf[RECV_BUF_SIZE];

/* Certificate for `example.com` */
static const char cert[] = {
	//#include "../cert/DigiCertGlobalRootCA.pem"
	//#include "../cert/gsr4.pem"
	#include "../cert/elsys.pem"
	//#include "../cert/google.pem"
};

BUILD_ASSERT(sizeof(cert) < KB(4), "Certificate too large");

/* Provision certificate to modem */
int cert_provision(void)
{
	int err;
	bool exists;
	int mismatch;

	/* It may be sufficient for you application to check whether the correct
	 * certificate is provisioned with a given tag directly using modem_key_mgmt_cmp().
	 * Here, for the sake of the completeness, we check that a certificate exists
	 * before comparing it with what we expect it to be.
	 */
	err = modem_key_mgmt_exists(TLS_SEC_TAG, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, &exists);
	if (err) {
		printk("Failed to check for certificates err %d\n", err);
		return err;
	}

	if (exists) {
		mismatch = modem_key_mgmt_cmp(TLS_SEC_TAG,
					      MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN,
					      cert, strlen(cert));
		if (!mismatch) {
			printk("Certificate match\n");
			return 0;
		}

		printk("Certificate mismatch\n");
		err = modem_key_mgmt_delete(TLS_SEC_TAG, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN);
		if (err) {
			printk("Failed to delete existing certificate, err %d\n", err);
		}
	}

	printk("Provisioning certificate\n");

	/*  Provision certificate to the modem */
	err = modem_key_mgmt_write(TLS_SEC_TAG,
				   MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN,
				   cert, sizeof(cert) - 1);
	if (err) {
		printk("Failed to provision certificate, err %d\n", err);
		return err;
	}

	return 0;
}

/* Setup TLS options on a given socket */
int tls_setup(int fd)
{
	int err;
	int verify;

	/* Security tag that we have provisioned the certificate with */
	const sec_tag_t tls_sec_tag[] = {
		TLS_SEC_TAG,
	};

#if defined(CONFIG_SAMPLE_TFM_MBEDTLS)
	err = tls_credential_add(tls_sec_tag[0], TLS_CREDENTIAL_CA_CERTIFICATE, cert, sizeof(cert));
	if (err) {
		return err;
	}
#endif

	/* 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, HTTPS_HOSTNAME, sizeof(HTTPS_HOSTNAME) - 1);
	if (err) {
		printk("Failed to setup TLS hostname, err %d\n", errno);
		return err;
	}
	return 0;
}

void main(void)
{
	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("HTTPS client sample started\n\r");

#if !defined(CONFIG_SAMPLE_TFM_MBEDTLS)
	/* Provision certificates before connecting to the LTE network */
	err = cert_provision();
	if (err) {
		return;
	}
#endif

	printk("Waiting for network.. ");
	err = lte_lc_init_and_connect();
	if (err) {
		printk("Failed to connect to the LTE network, err %d\n", err);
		return;
	}
	printk("OK\n");

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

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

	if (IS_ENABLED(CONFIG_SAMPLE_TFM_MBEDTLS)) {
		fd = socket(AF_INET, SOCK_STREAM | SOCK_NATIVE_TLS, IPPROTO_TLS_1_2);
	} else {
		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);
	if (err) {
		printk("We have tls errors... \n");
		goto clean_up;
	}
    printk("Error in tls setup: %d\n", err);

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

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

	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 */);

	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);

	lte_lc_power_off();
}

The error I'm getting: connect() failed, err: 111

Could someone please give me some direction in how to debug this? I've tried to replicate others who have posted that they have gotten further than this, but I find it hard to track down where my fault lies. 

Thanks in advance.

  • Hi,

    Could someone please give me some direction in how to debug this?

    In addition to logs (e.g. does the application write the certificates to the modem?), one of the most useful tool is to look at the traffic between the device and the server.

    You can do that by enabling modem tracing, and use the Trace Collector V2 Preview application in nRF Connect for Desktop to either store the trace in a Wireshark file, or display it live in Wireshark.

    For more information on how to take a modem trace, see: https://infocenter.nordicsemi.com/topic/ug_trace_collector/UG/trace_collector/intro.html

    The documentation is for Trace Collector v1, which doesn't let you see any of the data in the trace, but the v2 should be quite self explanatory. The most important details are how to get the trace off the device.

    Best regards,

    Didrik

  • Thanks for the advice Didrik.


    Yes, the certificate is written to the modem, and it seems to accept it. The application still fails when calling the connect function and returns the 111 error. 

    I tried enabling the Trace Collector, but Im not getting any packets out. I've followed the steps in the guide very closely, yet something has gone wrong. The CONFIG_NRF_MODEM_LIB_TRACE_ENABLED is set to "y" in my config file, and its also present in the build files. 

    I tried tracing with the working example file, but no packets are collected. Any advice on what the cause could be, or again, how to debug it? I'm getting a response from the server and the application is connecting and working. 

    I don't know if this is relevant, but if I do not select a trace database manually, the auto-detect does not stop. It seems to be loading forever. It suggest restarting the device, which I tried without any luck. Could this be a part of the issue?

    Best regards,

    Sindre

    Edit: I get something in this RAW file, maybe it contains something of relevance.

    trace-2022-03-21T09-50-21.847Z.bin

  • Sindre Nordtveit said:
    Edit: I get something in this RAW file, maybe it contains something of relevance.

    Looking at the trace file, it looks like you are using modem FW v1.2.0, which is not supported by the trace collector v2.

    I would highly recommend you update the modem FW to either 1.2.7 (if you have a rev 1 SiP, marked B0) or 1.3.1 (if you have a rev 2 SiP, marked B1).

    Note that trace collector v2 also doesn't have a trace DB for mfw v1.2.7 yet, but it is included in the modem FW .zip file, so you should be able to decode those by selecting the trace DB manually.

    The old modem FW also gives me a theory about why you are not able to connect: The Server Name Indication TLS extension was not supported until mfw v1.2.1. If your server requires SNI support to connect (e.g. if multiple servers are hosted on the same IP address), the old modem FW and lack of SNI support will be the reason for your failed connection attempts.

  • I've tried updating the firmware, is there any way I can confirm this myself? 

    Here is the new RAW file.

    trace-2022-03-21T12-50-55.042Z.bin

  • Unfortunately, this trace is not valid.

    All the bytes in the trace are 0.

    You can get the modem fw version wither via AT commands (e.g. AT%SHORTSWVER) or from the modem_info library (which also uses AT commands internally).

Related