This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

HTTPS POST request connect failing

Hi,

I am working on changing my GET request to a POST request and am failing at the connect() function, which makes me think I have the wrong header. There seem to be multiple examples out there, and all of them different. Can someone check my code to see where I may be going wrong?

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

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

#define HTTPS_PORT 443

#define HTTP_HEAD "POST /api? HTTP/1.1\r\nHost: fleet-test.staflsystems.com\r\nConnection: close\r\nContent-Type: text/plain\r\nContent-Length: 5\r\n\r\hello\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*/
static const char cert[] = {
	//#include "../cert/GlobalSign-Root-CA-R2"
};

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


/* Initialize AT communications */
int at_comms_init(void)
{
	int err;

	err = at_cmd_init();
	if (err) {
		printk("Failed to initialize AT commands, err %d\n", err);
		return err;
	}

	err = at_notif_init();
	if (err) {
		printk("Failed to initialize AT notifications, err %d\n", err);
		return err;
	}

	return 0;
}

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

	err = modem_key_mgmt_exists(TLS_SEC_TAG,
				    MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN,
				    &exists, &unused);
	if (err) {
		printk("Failed to check for certificates err %d\n", err);
		return err;
	}

	if (exists) {
		/* For the sake of simplicity we delete what is provisioned
		 * with our security tag and reprovision our certificate.
		 */
		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,
	};

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

	return 0;
}

void send_request()
{
	printk("Sending a request to Stafl Systems!\n");
	struct sockaddr_in local_addr;
	struct addrinfo *res;
	struct addrinfo hints;

	hints.ai_flags = 0;
	hints.ai_family = AF_INET;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = 0;

	local_addr.sin_family = AF_INET;
	local_addr.sin_port = htons(HTTPS_PORT);
	local_addr.sin_addr.s_addr = 0;

        int fd, bytes, err;
        char *p;
        size_t off;

        err = getaddrinfo("fleet-test.staflsystems.com", NULL, &hints, &res);

	if (err) {
		printk("getaddrinfo() failed, err %d\n", errno);
		return;
	}

        printk("Address info done!\n");

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

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

        printk("opened socket!\n");

	/* Setup TLS socket options */
	err = tls_setup(fd);

	if (err) {
		goto clean_up;
	}

        printk("tls done...\n");

	printk("Connecting to Stafl Systems..");
	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, &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 {
		int 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);
	}

clean_up:
        printk("Finished, closing socket.\n");
        freeaddrinfo(res);
	(void)close(fd);
}

void main(void)
{
	int err;

	printk("HTTPS client sample started\n\r");
        
	err = bsdlib_init();
	if (err) {
		printk("Failed to initialize bsdlib!");
		return;
	}

	/* Initialize AT comms in order to provision the certificate */
	err = at_comms_init();
	if (err) {
		return;
	}
        printk("Before cert ");
	/* Provision certificates before connecting to the LTE network */
//	err = cert_provision();
//	if (err) {
//		return;
//	}
        printk("After cert ");
	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");

        send_request();

        printk("done\n");

//        while(true)
//        {
//          //k_cpu_idle();
//          //printk("test\n");
//          send_request();
//          k_usleep(1000 * 5000);
//        }
}

Here is the response from LTE Link Monitor

HTTPS client sample started
Before cert 
After cert 
Waiting for network.. 
OK
Sending a request to Stafl Systems!
Address info done! 
Socket number 1
opened socket!
tls done...
Connecting to Stafl Systems..connect() failed, err: 45
Finished, closing socket.
done

Parents Reply Children
  • Hi Øyvind,

    Thanks for your response.

    The certificate is working when I attempt to connect to google.com, but I was told we don't need a certificate to connect to our webserver (I am not skilled in backend webdev stuff). What header file is EOPNOTSUPP defined in?

    At the end of the day yesterday, I was able to get the POST request working using the url below, but now I am unable to get it working again today.

    char send_buf[] = "POST /api? HTTP/1.1\r\nHost: myserver.com\r\nConnection: keep-alive\r\nContent-type: application/json\r\nContent-length: 41\r\n\r\n\{\"product_id\": \"xxxxxxxxx\"\}";

    Is there a better way to send a payload over https post? It seems a bit much to confgure for JSON and send in the request header. I can't seem to find any examples on how to send the payload.

    I am using the latest version of ncs v 1.3? And also the latest version of modem firmware 2020-04-29_bc7ade8b

    Thanks for your assistance.

  • Hello,

    kgarland789 said:
    What header file is EOPNOTSUPP defined in?

    The error code is given in errno.h following https://man7.org/linux/man-pages/man3/errno.3.html

    kgarland789 said:
    Is there a better way to send a payload over https post?

     HTTPS Client sample uses TLS. In this sample, the TLS_setup() associates a socket with the security tag that the samples provisions the certificate with. 

    The server you are using does most likely have a certificate that you must use, however, it may return a certificate that is too large for the TLS buffer to handle, which is currently 2kB (maximum). 

    I recommend talking to your IT department to clarify what is needed to communicate with the server and to check what the TLS handshake looks like in regards to certificates returned by the server to the client.  

    Kind regards,
    Øyvind

Related