Hello,
I'm having issues connecting over TLS more than a single time. Using the nrf/samples/nrf9160/https_client works fine.
However, if you modify it to connect more than once, it fails (assuming you do close the socket fd). I made a modifications to main.c that simply loop everything after the lte_lc_init_and_connect() call.
/* * 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 <net/bsdlib.h> #include <net/tls_credentials.h> #include <lte_lc.h> #include <at_cmd.h> #include <at_notif.h> #include <modem_key_mgmt.h> /* DOCLOSE_SOCKET_FD_AFTER_USE * undefine this to try _without_ closing the socket */ #define DOCLOSE_SOCKET_FD_AFTER_USE /* * undefine this to avoid re provisioning the cert on each * reset -- this must be done at least once though. */ #define DO_PROVISION_GOOGLE_ROOTCERT #define HTTPS_PORT 443 #define HTTP_HEAD \ "HEAD / HTTP/1.1\r\n" \ "Host: www.google.com: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 `google.com` */ 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; u8_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 setup(void) { } 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"); 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; } /* Provision certificates before connecting to the LTE network */ #ifdef DO_PROVISION_GOOGLE_ROOTCERT printk("installing root cert for verif"); 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"); /* LOOP HERE, to get the address, connect and send/rcv, then close */ for(;;) { printk("getting google address\n"); err = getaddrinfo("google.com", NULL, &hints, &res); if (err) { printk("getaddrinfo() failed, err %d\n", errno); return; } ((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"); return; } /* Setup TLS socket options */ err = tls_setup(fd); if (err) { goto clean_up; } printk("Connecting to %s through socket %i\n", "google.com", fd); err = connect(fd, res->ai_addr, sizeof(struct sockaddr_in)); if (err) { printk("connect() failed, err: %d\n", errno); goto clean_up; } printk("Connection established"); 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); #ifdef DOCLOSE_SOCKET_FD_AFTER_USE (void)close(fd); #endif k_sleep(4000); } /* end infinite loop */ }
If you leave the socket hanging, multiple connections will work but you eventually run out of resources. Output looks like:
HTTPS client sample started Waiting for network.. OK getting google address Connecting to google.com through socket 2 Connection establishedSent 64 bytes Received 903 bytes > HTTP/1.1 200 OK Finished, closing socket. getting google address Connecting to google.com through socket 3 Connection establishedSent 64 bytes Received 903 bytes > HTTP/1.1 200 OK Finished, closing socket. getting google address Connecting to google.com through socket 4 connect() failed, err: 45 getting google address Connecting to google.com through socket 5 connect() failed, err: 45 getting google address
If you actually clean up and close it, then it hangs forever on second call to connect(). Output stops at:
HTTPS client sample started Waiting for network.. OK getting google address Connecting to google.com through socket 2 Connection establishedSent 64 bytes Received 903 bytes > HTTP/1.1 200 OK Finished, closing socket. getting google address Connecting to google.com through socket 2 [stuck here forever and ever]
What is missing here to actually be able to connect to a remote host more than once? Is it some setsockopt() to clear out what was set previously, in tls_setup()? Something else?
Thanks!