diff --git a/include/net/download_client.h b/include/net/download_client.h index 6dd6b1f9603b..2292c561262f 100644 --- a/include/net/download_client.h +++ b/include/net/download_client.h @@ -24,6 +24,8 @@ #include #include +/* Predefinition of download client struct used in download client transport. */ + #ifdef __cplusplus extern "C" { #endif @@ -133,10 +135,6 @@ struct download_client_cfg { * @brief Download client configuration options. */ struct download_client_host_cfg { - /** Server hosting the file, null-terminated. - * The host name must be kept in scope while download is going on. - */ - const char *hostname; /** TLS security tag list. * Pass NULL to disable TLS. * The list must be kept in scope while download is going on. @@ -151,17 +149,10 @@ struct download_client_host_cfg { * Zero is the default PDN. */ uint8_t pdn_id; - /** - * Address family to be used for the download, AF_INET6 or AF_INET. - * Set to AF_UNSPEC (0) to fallback to AF_INET if AF_INET6 does not work. - */ - int family; /** Maximum fragment size to download. 0 indicates that values * configured using Kconfig shall be used. */ size_t range_override; - /** Set hostname for TLS Server Name Indication extension */ - bool set_tls_hostname; /** Set socket to native TLS */ bool set_native_tls; /** Close connection when done */ @@ -172,12 +163,12 @@ struct download_client_host_cfg { * @brief Download client state. */ enum download_client_state { - DOWNLOAD_CLIENT_DEINITIALIZED, - DOWNLOAD_CLIENT_IDLE, - DOWNLOAD_CLIENT_CONNECTING, - DOWNLOAD_CLIENT_CONNECTED, - DOWNLOAD_CLIENT_DOWNLOADING, - DOWNLOAD_CLIENT_DEINITIALIZING, + DOWNLOAD_CLIENT_DEINITIALIZED, + DOWNLOAD_CLIENT_IDLE, + DOWNLOAD_CLIENT_CONNECTING, + DOWNLOAD_CLIENT_CONNECTED, + DOWNLOAD_CLIENT_DOWNLOADING, + DOWNLOAD_CLIENT_DEINITIALIZING, }; /** @@ -190,64 +181,25 @@ struct download_client { struct download_client_cfg config; /** Host configuration options. */ struct download_client_host_cfg host_config; - + /** Host name, null-terminated. + */ + char hostname[CONFIG_DOWNLOAD_CLIENT_MAX_HOSTNAME_SIZE]; /** File name, null-terminated. - * The file name must be kept in scope while download is going on. */ - const char *file; + char file[CONFIG_DOWNLOAD_CLIENT_MAX_FILENAME_SIZE]; /** Size of the file being downloaded, in bytes. */ size_t file_size; /** Download progress, number of bytes downloaded. */ size_t progress; /** Buffer offset. */ size_t buf_offset; - /** Request new data */ - bool new_data_req; - struct { - /** Socket descriptor. */ - int fd; - /** Protocol for current download. */ - int proto; - /** Socket type */ - int type; - /** Port */ - uint16_t port; - /** Destination address storage */ - struct sockaddr remote_addr; - } sock; - - /** Application protocols */ - union { - struct { - /** The server has closed the connection. */ - bool connection_close; - /** Is using ranged query. */ - bool ranged; - /** Ranged progress */ - size_t ranged_progress; - /** HTTP header */ - struct { - /** Header length */ - size_t hdr_len; - /** Status code */ - unsigned long status_code; - /** Whether the HTTP header for - * the current fragment has been processed. - */ - bool has_end; - } header; - } http; - - struct { - bool initialized; - /** CoAP block context. */ - struct coap_block_context block_ctx; - - /** CoAP pending object. */ - struct coap_pending pending; - } coap; - }; + /** Download client transport, http, CoAP, MQTT, ... + * Store a pointer to the selected transport per DLC instance to avoid looking it up each call. + */ + void *transport; + /** Transport parameters. */ + uint8_t transport_internal[CONFIG_DOWNLOAD_CLIENT_TRANSPORT_PARAMS_SIZE]; /** Protect shared variables. */ struct k_mutex mutex; @@ -309,7 +261,7 @@ int download_client_deinit(struct download_client *client); */ int download_client_start(struct download_client *client, const struct download_client_host_cfg *host_config, - const char *file, size_t from); + const char *url, size_t from); /** * @brief Stop file download and disconnect from server. @@ -376,7 +328,7 @@ int download_client_downloaded_size_get(struct download_client *client, size_t * */ int download_client_get(struct download_client *client, const struct download_client_host_cfg *config, - const char *file, size_t from); + const char *url, size_t from); #ifdef __cplusplus } diff --git a/include/net/download_client_transport.h b/include/net/download_client_transport.h new file mode 100644 index 000000000000..8a1b8d956304 --- /dev/null +++ b/include/net/download_client_transport.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef DOWNLOAD_CLIENT_TRANSPORT_H +#define DOWNLOAD_CLIENT_TRANSPORT_H + +#include + +int dlc_transport_evt_connected(struct download_client *dlc); +int dlc_transport_evt_disconnected(struct download_client *dlc); +int dlc_transport_evt_data(struct download_client *dlc, void *data, size_t len); +int dlc_transport_evt_download_complete(struct download_client *dlc); + +struct dlc_transport { + //TODO Consider whether the transports should not have access to the DLC struct, but get the parameters they need? + /** + * Parse protocol + * + */ + bool (*proto_supported)(struct download_client *dlc, const char *uri); + /** + * Initialize DLC transport + * + * @param ... + * @returns 0 on success, negative error on failure. + */ + int (*init)(struct download_client *dlc, struct download_client_host_cfg *host_cgf, const char *uri); + /** + * Deinitialize DLC transport + * + * @param ... + * @returns 0 on success, negative error on failure. + */ + int (*deinit)(struct download_client *dlc); + /** + * Connect DLC transport. + * + * Connection result is given by callback to @c dlc_transport_event_connected. + * + * @param ... + * @returns 0 on success, negative error on failure. + */ + int (*connect)(struct download_client *dlc); + /** + * Close DLC transport + * + * @param ... + * @returns 0 on success, negative error on failure. + */ + int (*close)(struct download_client *dlc); + /** + * Download data with DLC transport + * + * @param ... + * @returns 0 on success, negative error on failure. + */ + int (*download)(struct download_client *dlc); +}; + +struct dlc_transport_entry { + struct dlc_transport *transport; +}; + +/** + * @brief Define a DLC transport. + * + * @param entry The entry name. + * @param _transport The transport. + */ +#define DLC_TRANSPORT(entry, _transport) \ + static STRUCT_SECTION_ITERABLE(dlc_transport_entry, entry) = { \ + .transport = _transport, \ + } + +#endif /* DOWNLOAD_CLIENT_TRANSPORT_H */ diff --git a/lib/nrf_modem_lib/trace_backends/flash/flash.c b/lib/nrf_modem_lib/trace_backends/flash/flash.c index 4184dcf5f69c..d9379a243ae4 100644 --- a/lib/nrf_modem_lib/trace_backends/flash/flash.c +++ b/lib/nrf_modem_lib/trace_backends/flash/flash.c @@ -98,6 +98,9 @@ static int buffer_flush_to_flash(void) loc_flush.fe_sector = 0; loc_flush.fe_elem_off = 0; err = fcb_getnext(&trace_fcb, &loc_flush); + if (err) { + LOG_ERR("fcb_getnext failed, err %d", err); + } /* Walk sector to remove unread trace data from count. */ err = fcb_walk(&trace_fcb, loc_flush.fe_sector, fcb_walk_callback, NULL); diff --git a/samples/net/download/prj.conf b/samples/net/download/prj.conf index 5ab720e5a279..511161e68919 100644 --- a/samples/net/download/prj.conf +++ b/samples/net/download/prj.conf @@ -16,8 +16,3 @@ CONFIG_POSIX_API=y CONFIG_NET_IPV4=y CONFIG_HEAP_MEM_POOL_SIZE=1024 CONFIG_MBEDTLS_LEGACY_CRYPTO_C=y - -CONFIG_LOG=y -CONFIG_DOWNLOAD_CLIENT_LOG_LEVEL_ERR=y - -#CONFIG_LOG_MODE_IMMEDIATE=y diff --git a/samples/net/download/src/main.c b/samples/net/download/src/main.c index e56be38cc034..50843b0e3de2 100644 --- a/samples/net/download/src/main.c +++ b/samples/net/download/src/main.c @@ -13,6 +13,10 @@ #include #include +#include +LOG_MODULE_REGISTER(download, LOG_LEVEL_INF); + + #if CONFIG_MODEM_KEY_MGMT #include #else @@ -60,11 +64,9 @@ static struct download_client_cfg config = { .buf_size = sizeof(dlc_buf), }; static struct download_client_host_cfg host_config = { - .hostname = URL, #if CONFIG_SAMPLE_SECURE_SOCKET .sec_tag_list = sec_tag_list, .sec_tag_count = ARRAY_SIZE(sec_tag_list), - .set_tls_hostname = true, #endif .range_override = 0, }; @@ -173,18 +175,16 @@ static void connectivity_event_handler(struct net_mgmt_event_callback *cb, static void progress_print(size_t downloaded, size_t file_size) { + static int prev_percent = 0; const int percent = (downloaded * 100) / file_size; - size_t lpad = (percent * PROGRESS_WIDTH) / 100; - size_t rpad = PROGRESS_WIDTH - lpad; - printk("\r[ %3d%% ] |", percent); - for (size_t i = 0; i < lpad; i++) { - printk("="); - } - for (size_t i = 0; i < rpad; i++) { - printk(" "); + if (percent >= prev_percent && percent < prev_percent + 5) { + return; } - printk("| (%d/%d bytes)", downloaded, file_size); + + prev_percent = percent; + + LOG_INF("[ %3d%% ] (%d/%d bytes)", percent, downloaded, file_size); } static int callback(const struct download_client_evt *event) @@ -205,7 +205,7 @@ static int callback(const struct download_client_evt *event) if (file_size) { progress_print(downloaded, file_size); } else { - printk("\r[ %d bytes ] ", downloaded); + printk("\r[ %d bytes ] \n", downloaded); } #if CONFIG_SAMPLE_COMPUTE_HASH @@ -332,7 +332,7 @@ int main(void) err = download_client_get(&downloader, &host_config, URL, STARTING_OFFSET); if (err) { printk("Failed to start the downloader, err %d", err); - return 0; + k_sleep(K_FOREVER); } printk("Downloading %s\n", URL); diff --git a/subsys/net/lib/download_client/CMakeLists.txt b/subsys/net/lib/download_client/CMakeLists.txt index e99595fbbfb4..3833715bcd79 100644 --- a/subsys/net/lib/download_client/CMakeLists.txt +++ b/subsys/net/lib/download_client/CMakeLists.txt @@ -7,14 +7,18 @@ zephyr_library() zephyr_library_sources( src/download_client.c src/parse.c - src/http.c src/sanity.c src/client_socket.c ) zephyr_library_sources_ifdef( - CONFIG_COAP - src/coap.c + CONFIG_DOWNLOAD_CLIENT_TRANSPORT_HTTP + src/transports/http.c +) + +zephyr_library_sources_ifdef( + CONFIG_DOWNLOAD_CLIENT_TRANSPORT_COAP + src/transports/coap.c ) zephyr_library_sources_ifdef( @@ -23,3 +27,4 @@ zephyr_library_sources_ifdef( ) zephyr_include_directories(./include) +zephyr_linker_sources(RODATA dlc_transports.ld) diff --git a/subsys/net/lib/download_client/Kconfig b/subsys/net/lib/download_client/Kconfig index d79c3eeeebdb..5ec6dad87c64 100644 --- a/subsys/net/lib/download_client/Kconfig +++ b/subsys/net/lib/download_client/Kconfig @@ -26,7 +26,22 @@ config DOWNLOAD_CLIENT_MAX_FILENAME_SIZE range 8 2048 default 255 -config DOWNLOAD_CLIENT_TCP_SOCK_TIMEO_MS +config DOWNLOAD_CLIENT_SHELL + bool "Enable shell" + depends on SHELL + +config DOWNLOAD_CLIENT_TRANSPORT_PARAMS_SIZE + int "Maximum transport parameter size" + default 128 if DOWNLOAD_CLIENT_TRANSPORT_COAP + default 64 if DOWNLOAD_CLIENT_TRANSPORT_HTTP + +config DOWNLOAD_CLIENT_TRANSPORT_HTTP + bool "HTTP transport" + default y + +if DOWNLOAD_CLIENT_TRANSPORT_HTTP + +config DOWNLOAD_CLIENT_HTTP_TIMEO_MS int "Receive timeout on TCP sockets, in milliseconds" default 30000 range -1 600000 @@ -36,6 +51,14 @@ config DOWNLOAD_CLIENT_TCP_SOCK_TIMEO_MS when the server is not responding and client should give up. Set to -1 disable. +endif #DOWNLOAD_CLIENT_TRANSPORT_HTTP + +config DOWNLOAD_CLIENT_TRANSPORT_COAP + depends on COAP + bool "CoAP transport" + +if DOWNLOAD_CLIENT_TRANSPORT_COAP + config DOWNLOAD_CLIENT_COAP_MAX_RETRANSMIT_REQUEST_COUNT int "Number of CoAP request retransmissions" default 4 @@ -51,9 +74,35 @@ config DOWNLOAD_CLIENT_CID Use DTLS Connection-ID option when downloading from CoAPS resources. This requires modem firmware 1.3.5 or newer. -config DOWNLOAD_CLIENT_SHELL - bool "Enable shell" - depends on SHELL + +config DOWNLOAD_CLIENT_COAP_BLOCK_SIZE + int + default 3 if DOWNLOAD_CLIENT_COAP_BLOCK_SIZE_128 + default 4 if DOWNLOAD_CLIENT_COAP_BLOCK_SIZE_256 + default 5 if DOWNLOAD_CLIENT_COAP_BLOCK_SIZE_512 + +choice DOWNLOAD_CLIENT_COAP_BLOCK_SIZE_CHOICE + prompt "CoAP block size" + depends on COAP + default DOWNLOAD_CLIENT_COAP_BLOCK_SIZE_512 + help + CoAP blockwise transfer block size. + +config DOWNLOAD_CLIENT_COAP_BLOCK_SIZE_512 + bool "512" + depends on DOWNLOAD_CLIENT_BUF_SIZE >= 532 + +config DOWNLOAD_CLIENT_COAP_BLOCK_SIZE_256 + bool "256" + depends on DOWNLOAD_CLIENT_BUF_SIZE >= 276 + +config DOWNLOAD_CLIENT_COAP_BLOCK_SIZE_128 + bool "128" + depends on DOWNLOAD_CLIENT_BUF_SIZE >= 148 + +endchoice + +endif #DOWNLOAD_CLIENT_TRANSPORT_COAP module=DOWNLOAD_CLIENT module-dep=LOG diff --git a/subsys/net/lib/download_client/dlc_transports.ld b/subsys/net/lib/download_client/dlc_transports.ld new file mode 100644 index 000000000000..585d621f481b --- /dev/null +++ b/subsys/net/lib/download_client/dlc_transports.ld @@ -0,0 +1,5 @@ +/* DLC transports */ +. = ALIGN(4); +_dlc_transport_entry_list_start = .; +KEEP(*(SORT_BY_NAME("._dlc_transport_entry.*"))); +_dlc_transport_entry_list_end = .; diff --git a/subsys/net/lib/download_client/include/download_client_internal.h b/subsys/net/lib/download_client/include/download_client_internal.h index a0e1c55c7363..e3323b003cea 100644 --- a/subsys/net/lib/download_client/include/download_client_internal.h +++ b/subsys/net/lib/download_client/include/download_client_internal.h @@ -21,18 +21,14 @@ int url_parse_file(const char *url, char *file, size_t len); int http_parse(struct download_client *client, size_t len); int http_get_request_send(struct download_client *client); -#if CONFIG_COAP -bool use_coap(struct download_client *dlc); -int coap_block_init(struct download_client *client, size_t from); -int coap_get_recv_timeout(struct download_client *dl); -int coap_initiate_retransmission(struct download_client *dl); -int coap_parse(struct download_client *client, size_t len); -int coap_request_send(struct download_client *client); -#endif - -int client_socket_configure_and_connect(struct download_client *dl); -int client_socket_close(struct download_client *dlc); -int client_socket_send(const struct download_client *client, size_t len, int timeout); -ssize_t client_socket_recv(struct download_client *dl); +int client_socket_host_lookup(const char * const hostname, uint8_t pdn_id, struct sockaddr *sa); +int client_socket_configure_and_connect( + int *fd, int proto, int type, uint16_t port, struct sockaddr *remote_addr, //from dlc->sock + const char *hostname, struct download_client_host_cfg *host_cfg); +int client_socket_close(int *fd); +int client_socket_send(int fd, void *buf, size_t len); +ssize_t client_socket_recv(int fd, void *buf, size_t len); +int client_socket_recv_timeout_set(int fd, int timeout_ms); +int client_socket_send_timeout_set(int fd, int timeout_ms); #endif /* DOWNLOAD_CLIENT_INTERNAL_H */ diff --git a/subsys/net/lib/download_client/src/client_socket.c b/subsys/net/lib/download_client/src/client_socket.c index 745dae9043ec..440bd81adac3 100644 --- a/subsys/net/lib/download_client/src/client_socket.c +++ b/subsys/net/lib/download_client/src/client_socket.c @@ -12,9 +12,9 @@ #include #include -#include #include "download_client_internal.h" +#include LOG_MODULE_DECLARE(download_client, CONFIG_DOWNLOAD_CLIENT_LOG_LEVEL); #define SIN6(A) ((struct sockaddr_in6 *)(A)) @@ -35,33 +35,7 @@ static const char *str_family(int family) } } -static int socket_recv_timeout_set(int fd, int timeout_ms) -{ - int err; - - if (timeout_ms <= 0) { - return 0; - } - - if (fd == -1) { - return -1; - } - - struct timeval timeo = { - .tv_sec = (timeout_ms / 1000), - .tv_usec = (timeout_ms % 1000) * 1000, - }; - - err = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo)); - if (err) { - LOG_WRN("Failed to set socket timeout, errno %d", errno); - return -1; - } - - return 0; -} - -static int socket_send_timeout_set(int fd, int timeout_ms) +int client_socket_send_timeout_set(int fd, int timeout_ms) { int err; @@ -77,7 +51,7 @@ static int socket_send_timeout_set(int fd, int timeout_ms) err = setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo, sizeof(timeo)); if (err) { LOG_WRN("Failed to set socket timeout, errno %d", errno); - return -1; + return -errno; } return 0; @@ -113,24 +87,17 @@ static int socket_sectag_set(int fd, const int * const sec_tag_list, uint8_t sec return 0; } -static int socket_tls_hostname_set(int fd, const char * const hostname) +static int socket_tls_hostname_set(int fd, const char * const host) { - __ASSERT_NO_MSG(hostname); + __ASSERT_NO_MSG(host); - char parsed_host[HOSTNAME_SIZE]; int err; - err = url_parse_host(hostname, parsed_host, sizeof(parsed_host)); - if (err) { - LOG_ERR("Failed to parse host, err %d", err); - return err; - } - - err = setsockopt(fd, SOL_TLS, TLS_HOSTNAME, parsed_host, - strlen(parsed_host)); + err = setsockopt(fd, SOL_TLS, TLS_HOSTNAME, host, + strlen(host)); if (err) { LOG_ERR("Failed to setup TLS hostname (%s), errno %d", - parsed_host, errno); + host, errno); return -errno; } @@ -152,77 +119,77 @@ static int socket_pdn_id_set(int fd, int pdn_id) return 0; } +static bool is_ip_address(const char *hostname) +{ + struct sockaddr sa; -static int host_lookup(const char *host, int family, uint8_t pdn_id, - struct sockaddr *sa) + if (zsock_inet_pton(AF_INET, hostname, sa.data) == 1) { + return true; + } else if (zsock_inet_pton(AF_INET6, hostname, sa.data) == 1) { + return true; + } + + return false; +} + +int client_socket_host_lookup(const char * const hostname, uint8_t pdn_id, struct sockaddr *sa) { int err; char pdnserv[4]; - char hostname[HOSTNAME_SIZE]; + char *servname = NULL; + //char hostname[HOSTNAME_SIZE]; struct addrinfo *ai; + struct addrinfo hints; - struct addrinfo hints = { - .ai_family = family, - }; - - /* Extract the hostname, without protocol or port */ - err = url_parse_host(host, hostname, sizeof(hostname)); - if (err) { - return err; - } + printk("host lookup %s, pdn id %d\n", hostname, pdn_id); if (pdn_id) { hints.ai_flags = AI_PDNSERV; (void)snprintf(pdnserv, sizeof(pdnserv), "%d", pdn_id); - err = getaddrinfo(hostname, pdnserv, &hints, &ai); + servname = pdnserv; + } + +#if CONFIG_NET_IPV6 + hints.ai_family = AF_INET6; + err = getaddrinfo(hostname, servname, &hints, &ai); + if (err) { + LOG_DBG("Failed to resolve hostname %s on %s, err %d", + hostname, str_family(hints.ai_family), err); } else { - err = getaddrinfo(hostname, NULL, &hints, &ai); + goto out; } +#endif + + hints.ai_family = AF_INET; + err = getaddrinfo(hostname, servname, &hints, &ai); if (err) { - LOG_DBG("Failed to resolve hostname %s on %s", - hostname, str_family(family)); + LOG_ERR("Failed to resolve hostname %s on %s, err %d", + hostname, str_family(hints.ai_family), err); return -EHOSTUNREACH; } +out: memcpy(sa, ai->ai_addr, ai->ai_addrlen); freeaddrinfo(ai); return 0; } -int client_socket_configure_and_connect(struct download_client *dlc) +int client_socket_configure_and_connect( + int *fd, int proto, int type, uint16_t port, struct sockaddr *remote_addr, + const char *hostname, struct download_client_host_cfg *host_cfg) { int err; socklen_t addrlen; - err = -ENOTSUP; - /* Attempt IPv6 connection if configured, fallback to IPv4 on error */ - if ((dlc->host_config.family == AF_UNSPEC) || (dlc->host_config.family == AF_INET6)) { - err = host_lookup(dlc->host_config.hostname, AF_INET6, - dlc->host_config.pdn_id, &dlc->sock.remote_addr); - /* err is checked later */ - } - - if (((dlc->host_config.family == AF_UNSPEC) && err) || - (dlc->host_config.family == AF_INET)) { - err = host_lookup(dlc->host_config.hostname, AF_INET, - dlc->host_config.pdn_id, &dlc->sock.remote_addr); - /* err is checked later */ - } - - if (err) { - LOG_ERR("DNS lookup failed %s", dlc->host_config.hostname); - return err; - } - - switch (dlc->sock.remote_addr.sa_family) { + switch (remote_addr->sa_family) { case AF_INET6: - SIN6(&dlc->sock.remote_addr)->sin6_port = htons(dlc->sock.port); + SIN6(remote_addr)->sin6_port = htons(port); addrlen = sizeof(struct sockaddr_in6); break; case AF_INET: - SIN(&dlc->sock.remote_addr)->sin_port = htons(dlc->sock.port); + SIN(remote_addr)->sin_port = htons(port); addrlen = sizeof(struct sockaddr_in); break; default: @@ -231,43 +198,46 @@ int client_socket_configure_and_connect(struct download_client *dlc) } LOG_DBG("family: %d, type: %d, proto: %d", - dlc->sock.remote_addr.sa_family, dlc->sock.type, dlc->sock.proto); + remote_addr->sa_family, type, proto); - dlc->sock.fd = socket(dlc->sock.remote_addr.sa_family, dlc->sock.type, dlc->sock.proto); - if (dlc->sock.fd < 0) { + *fd = socket(remote_addr->sa_family, type, proto); + if (*fd < 0) { err = -errno; LOG_ERR("Failed to create socket, errno %d", -err); goto cleanup; } - if (dlc->host_config.pdn_id) { - err = socket_pdn_id_set(dlc->sock.fd, dlc->host_config.pdn_id); + LOG_DBG("Socket opened, fd %d", *fd); + + if (host_cfg->pdn_id) { + err = socket_pdn_id_set(*fd, host_cfg->pdn_id); if (err) { goto cleanup; } } - if ((dlc->sock.proto == IPPROTO_TLS_1_2 || dlc->sock.proto == IPPROTO_DTLS_1_2) && - (dlc->host_config.sec_tag_list != NULL) && (dlc->host_config.sec_tag_count > 0)) { - err = socket_sectag_set(dlc->sock.fd, dlc->host_config.sec_tag_list, - dlc->host_config.sec_tag_count); + if ((proto == IPPROTO_TLS_1_2 || proto == IPPROTO_DTLS_1_2) && + (host_cfg->sec_tag_list != NULL) && (host_cfg->sec_tag_count > 0)) { + err = socket_sectag_set(*fd, host_cfg->sec_tag_list, + host_cfg->sec_tag_count); if (err) { goto cleanup; } - if (dlc->host_config.set_tls_hostname) { - err = socket_tls_hostname_set(dlc->sock.fd, dlc->host_config.hostname); + if (proto == IPPROTO_TLS_1_2 && + !is_ip_address(hostname)) { //TODO former dlc->host_cfg.set_tls_hostname, is this ok? + err = socket_tls_hostname_set(*fd, hostname); if (err) { err = -errno; goto cleanup; } } - if (dlc->sock.proto == IPPROTO_DTLS_1_2 && IS_ENABLED(CONFIG_DOWNLOAD_CLIENT_CID)) { + if (proto == IPPROTO_DTLS_1_2 && IS_ENABLED(CONFIG_DOWNLOAD_CLIENT_CID)) { /* Enable connection ID */ uint32_t dtls_cid = TLS_DTLS_CID_ENABLED; - err = setsockopt(dlc->sock.fd, SOL_TLS, TLS_DTLS_CID, &dtls_cid, + err = setsockopt(*fd, SOL_TLS, TLS_DTLS_CID, &dtls_cid, sizeof(dtls_cid)); if (err) { err = -errno; @@ -281,19 +251,19 @@ int client_socket_configure_and_connect(struct download_client *dlc) char ip_addr_str[NET_IPV6_ADDR_LEN]; void *sin_addr; - if (dlc->sock.remote_addr.sa_family == AF_INET6) { - sin_addr = &((struct sockaddr_in6 *)&dlc->sock.remote_addr)->sin6_addr; + if (remote_addr->sa_family == AF_INET6) { + sin_addr = &((struct sockaddr_in6 *)remote_addr)->sin6_addr; } else { - sin_addr = &((struct sockaddr_in *)&dlc->sock.remote_addr)->sin_addr; + sin_addr = &((struct sockaddr_in *)remote_addr)->sin_addr; } - inet_ntop(dlc->sock.remote_addr.sa_family, sin_addr, + inet_ntop(remote_addr->sa_family, sin_addr, ip_addr_str, sizeof(ip_addr_str)); LOG_INF("Connecting to %s", ip_addr_str); } LOG_DBG("fd %d, addrlen %d, fam %s, port %d", - dlc->sock.fd, addrlen, str_family(dlc->sock.remote_addr.sa_family), dlc->sock.port); + *fd, addrlen, str_family(remote_addr->sa_family), port); - err = connect(dlc->sock.fd, &dlc->sock.remote_addr, addrlen); + err = connect(*fd, remote_addr, addrlen); if (err) { err = -errno; LOG_ERR("Unable to connect, errno %d", -err); @@ -307,45 +277,37 @@ int client_socket_configure_and_connect(struct download_client *dlc) cleanup: if (err) { - if (dlc->sock.fd != -1) { - close(dlc->sock.fd); - dlc->sock.fd = -1; - } + client_socket_close(fd); } return err; } -int client_socket_close(struct download_client *dlc) +int client_socket_close(int *fd) { int err = 0; - if (dlc->sock.fd != -1) { - err = close(dlc->sock.fd); + if (*fd != -1) { + err = close(*fd); if (err && errno != EBADF) { err = -errno; LOG_ERR("Failed to close socket, errno %d", -err); } - } - dlc->sock.fd = -1; + LOG_DBG("Socket closed, fd %d", *fd); + *fd = -1; + } return err; } -int client_socket_send(const struct download_client *dlc, size_t len, int timeout) +int client_socket_send(int fd, void *buf, size_t len) { - int err; int sent; size_t off = 0; - err = socket_send_timeout_set(dlc->sock.fd, timeout); - if (err) { - return -errno; - } - while (len) { - sent = send(dlc->sock.fd, dlc->config.buf + off, len, 0); + sent = send(fd, (uint8_t *)buf + off, len, 0); if (sent < 0) { return -errno; } @@ -357,30 +319,43 @@ int client_socket_send(const struct download_client *dlc, size_t len, int timeou return 0; } -ssize_t client_socket_recv(struct download_client *dlc) +int client_socket_recv_timeout_set(int fd, int timeout_ms) { - int err, timeout = 0; - - if (use_http(dlc)) { - timeout = CONFIG_DOWNLOAD_CLIENT_TCP_SOCK_TIMEO_MS; -#if CONFIG_COAP - } else if (use_coap(dlc)) { - timeout = coap_get_recv_timeout(dlc); - if (timeout == 0) { - errno = ETIMEDOUT; - return -1; - } -#endif - } else { - LOG_ERR("unhandled proto"); - return -1; + int err; + struct timeval timeo; + + if (timeout_ms <= 0) { + return 0; + } + + if (fd == -1) { + return -EINVAL; } - err = socket_recv_timeout_set(dlc->sock.fd, timeout); + timeo.tv_sec = (timeout_ms / 1000); + timeo.tv_usec = (timeout_ms % 1000) * 1000; + + err = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo)); if (err) { - return -1; + LOG_WRN("Failed to set socket timeout, errno %d", errno); + return -errno; } - return recv(dlc->sock.fd, dlc->config.buf + dlc->buf_offset, - dlc->config.buf_size - dlc->buf_offset, 0); + return 0; +} + +ssize_t client_socket_recv(int fd, void *buf, size_t len) +{ + int err = 0; + + if (fd == -1) { + return -EINVAL; + } + + err = recv(fd, buf, len, 0); + if (err < 0) { + return -errno; + } + + return err; } diff --git a/subsys/net/lib/download_client/src/coap.c b/subsys/net/lib/download_client/src/coap.c deleted file mode 100644 index 128ecdbccd88..000000000000 --- a/subsys/net/lib/download_client/src/coap.c +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright (c) 2020 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - -#include -#include -#include -#include -#include -#include - -LOG_MODULE_DECLARE(download_client, CONFIG_DOWNLOAD_CLIENT_LOG_LEVEL); - -#define COAP_VER 1 -#define FILENAME_SIZE CONFIG_DOWNLOAD_CLIENT_MAX_FILENAME_SIZE -#define COAP_PATH_ELEM_DELIM "/" - -/* declaration of strtok_r appears to be missing in some cases, - * even though it's defined in the minimal libc, so we forward declare it - */ -extern char *strtok_r(char *str, const char *sep, char **state); - -int url_parse_file(const char *url, char *file, size_t len); -int client_socket_send(const struct download_client *dlc, size_t len, int timeout); - -static int coap_get_current_from_response_pkt(const struct coap_packet *cpkt) -{ - int block = 0; - - block = coap_get_option_int(cpkt, COAP_OPTION_BLOCK2); - if (block < 0) { - return block; - } - - return GET_BLOCK_NUM(block) << (GET_BLOCK_SIZE(block) + 4); -} - -static bool has_pending(struct download_client *dlc) -{ - return dlc->coap.pending.timeout > 0; -} - -int coap_block_init(struct download_client *dlc, size_t from) -{ - if (dlc->coap.initialized) { - return 0; - } - - coap_block_transfer_init(&dlc->coap.block_ctx, - CONFIG_DOWNLOAD_CLIENT_COAP_BLOCK_SIZE, 0); - dlc->coap.block_ctx.current = from; - coap_pending_clear(&dlc->coap.pending); - - dlc->coap.initialized = true; - return 0; -} - -int coap_get_recv_timeout(struct download_client *dlc) -{ - int timeout; - - __ASSERT(has_pending(dlc), "Must have coap pending"); - - /* Retransmission is cycled in case recv() times out. In case sending request - * blocks, the time that is used for sending request must be substracted next time - * recv() is called. - */ - timeout = dlc->coap.pending.t0 + dlc->coap.pending.timeout - k_uptime_get_32(); - if (timeout < 0) { - /* All time is spent when sending request and time this - * method is called, there is no time left for receiving; - * skip over recv() and initiate retransmission on next - * cycle - */ - return 0; - } - - return timeout; -} - -int coap_initiate_retransmission(struct download_client *dlc) -{ - if (dlc->coap.pending.timeout == 0) { - return -EINVAL; - } - - if (!coap_pending_cycle(&dlc->coap.pending)) { - LOG_ERR("CoAP max-retransmissions exceeded"); - return -1; - } - - return 0; -} - -static int coap_block_update(struct download_client *dlc, struct coap_packet *pkt, - size_t *blk_off, bool *more) -{ - int err, new_current; - - *blk_off = dlc->coap.block_ctx.current % - coap_block_size_to_bytes(dlc->coap.block_ctx.block_size); - if (*blk_off) { - LOG_DBG("%d bytes of current block already downloaded", - *blk_off); - } - - new_current = coap_get_current_from_response_pkt(pkt); - if (new_current < 0) { - LOG_ERR("Failed to get current from CoAP packet, err %d", new_current); - return new_current; - } - - if (new_current < dlc->coap.block_ctx.current) { - LOG_WRN("Block out of order %d, expected %d", new_current, - dlc->coap.block_ctx.current); - return -1; - } else if (new_current > dlc->coap.block_ctx.current) { - LOG_WRN("Block out of order %d, expected %d", new_current, - dlc->coap.block_ctx.current); - return -1; - } - - err = coap_update_from_block(pkt, &dlc->coap.block_ctx); - if (err) { - return err; - } - - if (dlc->file_size == 0 && dlc->coap.block_ctx.total_size > 0) { - LOG_DBG("Total size: %d", dlc->coap.block_ctx.total_size); - dlc->file_size = dlc->coap.block_ctx.total_size; - } - - *more = coap_next_block(pkt, &dlc->coap.block_ctx); - if (!*more) { - LOG_DBG("Last block received"); - } - - return 0; -} - -int coap_parse(struct download_client *dlc, size_t len) -{ - int err; - size_t blk_off; - uint8_t response_code; - uint16_t payload_len; - const uint8_t *payload; - struct coap_packet response; - bool more; - - /* TODO: currently we stop download on every error, but this is mostly not necessary - * and we can just request the same block again using retry mechanism - */ - - err = coap_packet_parse(&response, dlc->config.buf, len, NULL, 0); - if (err) { - LOG_ERR("Failed to parse CoAP packet, err %d", err); - return -EBADMSG; - } - - - if (coap_header_get_id(&response) != dlc->coap.pending.id) { - LOG_ERR("Response is not pending"); - return -EBADMSG; - } - - coap_pending_clear(&dlc->coap.pending); - - if (coap_header_get_type(&response) != COAP_TYPE_ACK) { - LOG_ERR("Response must be of coap type ACK"); - return -EBADMSG; - } - - response_code = coap_header_get_code(&response); - if (response_code != COAP_RESPONSE_CODE_OK && - response_code != COAP_RESPONSE_CODE_CONTENT) { - LOG_ERR("Server responded with code 0x%x", response_code); - return -EBADMSG; - } - - err = coap_block_update(dlc, &response, &blk_off, &more); - if (err) { - return -EBADMSG; - } - - payload = coap_packet_get_payload(&response, &payload_len); - if (!payload) { - LOG_WRN("No CoAP payload!"); - return -EBADMSG; - } - - /* TODO: because our buffer is large enough for the whole datagram, - * we don't scrictly need to copy the bytes at the beginning - * of the buffer, we could simply send a fragment pointing to the - * payload directly. - */ - LOG_DBG("CoAP response: %d, copying %d bytes", - coap_header_get_code(&response), payload_len - blk_off); - memcpy(dlc->config.buf + dlc->buf_offset, payload + blk_off, - payload_len - blk_off); - - dlc->buf_offset += payload_len - blk_off; - dlc->progress += payload_len - blk_off; - - if (!more) { - /* Mark the end, in case we did not know the total size */ - dlc->file_size = dlc->progress; - } - - dlc->new_data_req = true; - return 0; -} - -int coap_request_send(struct download_client *dlc) -{ - int err; - uint16_t id; - char file[FILENAME_SIZE]; - char *path_elem; - char *path_elem_saveptr; - struct coap_packet request; - - if (has_pending(dlc)) { - id = dlc->coap.pending.id; - } else { - id = coap_next_id(); - } - - err = coap_packet_init(&request, dlc->config.buf, dlc->config.buf_size, COAP_VER, - COAP_TYPE_CON, 8, coap_next_token(), COAP_METHOD_GET, id); - if (err) { - LOG_ERR("Failed to init CoAP message, err %d", err); - return err; - } - - err = url_parse_file(dlc->file, file, sizeof(file)); - if (err) { - LOG_ERR("Unable to parse url"); - return err; - } - - path_elem = strtok_r(file, COAP_PATH_ELEM_DELIM, &path_elem_saveptr); - do { - err = coap_packet_append_option(&request, COAP_OPTION_URI_PATH, - path_elem, strlen(path_elem)); - if (err) { - LOG_ERR("Unable add option to request"); - return err; - } - } while ((path_elem = strtok_r(NULL, COAP_PATH_ELEM_DELIM, &path_elem_saveptr))); - - err = coap_append_block2_option(&request, &dlc->coap.block_ctx); - if (err) { - LOG_ERR("Unable to add block2 option"); - return err; - } - - err = coap_append_size2_option(&request, &dlc->coap.block_ctx); - if (err) { - LOG_ERR("Unable to add size2 option"); - return err; - } - - if (!has_pending(dlc)) { - struct coap_transmission_parameters params = coap_get_transmission_parameters(); - - params.max_retransmission = - CONFIG_DOWNLOAD_CLIENT_COAP_MAX_RETRANSMIT_REQUEST_COUNT; - err = coap_pending_init(&dlc->coap.pending, &request, &dlc->sock.remote_addr, - ¶ms); - if (err < 0) { - return -EINVAL; - } - - coap_pending_cycle(&dlc->coap.pending); - } - - LOG_DBG("CoAP next block: %d", dlc->coap.block_ctx.current); - - err = client_socket_send(dlc, request.offset, dlc->coap.pending.timeout); - if (err) { - LOG_ERR("Failed to send CoAP request, errno %d", errno); - return err; - } - - if (IS_ENABLED(CONFIG_DOWNLOAD_CLIENT_LOG_HEADERS)) { - LOG_HEXDUMP_DBG(request.data, request.offset, "CoAP request"); - } - - return 0; -} diff --git a/subsys/net/lib/download_client/src/download_client.c b/subsys/net/lib/download_client/src/download_client.c index c435b87beb5e..8ab8e1f75131 100644 --- a/subsys/net/lib/download_client/src/download_client.c +++ b/subsys/net/lib/download_client/src/download_client.c @@ -9,48 +9,17 @@ #include #include #include -#if defined(CONFIG_POSIX_API) -#include -#include -#include -#include -#include -#else -#include -#endif -#include -#include #include -#include +#include + #include "download_client_internal.h" +#include LOG_MODULE_REGISTER(download_client, CONFIG_DOWNLOAD_CLIENT_LOG_LEVEL); #define HOSTNAME_SIZE CONFIG_DOWNLOAD_CLIENT_MAX_HOSTNAME_SIZE -static int client_close(struct download_client *dlc); -static int error_evt_send(const struct download_client *dlc, int error); - -#if CONFIG_COAP -bool use_coap(struct download_client *dlc) -{ - return (dlc->sock.proto == IPPROTO_UDP || dlc->sock.proto == IPPROTO_DTLS_1_2); -} -#endif - -bool use_http(struct download_client *dlc) { - return (dlc->sock.proto == IPPROTO_TCP || dlc->sock.proto == IPPROTO_TLS_1_2); -} - -static bool is_state(struct download_client *dlc, enum download_client_state state) -{ - bool ret; - - k_mutex_lock(&dlc->mutex, K_FOREVER); - ret = dlc->state == state; - k_mutex_unlock(&dlc->mutex); - return ret; -} +static int close_evt_send(struct download_client *dlc); #if CONFIG_DOWNLOAD_CLIENT_LOG_LEVEL_DBG char *state_to_str(int state) @@ -86,88 +55,76 @@ static void state_set(struct download_client *dlc, int state) LOG_DBG("state = %d (%s)", state, state_to_str(state)); } -static int client_connect(struct download_client *dlc) +static bool is_state(struct download_client *dlc, enum download_client_state state) { - int err; - int ns_err; - - err = -1; - ns_err = -1; - - err = client_socket_configure_and_connect(dlc); - if (err) { - goto cleanup; - } - -#if CONFIG_COAP - /* Initialize CoAP */ - if (use_coap(dlc)) { - coap_block_init(dlc, dlc->progress); - } -#endif - -cleanup: - if (err) { - /* Unable to connect, close socket */ - client_close(dlc); - state_set(dlc, DOWNLOAD_CLIENT_IDLE); - } + bool ret; - return err; + k_mutex_lock(&dlc->mutex, K_FOREVER); + ret = dlc->state == state; + k_mutex_unlock(&dlc->mutex); + return ret; } -static int client_close(struct download_client *dlc) +static int reconnect(struct download_client *dlc) { - int err; - const struct download_client_evt evt = { - .id = DOWNLOAD_CLIENT_EVT_CLOSED, - }; - - k_mutex_lock(&dlc->mutex, K_FOREVER); + int err = 0; - err = client_socket_close(dlc); + LOG_INF("Reconnecting..."); - dlc->host_config.hostname = NULL; - dlc->file = NULL; + err = ((struct dlc_transport *)dlc->transport)->close(dlc); + if (err) { + LOG_DBG("disconnect failed, %d", err); + } - dlc->config.callback(&evt); + ((struct dlc_transport *)dlc->transport)->close(dlc); - k_mutex_unlock(&dlc->mutex); + state_set(dlc, DOWNLOAD_CLIENT_CONNECTING); return err; } -static int request_send(struct download_client *dlc) -{ - if (dlc->sock.fd < 0) { - return -1; - } +static void restart_and_suspend(struct download_client *dlc) { - if (use_http(dlc)) { - return http_get_request_send(dlc); -#if CONFIG_COAP - } else if (use_coap(dlc)) { - return coap_request_send(dlc); -#endif + if (!is_state(dlc, DOWNLOAD_CLIENT_DOWNLOADING)) { + return; } - return 0; + if (dlc->host_config.close_when_done) { + ((struct dlc_transport *)dlc->transport)->close(dlc); + close_evt_send(dlc); + state_set(dlc, DOWNLOAD_CLIENT_IDLE); + return; + } + state_set(dlc, DOWNLOAD_CLIENT_CONNECTED); } -static int fragment_evt_send(struct download_client *dlc) +static int data_evt_send(const struct download_client *dlc, void *data, size_t len) { - __ASSERT(dlc->buf_offset <= dlc->config.buf_size, - "Buffer overflow!"); - const struct download_client_evt evt = { .id = DOWNLOAD_CLIENT_EVT_FRAGMENT, .fragment = { - .buf = dlc->config.buf, - .len = dlc->buf_offset, + .buf = data, + .len = len, } }; - dlc->buf_offset = 0; + return dlc->config.callback(&evt); +} + +static int download_complete_evt_send(const struct download_client *dlc) +{ + const struct download_client_evt evt = { + .id = DOWNLOAD_CLIENT_EVT_DONE, + }; + + return dlc->config.callback(&evt); +} + +static int close_evt_send(struct download_client *dlc) +{ + const struct download_client_evt evt = { + .id = DOWNLOAD_CLIENT_EVT_CLOSED, + }; return dlc->config.callback(&evt); } @@ -194,166 +151,60 @@ static int deinit_evt_send(const struct download_client *dlc) return dlc->config.callback(&evt); } -static int reconnect(struct download_client *dlc) +/* Events from the transport */ +int dlc_transport_evt_connected(struct download_client *dlc) { - int err = 0; - - LOG_INF("Reconnecting..."); - - if (dlc->sock.fd >= 0) { - err = close(dlc->sock.fd); - if (err) { - LOG_DBG("disconnect failed, %d", err); - } - dlc->sock.fd = -1; - } - k_mutex_lock(&dlc->mutex, K_FOREVER); - state_set(dlc, DOWNLOAD_CLIENT_CONNECTING); + if (dlc->state == DOWNLOAD_CLIENT_CONNECTING) { + state_set(dlc, DOWNLOAD_CLIENT_CONNECTED); + } k_mutex_unlock(&dlc->mutex); - - return err; + return 0; } -static int request_resend(struct download_client *dlc) +int dlc_transport_evt_disconnected(struct download_client *dlc) { -#if CONFIG_COAP - int rc; - - if (use_coap(dlc)) { - rc = coap_initiate_retransmission(dlc); - if (rc) { - error_evt_send(dlc, -ETIMEDOUT); - return -1; - } - } -#endif + state_set(dlc, DOWNLOAD_CLIENT_IDLE); return 0; } -/** - * @brief Handle socket errors. - * - * @param dlc - * @return negative value to stop the handler loop, zero to retry the query. - */ -static int client_revc_error_handle(struct download_client *dlc, ssize_t len) +int dlc_transport_evt_data(struct download_client *dlc, void *data, size_t len) { - int rc; - - if (dlc->sock.fd == -1) { - /* download was aborted */ - return -1; - } - - /* If there is a partial data payload in our buffer, - * and it has been accounted in our progress, we have - * to hand it to the application before discarding it. - */ - if ((dlc->buf_offset > 0) && (dlc->http.header.has_end)) { - rc = fragment_evt_send(dlc); - if (rc) { - /* Restart and suspend */ - LOG_INF("Fragment refused, download stopped."); - return -1; - } - } - - rc = -ECONNRESET; - - if (len == -1) { - if ((errno == ETIMEDOUT) || - (errno == EWOULDBLOCK) || - (errno == EAGAIN)) { - k_mutex_lock(&dlc->mutex, K_FOREVER); - rc = request_resend(dlc); - k_mutex_unlock(&dlc->mutex); - if (rc == -1) { - return -1; - } - rc = -ETIMEDOUT; - } - LOG_ERR("Error in recv(), errno %d", errno); - } + int err; - if (len == 0) { - LOG_WRN("Peer closed connection!"); - } + LOG_DBG("Read %d bytes from transport", len); - /* Notify the application of the error via en event. - * Attempt to reconnect and resume the download - * if the application returns Zero via the event. - */ - rc = error_evt_send(dlc, rc); - if (rc) { - /* Restart and suspend */ - return -1; + if (dlc->file_size) { + LOG_INF("Downloaded %u/%u bytes (%d%%)", + dlc->progress, dlc->file_size, + (dlc->progress * 100) / dlc->file_size); + } else { + LOG_INF("Downloaded %u bytes", dlc->progress); } - rc = reconnect(dlc); - if (rc) { - return -1; + err = data_evt_send(dlc, data, len); + if (err) { + /* Application refused data, suspend */ + restart_and_suspend(dlc); } return 0; } -/* Return: - * 1 wait for more data, - * -1 to stop - * 0 to send a next request - */ -static int client_revc_handle(struct download_client *dlc, ssize_t len) +int dlc_transport_evt_download_complete(struct download_client *dlc) { - int rc; - - LOG_DBG("Read %d bytes from socket", len); - - if (use_http(dlc)) { - rc = http_parse(dlc, len); - if (rc) { - return rc; - } - - if (dlc->http.header.has_end && dlc->buf_offset) { - rc = fragment_evt_send(dlc); - if (rc) { - /* Restart and suspend */ - LOG_INF("Fragment refused (%d), download stopped.", rc); - return rc; - } - } -#if CONFIG_COAP - } else if (use_coap(dlc)) { - rc = coap_parse(dlc, (size_t)len); - if (rc < 0) { - return rc; - } -#endif - } else { - return -EBADMSG; - } + LOG_INF("Download complete"); + download_complete_evt_send(dlc); + restart_and_suspend(dlc); return 0; } -static void restart_and_suspend(struct download_client *dlc) { - if (dlc->host_config.close_when_done) { - client_close(dlc); - state_set(dlc, DOWNLOAD_CLIENT_IDLE); - return; - } - - state_set(dlc, DOWNLOAD_CLIENT_CONNECTED); -} - - void download_thread(void *cli, void *a, void *b) { int rc; - ssize_t len; struct download_client *const dlc = cli; while (true) { @@ -367,24 +218,15 @@ void download_thread(void *cli, void *a, void *b) /* Connect to the target host */ if (is_state(dlc, DOWNLOAD_CLIENT_CONNECTING)) { /* Client */ - rc = client_connect(dlc); + rc = ((struct dlc_transport *)dlc->transport)->connect(dlc); if (rc) { + state_set(dlc, DOWNLOAD_CLIENT_IDLE); error_evt_send(dlc, rc); - /* Try connecting again */ continue; } /* Connection successful */ - k_mutex_lock(&dlc->mutex, K_FOREVER); - if (dlc->file) { - dlc->new_data_req = true; - state_set(dlc, DOWNLOAD_CLIENT_DOWNLOADING); - k_mutex_unlock(&dlc->mutex); - continue; - } - - state_set(dlc, DOWNLOAD_CLIENT_CONNECTED); - k_mutex_unlock(&dlc->mutex); + state_set(dlc, DOWNLOAD_CLIENT_DOWNLOADING); } if (is_state(dlc, DOWNLOAD_CLIENT_CONNECTED)) { @@ -392,79 +234,27 @@ void download_thread(void *cli, void *a, void *b) k_sem_take(&dlc->wait_for_download, K_FOREVER); } - /* Request loop */ if (is_state(dlc, DOWNLOAD_CLIENT_DOWNLOADING)) { - if (dlc->new_data_req) { - /* Request next fragment */ - dlc->buf_offset = 0; - rc = request_send(dlc); + /* Download untill transport returns an error or the download is complete + * (separate event). + */ + rc = ((struct dlc_transport *)dlc->transport)->download(dlc); + if (rc) { + rc = error_evt_send(dlc, rc); if (rc) { - rc = error_evt_send(dlc, -ECONNRESET); - if (rc) { - restart_and_suspend(dlc); - } - - rc = reconnect(dlc); - if (rc) { - restart_and_suspend(dlc); - } - continue; - } - - dlc->new_data_req = false; - } - - __ASSERT(dlc->buf_offset < dlc->config.buf_size, "Buffer overflow"); - - LOG_DBG("Receiving up to %d bytes at %p...", - (dlc->config.buf_size - dlc->buf_offset), - (void *)(dlc->config.buf + dlc->buf_offset)); - - len = client_socket_recv(dlc); - if (len <= 0) { - rc = client_revc_error_handle(dlc, len); - if (rc < 0) { restart_and_suspend(dlc); } - } else { - /* Accumulate buffer offset */ - dlc->buf_offset += len; - rc = client_revc_handle(dlc, len); - if (rc < 0) { - error_evt_send(dlc, rc); - restart_and_suspend(dlc); - } - - if (dlc->file_size) { - LOG_INF("Downloaded %u/%u bytes (%d%%)", - dlc->progress, dlc->file_size, - (dlc->progress * 100) / dlc->file_size); - } else { - LOG_INF("Downloaded %u bytes", dlc->progress); - } - if (dlc->progress == dlc->file_size) { - LOG_INF("Download complete"); - const struct download_client_evt evt = { - .id = DOWNLOAD_CLIENT_EVT_DONE, - }; - dlc->config.callback(&evt); - restart_and_suspend(dlc); - } - } - - /* Attempt to reconnect if the connection was closed */ - if (dlc->http.connection_close) { - dlc->http.connection_close = false; rc = reconnect(dlc); if (rc) { - error_evt_send(dlc, -EHOSTDOWN); + restart_and_suspend(dlc); } } } if (is_state(dlc, DOWNLOAD_CLIENT_DEINITIALIZING)) { - client_close(dlc); + ((struct dlc_transport *)dlc->transport)->close(dlc); + close_evt_send(dlc); state_set(dlc, DOWNLOAD_CLIENT_DEINITIALIZED); deinit_evt_send(dlc); return; @@ -485,7 +275,6 @@ int download_client_init(struct download_client *const dlc, } memset(dlc, 0, sizeof(*dlc)); - dlc->sock.fd = -1; dlc->config = *config; k_sem_init(&dlc->wait_for_download, 0, 1); k_mutex_init(&dlc->mutex); @@ -503,6 +292,8 @@ int download_client_init(struct download_client *const dlc, k_thread_name_set(dlc->tid, "download_client"); + state_set(dlc, DOWNLOAD_CLIENT_IDLE); + k_mutex_unlock(&dlc->mutex); return 0; @@ -521,12 +312,12 @@ int download_client_deinit(struct download_client *const dlc) int download_client_start(struct download_client *dlc, const struct download_client_host_cfg *host_config, - const char *file, size_t from) + const char *uri, size_t from) { int err; - bool host_connected; + struct dlc_transport *transport_connected = NULL; - if (dlc == NULL || host_config == NULL || host_config->hostname == NULL) { + if (dlc == NULL || host_config == NULL || uri == NULL) { return -EINVAL; } @@ -534,88 +325,67 @@ int download_client_start(struct download_client *dlc, if (!is_state(dlc, DOWNLOAD_CLIENT_IDLE) && !is_state(dlc, DOWNLOAD_CLIENT_CONNECTED)) { + LOG_ERR("Invalid start state: %d", dlc->state); k_mutex_unlock(&dlc->mutex); return -EPERM; } - host_connected = is_state(dlc, DOWNLOAD_CLIENT_CONNECTED) && - dlc->host_config.hostname == host_config->hostname; - - dlc->host_config = *host_config; - dlc->file = file; - dlc->file_size = 0; - dlc->progress = from; - dlc->buf_offset = 0; + /* Check if we are already connected to the correct host */ + if (is_state(dlc, DOWNLOAD_CLIENT_CONNECTED)) { + char hostname[CONFIG_DOWNLOAD_CLIENT_MAX_HOSTNAME_SIZE]; - /* Socket configuration */ - err = url_parse_proto(dlc->host_config.hostname, &dlc->sock.proto, &dlc->sock.type); - if (err == -EINVAL) { - LOG_DBG("Protocol not specified, defaulting to HTTP(S)"); - dlc->sock.type = SOCK_STREAM; - if (dlc->host_config.sec_tag_list && (dlc->host_config.sec_tag_count > 0)) { - dlc->sock.proto = IPPROTO_TLS_1_2; - } else { - dlc->sock.proto = IPPROTO_TCP; + err = url_parse_host(uri, hostname, sizeof(hostname)); + if (err) { + LOG_ERR("Failed to parse hostname"); + return EINVAL; + } + if (strncmp(hostname, dlc->hostname, sizeof(hostname)) == 0) { + transport_connected = dlc->transport; } - } else if (err) { - return err; } - if ((dlc->sock.proto == IPPROTO_UDP || dlc->sock.proto == IPPROTO_DTLS_1_2) && - (!IS_ENABLED(CONFIG_COAP))) { - return -EPROTONOSUPPORT; + /* Extract the hostname, without protocol or port */ + err = url_parse_host(uri, dlc->hostname, sizeof(dlc->hostname)); + if (err) { + LOG_ERR("Failed to parse hostname"); + return EINVAL; } - if (dlc->sock.proto == IPPROTO_TLS_1_2 || dlc->sock.proto == IPPROTO_DTLS_1_2) { - if (dlc->host_config.sec_tag_list == NULL || dlc->host_config.sec_tag_count == 0) { - LOG_WRN("No security tag provided for TLS/DTLS"); - return -EINVAL; - } + /* Extract the filename, without protocol or port */ + err = url_parse_file(uri, dlc->file, sizeof(dlc->file)); + if (err) { + LOG_ERR("Failed to parse filename"); + return EINVAL; } - if ((dlc->host_config.sec_tag_list == NULL || dlc->host_config.sec_tag_count == 0) && - dlc->host_config.set_tls_hostname) { - LOG_WRN("set_tls_hostname flag is set for non-TLS connection"); - return -EINVAL; - } + dlc->host_config = *host_config; + dlc->file_size = 0; + dlc->progress = from; + dlc->buf_offset = 0; - err = url_parse_port(dlc->host_config.hostname, &dlc->sock.port); - if (err) { - switch (dlc->sock.proto) { - case IPPROTO_TLS_1_2: - dlc->sock.port = 443; - break; - case IPPROTO_TCP: - dlc->sock.port = 80; - break; - case IPPROTO_DTLS_1_2: - dlc->sock.port = 5684; - break; - case IPPROTO_UDP: - dlc->sock.port = 5683; + dlc->transport = NULL; + + STRUCT_SECTION_FOREACH(dlc_transport_entry, entry) { + if (entry->transport->proto_supported(dlc, uri)) { + dlc->transport = entry->transport; break; } - LOG_DBG("Port not specified, using default: %d", dlc->sock.port); } - if (dlc->host_config.set_native_tls) { - LOG_DBG("Enabled native TLS"); - dlc->sock.type |= SOCK_NATIVE_TLS; - } - - if (use_http(dlc)) { - dlc->http.header.has_end = false; - dlc->http.header.hdr_len = 0; - dlc->http.header.status_code = 0; - } + if (!dlc->transport) { + LOG_ERR("Protocol not specified"); + return -EINVAL; + }; if (is_state(dlc, DOWNLOAD_CLIENT_CONNECTED)) { - if (host_connected) { + if (dlc->transport == transport_connected) { state_set(dlc, DOWNLOAD_CLIENT_DOWNLOADING); + goto out; } else { /* We are connected to the wrong host */ - LOG_DBG("Closing connection to connect to different host"); - client_socket_close(dlc); + LOG_DBG("Closing connection to connect different host or protocol"); + transport_connected->close(dlc); + transport_connected->deinit(dlc); state_set(dlc, DOWNLOAD_CLIENT_CONNECTING); } } else { @@ -623,6 +393,10 @@ int download_client_start(struct download_client *dlc, state_set(dlc, DOWNLOAD_CLIENT_CONNECTING); } + memset(dlc->transport_internal, 0, sizeof(dlc->transport_internal)); + err = ((struct dlc_transport *)dlc->transport)->init(dlc, &dlc->host_config, uri); + +out: k_mutex_unlock(&dlc->mutex); /* Let the thread run */ @@ -636,7 +410,8 @@ int download_client_stop(struct download_client *const dlc) return -EINVAL; } - client_close(dlc); + ((struct dlc_transport *)dlc->transport)->close(dlc); + close_evt_send(dlc); state_set(dlc, DOWNLOAD_CLIENT_IDLE); return 0; @@ -644,22 +419,18 @@ int download_client_stop(struct download_client *const dlc) int download_client_get(struct download_client *dlc, const struct download_client_host_cfg *host_config, - const char *file, size_t from) + const char *url, size_t from) { int rc; - if (dlc == NULL) { + if (dlc == NULL || url == NULL) { return -EINVAL; } - if (file == NULL) { - file = host_config->hostname; - } - k_mutex_lock(&dlc->mutex, K_FOREVER); dlc->host_config.close_when_done = true; - rc = download_client_start(dlc, host_config, file, from); + rc = download_client_start(dlc, host_config, url, from); k_mutex_unlock(&dlc->mutex); diff --git a/subsys/net/lib/download_client/src/parse.c b/subsys/net/lib/download_client/src/parse.c index e38abe3178a0..8b383220b919 100644 --- a/subsys/net/lib/download_client/src/parse.c +++ b/subsys/net/lib/download_client/src/parse.c @@ -23,28 +23,6 @@ static int swallow(const char **str, const char *swallow) return 0; } -int url_parse_proto(const char *url, int *proto, int *type) -{ - if (strncmp(url, "https://", 8) == 0) { - *proto = IPPROTO_TLS_1_2; - *type = SOCK_STREAM; - } else if (strncmp(url, "http://", 7) == 0) { - *proto = IPPROTO_TCP; - *type = SOCK_STREAM; - } else if (strncmp(url, "coaps://", 8) == 0) { - *proto = IPPROTO_DTLS_1_2; - *type = SOCK_DGRAM; - } else if (strncmp(url, "coap://", 7) == 0) { - *proto = IPPROTO_UDP; - *type = SOCK_DGRAM; - } else if (strstr(url, "://")) { - return -EPROTONOSUPPORT; - } else { - return -EINVAL; - } - return 0; -} - int url_parse_host(const char *url, char *host, size_t len) { const char *cur; diff --git a/subsys/net/lib/download_client/src/shell.c b/subsys/net/lib/download_client/src/shell.c index 310603048867..7e428fe93aa2 100644 --- a/subsys/net/lib/download_client/src/shell.c +++ b/subsys/net/lib/download_client/src/shell.c @@ -16,10 +16,20 @@ LOG_MODULE_DECLARE(download_client); static char host[CONFIG_DOWNLOAD_CLIENT_MAX_HOSTNAME_SIZE]; static char file[CONFIG_DOWNLOAD_CLIENT_MAX_FILENAME_SIZE]; -static int sec_tag_list[1]; +static int callback(const struct download_client_evt *event); + +static char dlc_buf[2048]; static struct download_client downloader; +static struct download_client_cfg config = { + .callback = callback, + .buf = dlc_buf, + .buf_size = sizeof(dlc_buf), +}; + +static int sec_tag_list[1]; static struct download_client_host_cfg host_config = { .sec_tag_list = sec_tag_list, + .hostname = host, }; static const struct shell *shell_instance; @@ -61,9 +71,14 @@ static int callback(const struct download_client_evt *event) return 0; } -static int download_shell_init(void) +static int cmd_dc_init(const struct shell *shell, size_t argc, char **argv) { - return download_client_init(&downloader, callback); + return download_client_init(&downloader, &config); +} + +static int cmd_dc_deinit(const struct shell *shell, size_t argc, char **argv) +{ + return download_client_deinit(&downloader); } static int cmd_dc_config(const struct shell *shell, size_t argc, char **argv) @@ -72,11 +87,11 @@ static int cmd_dc_config(const struct shell *shell, size_t argc, char **argv) return 0; } -static int cmd_dc_config_pdn_id(const struct shell *shell, size_t argc, +static int cmd_dc_set_pdn_id(const struct shell *shell, size_t argc, char **argv) { if (argc != 2) { - shell_warn(shell, "usage: dc host_config pdn \n"); + shell_warn(shell, "usage: dc set pdn \n"); return -EINVAL; } @@ -86,11 +101,11 @@ static int cmd_dc_config_pdn_id(const struct shell *shell, size_t argc, return 0; } -static int cmd_dc_config_sec_tag(const struct shell *shell, size_t argc, +static int cmd_dc_set_sec_tag(const struct shell *shell, size_t argc, char **argv) { if (argc != 2) { - shell_warn(shell, "usage: dc host_config sec_tag \n"); + shell_warn(shell, "usage: dc set sec_tag \n"); return -EINVAL; } @@ -114,13 +129,6 @@ static int cmd_dc_set_host(const struct shell *shell, size_t argc, char **argv) memcpy(host, argv[1], MIN(strlen(argv[1]) + 1, sizeof(host))); - err = download_client_connect(&downloader, host, &host_config); - if (err) { - shell_warn(shell, "download_client_connect() failed, err %d", - err); - return -ENOEXEC; - } - return 0; } @@ -178,7 +186,7 @@ static int cmd_dc_get(const struct shell *shell, size_t argc, char **argv) } - err = download_client_get(&downloader, host, &host_config, f, from); + err = download_client_get(&downloader, &host_config, f, from); if (err) { shell_warn(shell, "download_client_get() failed, err %d", @@ -190,28 +198,31 @@ static int cmd_dc_get(const struct shell *shell, size_t argc, char **argv) return 0; } -static int cmd_dc_disconnect(const struct shell *shell, size_t argc, - char **argv) -{ - return download_client_stop(&downloader); -} +SHELL_STATIC_SUBCMD_SET_CREATE(set_options, + SHELL_CMD(hostname, NULL, "Set a target host", cmd_dc_set_host), + SHELL_CMD(sec_tag, NULL, "Set security tag", cmd_dc_set_sec_tag), + SHELL_CMD(pdn_id, NULL, "Set PDN ID", cmd_dc_set_pdn_id), + SHELL_CMD(range_override, NULL, "Set a target host", cmd_dc_set_range_override), + SHELL_CMD(native_tls, NULL, "Set a target host", cmd_dc_set_tls_hostname), + SHELL_CMD(close_when_done, NULL, "Set a target host", cmd_dc_set_tls_hostname), + SHELL_SUBCMD_SET_END +); -SHELL_STATIC_SUBCMD_SET_CREATE(sub_config, - SHELL_CMD(pdn_id, NULL, "Set PDN ID", cmd_dc_config_pdn_id), - SHELL_CMD(sec_tag, NULL, "Set security tag", cmd_dc_config_sec_tag), +SHELL_STATIC_SUBCMD_SET_CREATE(download_options, + SHELL_CMD(start, NULL, "Start downloading file", cmd_dc_dl_start), + SHELL_CMD(stop, NULL, "Stop download", cmd_dc_dl_stop), + SHELL_CMD(fget, NULL, "Get file", cmd_dc_dl_get), + SHELL_CMD(file_size, NULL, "Get file size", cmd_dc_dl_f_size), + SHELL_CMD(downloaded_size, NULL, "Retrieve number of bytes downloaded", cmd_dc_dl_size), SHELL_SUBCMD_SET_END ); SHELL_STATIC_SUBCMD_SET_CREATE(sub_dc, - SHELL_CMD(host_config, &sub_config, "Configure", cmd_dc_config), - SHELL_CMD(set_host, NULL, "Set a target host", cmd_dc_set_host), - SHELL_CMD(disconnect, NULL, "Disconnect from a host", - cmd_dc_disconnect), - SHELL_CMD(download, NULL, "Download a file", cmd_dc_download), - SHELL_CMD(get, NULL, "Download", cmd_dc_get), + SHELL_CMD(init, NULL, "Initialize download client", cmd_dc_init), + SHELL_CMD(deinit, NULL, "Deinitialize download client", cmd_dc_deinit), + SHELL_CMD(set, &set_options, "Set configuration option", cmd_dc_set), + SHELL_CMD(download, &download_options, "Download options", cmd_dc_download), SHELL_SUBCMD_SET_END ); -SYS_INIT(download_shell_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); - SHELL_CMD_REGISTER(dc, &sub_dc, "Download client", NULL); diff --git a/subsys/net/lib/download_client/src/transports/coap.c b/subsys/net/lib/download_client/src/transports/coap.c new file mode 100644 index 000000000000..9a5f4db0529c --- /dev/null +++ b/subsys/net/lib/download_client/src/transports/coap.c @@ -0,0 +1,562 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "download_client_internal.h" + + +LOG_MODULE_DECLARE(download_client, CONFIG_DOWNLOAD_CLIENT_LOG_LEVEL); + +#define COAP_VER 1 +#define FILENAME_SIZE CONFIG_DOWNLOAD_CLIENT_MAX_FILENAME_SIZE +#define COAP_PATH_ELEM_DELIM "/" + +struct transport_params_coap { + /** Initialization status */ + bool initialized; + /** CoAP block context. */ + struct coap_block_context block_ctx; + /** CoAP pending object. */ + struct coap_pending pending; + + struct { + /** Socket descriptor. */ + int fd; + /** Protocol for current download. */ + int proto; + /** Socket type */ + int type; + /** Port */ + uint16_t port; + /** Destination address storage */ + struct sockaddr remote_addr; + } sock; + + /** Request new data */ + bool new_data_req; + /** Request retransmission */ + bool retransmission_req; +}; + +BUILD_ASSERT(CONFIG_DOWNLOAD_CLIENT_TRANSPORT_PARAMS_SIZE >= sizeof(struct transport_params_coap)); + +/* declaration of strtok_r appears to be missing in some cases, + * even though it's defined in the minimal libc, so we forward declare it + */ +extern char *strtok_r(char *str, const char *sep, char **state); + +int url_parse_file(const char *url, char *file, size_t len); + +static int coap_get_current_from_response_pkt(const struct coap_packet *cpkt) +{ + int block = 0; + + block = coap_get_option_int(cpkt, COAP_OPTION_BLOCK2); + if (block < 0) { + return block; + } + + return GET_BLOCK_NUM(block) << (GET_BLOCK_SIZE(block) + 4); +} + +static bool has_pending(struct download_client *dlc) +{ + struct transport_params_coap *coap; + + coap = (struct transport_params_coap *)dlc->transport_internal; + return coap->pending.timeout > 0; +} + +int coap_block_init(struct download_client *dlc, size_t from) +{ + struct transport_params_coap *coap; + + coap = (struct transport_params_coap *)dlc->transport_internal; + + if (coap->initialized) { + return 0; + } + + coap_block_transfer_init(&coap->block_ctx, + 5, 0); //TODO 5 replaced by CONFIG_DOWNLOAD_CLIENT_COAP_BLOCK_SIZE + coap->block_ctx.current = from; + coap_pending_clear(&coap->pending); + + coap->initialized = true; + return 0; +} + +int coap_get_recv_timeout(struct download_client *dlc) +{ + int timeout; + struct transport_params_coap *coap; + + coap = (struct transport_params_coap *)dlc->transport_internal; + + __ASSERT(has_pending(dlc), "Must have coap pending"); + + /* Retransmission is cycled in case recv() times out. In case sending request + * blocks, the time that is used for sending request must be substracted next time + * recv() is called. + */ + timeout = coap->pending.t0 + coap->pending.timeout - k_uptime_get_32(); + if (timeout < 0) { + /* All time is spent when sending request and time this + * method is called, there is no time left for receiving; + * skip over recv() and initiate retransmission on next + * cycle + */ + return 0; + } + + return timeout; +} + +int coap_initiate_retransmission(struct download_client *dlc) +{ + struct transport_params_coap *coap; + + coap = (struct transport_params_coap *)dlc->transport_internal; + + if (coap->pending.timeout == 0) { + return -EINVAL; + } + + if (!coap_pending_cycle(&coap->pending)) { + LOG_ERR("CoAP max-retransmissions exceeded"); + return -1; + } + + return 0; +} + +static int coap_block_update(struct download_client *dlc, struct coap_packet *pkt, + size_t *blk_off, bool *more) +{ + struct transport_params_coap *coap; + + coap = (struct transport_params_coap *)dlc->transport_internal; + + int err, new_current; + + *blk_off = coap->block_ctx.current % + coap_block_size_to_bytes(coap->block_ctx.block_size); + if (*blk_off) { + LOG_DBG("%d bytes of current block already downloaded", + *blk_off); + } + + new_current = coap_get_current_from_response_pkt(pkt); + if (new_current < 0) { + LOG_ERR("Failed to get current from CoAP packet, err %d", new_current); + return new_current; + } + + if (new_current < coap->block_ctx.current) { + LOG_WRN("Block out of order %d, expected %d", new_current, + coap->block_ctx.current); + return -1; + } else if (new_current > coap->block_ctx.current) { + LOG_WRN("Block out of order %d, expected %d", new_current, + coap->block_ctx.current); + return -1; + } + + err = coap_update_from_block(pkt, &coap->block_ctx); + if (err) { + return err; + } + + if (dlc->file_size == 0 && coap->block_ctx.total_size > 0) { + LOG_DBG("Total size: %d", coap->block_ctx.total_size); + dlc->file_size = coap->block_ctx.total_size; + } + + *more = coap_next_block(pkt, &coap->block_ctx); + if (!*more) { + LOG_DBG("Last block received"); + } + + return 0; +} + +static int coap_parse(struct download_client *dlc, size_t len) +{ + int err; + size_t blk_off; + uint8_t response_code; + uint16_t payload_len; + const uint8_t *payload; + struct coap_packet response; + bool more; + struct transport_params_coap *coap; + + coap = (struct transport_params_coap *)dlc->transport_internal; + + /* TODO: currently we stop download on every error, but this is mostly not necessary + * and we can just request the same block again using retry mechanism + */ + + err = coap_packet_parse(&response, dlc->config.buf, len, NULL, 0); + if (err) { + LOG_ERR("Failed to parse CoAP packet, err %d", err); + return -EBADMSG; + } + + + if (coap_header_get_id(&response) != coap->pending.id) { + LOG_ERR("Response is not pending"); + return -EBADMSG; + } + + coap_pending_clear(&coap->pending); + + if (coap_header_get_type(&response) != COAP_TYPE_ACK) { + LOG_ERR("Response must be of coap type ACK"); + return -EBADMSG; + } + + response_code = coap_header_get_code(&response); + if (response_code != COAP_RESPONSE_CODE_OK && + response_code != COAP_RESPONSE_CODE_CONTENT) { + LOG_ERR("Server responded with code 0x%x", response_code); + return -EBADMSG; + } + + err = coap_block_update(dlc, &response, &blk_off, &more); + if (err) { + return -EBADMSG; + } + + payload = coap_packet_get_payload(&response, &payload_len); + if (!payload) { + LOG_WRN("No CoAP payload!"); + return -EBADMSG; + } + + /* TODO: because our buffer is large enough for the whole datagram, + * we don't scrictly need to copy the bytes at the beginning + * of the buffer, we could simply send a fragment pointing to the + * payload directly. + */ + LOG_DBG("CoAP response: %d, copying %d bytes", + coap_header_get_code(&response), payload_len - blk_off); + memcpy(dlc->config.buf + dlc->buf_offset, payload + blk_off, + payload_len - blk_off); + + dlc->buf_offset += payload_len - blk_off; + dlc->progress += payload_len - blk_off; + + if (!more) { + /* Mark the end, in case we did not know the total size */ + dlc->file_size = dlc->progress; + } + + coap->new_data_req = true; + return 0; +} + +static int coap_request_send(struct download_client *dlc) +{ + int err; + uint16_t id; + char file[FILENAME_SIZE]; + char *path_elem; + char *path_elem_saveptr; + struct coap_packet request; + struct transport_params_coap *coap; + + coap = (struct transport_params_coap *)dlc->transport_internal; + + if (has_pending(dlc)) { + id = coap->pending.id; + } else { + id = coap_next_id(); + } + + err = coap_packet_init(&request, dlc->config.buf, dlc->config.buf_size, COAP_VER, + COAP_TYPE_CON, 8, coap_next_token(), COAP_METHOD_GET, id); + if (err) { + LOG_ERR("Failed to init CoAP message, err %d", err); + return err; + } + + err = url_parse_file(dlc->file, file, sizeof(file)); + if (err) { + LOG_ERR("Unable to parse url"); + return err; + } + + path_elem = strtok_r(file, COAP_PATH_ELEM_DELIM, &path_elem_saveptr); + do { + err = coap_packet_append_option(&request, COAP_OPTION_URI_PATH, + path_elem, strlen(path_elem)); + if (err) { + LOG_ERR("Unable add option to request"); + return err; + } + } while ((path_elem = strtok_r(NULL, COAP_PATH_ELEM_DELIM, &path_elem_saveptr))); + + err = coap_append_block2_option(&request, &coap->block_ctx); + if (err) { + LOG_ERR("Unable to add block2 option"); + return err; + } + + err = coap_append_size2_option(&request, &coap->block_ctx); + if (err) { + LOG_ERR("Unable to add size2 option"); + return err; + } + + if (!has_pending(dlc)) { + struct coap_transmission_parameters params = coap_get_transmission_parameters(); + + params.max_retransmission = + CONFIG_DOWNLOAD_CLIENT_COAP_MAX_RETRANSMIT_REQUEST_COUNT; + err = coap_pending_init(&coap->pending, &request, &coap->sock.remote_addr, + ¶ms); + if (err < 0) { + return -EINVAL; + } + + coap_pending_cycle(&coap->pending); + } + + LOG_DBG("CoAP next block: %d", coap->block_ctx.current); + + err = client_socket_send_timeout_set(coap->sock.fd, coap->pending.timeout); + if (err) { + return err; + } + + err = client_socket_send(coap->sock.fd, dlc->config.buf, request.offset); + if (err) { + LOG_ERR("Failed to send CoAP request, errno %d", errno); + return err; + } + + if (IS_ENABLED(CONFIG_DOWNLOAD_CLIENT_LOG_HEADERS)) { + LOG_HEXDUMP_DBG(request.data, request.offset, "CoAP request"); + } + + return 0; +} + +static bool dlc_coap_proto_supported(struct download_client *dlc, const char *uri) +{ + if (strncmp(uri, "coaps://", 8) == 0) { + return true; + } else if (strncmp(uri, "coap://", 7) == 0) { + return true; + } + + return false; +} + +static int dlc_coap_init(struct download_client *dlc, struct download_client_host_cfg *host_cfg, const char *uri) +{ + int err; + struct transport_params_coap *coap; + + coap = (struct transport_params_coap *)dlc->transport_internal; + + coap->sock.proto = IPPROTO_UDP; + coap->sock.type = SOCK_DGRAM; + + if (strncmp(uri, "coaps://", 8) == 0) { + coap->sock.proto = IPPROTO_DTLS_1_2; + coap->sock.type = SOCK_DGRAM; + + if (host_cfg->sec_tag_list == NULL || host_cfg->sec_tag_count == 0) { + LOG_WRN("No security tag provided for TLS/DTLS"); + return -EINVAL; + } + } + + err = url_parse_port(uri, &coap->sock.port); + if (err) { + switch (coap->sock.proto) { + case IPPROTO_DTLS_1_2: + coap->sock.port = 5684; + break; + case IPPROTO_UDP: + coap->sock.port = 5683; + break; + } + LOG_DBG("Port not specified, using default: %d", coap->sock.port); + } + + if (host_cfg->set_native_tls) { + LOG_DBG("Enabled native TLS"); + coap->sock.type |= SOCK_NATIVE_TLS; + } + + return 0; +} + +static int dlc_coap_deinit(struct download_client *dlc) +{ + struct transport_params_coap *coap; + + coap = (struct transport_params_coap *)dlc->transport_internal; + + if (coap->sock.fd != -1) { + client_socket_close(&coap->sock.fd); + dlc_transport_evt_disconnected(dlc); + } + + return 0; +} + +static int dlc_coap_connect(struct download_client *dlc) +{ + int err; + int ns_err; + struct transport_params_coap *coap; + + coap = (struct transport_params_coap *)dlc->transport_internal; + + err = -1; + ns_err = -1; + + err = client_socket_configure_and_connect( + &coap->sock.fd, coap->sock.proto, coap->sock.type, coap->sock.port, + &coap->sock.remote_addr, dlc->hostname, &dlc->host_config); + if (err) { + goto cleanup; + } + + coap_block_init(dlc, dlc->progress); + +cleanup: + if (err) { + /* Unable to connect, close socket */ + client_socket_close(&coap->sock.fd); + } + + return 0; +} + +static int dlc_coap_close(struct download_client *dlc) +{ + int err; + struct transport_params_coap *coap; + + coap = (struct transport_params_coap *)dlc->transport_internal; + + if (coap->sock.fd != -1) { + err = client_socket_close(&coap->sock.fd); + dlc_transport_evt_disconnected(dlc); + return err; + } + + return -EBADF; +} + +static int dlc_coap_download(struct download_client *dlc) +{ + int ret, len, timeout; + struct transport_params_coap *coap; + + coap = (struct transport_params_coap *)dlc->transport_internal; + + //if (coap->connection_close) { + // return -ECONNRESET; + //} + + if (coap->new_data_req) { + /* Request next fragment */ + dlc->buf_offset = 0; + ret = coap_request_send(dlc); + if (ret) { + LOG_DBG("data_req failed, err %d", ret); + /** Attempt reconnection. */ + return -ECONNRESET; + } + + coap->new_data_req = false; + } + + if (coap->retransmission_req) { + dlc->buf_offset = 0; //TODO should this be here? + ret = coap_initiate_retransmission(dlc); + if (ret) { + LOG_DBG("retransmission_req failed, err %d", ret); + /** Attempt reconnection. */ + return -ECONNRESET; + } + + coap->retransmission_req = false; + } + + __ASSERT(dlc->buf_offset < dlc->config.buf_size, "Buffer overflow"); + + LOG_DBG("Receiving up to %d bytes at %p...", + (dlc->config.buf_size - dlc->buf_offset), + (void *)(dlc->config.buf + dlc->buf_offset)); + + timeout = coap_get_recv_timeout(dlc); + if (!timeout) { + return -ETIMEDOUT; + } + + ret = client_socket_recv_timeout_set(coap->sock.fd, timeout); + if (ret) { + return ret; + } + + len = client_socket_recv(coap->sock.fd, + dlc->config.buf + dlc->buf_offset, + dlc->config.buf_size - dlc->buf_offset); + if (len < 0) { + if ((len == ETIMEDOUT) || + (len == EWOULDBLOCK) || + (len == EAGAIN)) { + /* Request data again */ + coap->retransmission_req = true; + return 0; + } + + return len; + } + + ret = coap_parse(dlc, len); + if (ret < 0) { + /* Request data again */ + coap->retransmission_req = true; + return 0; + } + + /* Accumulate buffer offset */ + dlc->progress += len; + dlc->buf_offset += len; + + dlc_transport_evt_data(dlc, dlc->config.buf, len); + if (dlc->progress == dlc->file_size) { + dlc_transport_evt_download_complete(dlc); + } + + return 0; +} + +static struct dlc_transport dlc_transport_coap = { + .proto_supported = dlc_coap_proto_supported, + .init = dlc_coap_init, + .deinit = dlc_coap_deinit, + .connect = dlc_coap_connect, + .close = dlc_coap_close, + .download = dlc_coap_download, +}; + +DLC_TRANSPORT(coap, &dlc_transport_coap); \ No newline at end of file diff --git a/subsys/net/lib/download_client/src/http.c b/subsys/net/lib/download_client/src/transports/http.c similarity index 52% rename from subsys/net/lib/download_client/src/http.c rename to subsys/net/lib/download_client/src/transports/http.c index ae8915d6fbfe..b955fa1341fc 100644 --- a/subsys/net/lib/download_client/src/http.c +++ b/subsys/net/lib/download_client/src/transports/http.c @@ -8,9 +8,11 @@ #include #include #include +#include #include #include #include +#include #include "download_client_internal.h" LOG_MODULE_DECLARE(download_client, CONFIG_DOWNLOAD_CLIENT_LOG_LEVEL); @@ -44,6 +46,44 @@ LOG_MODULE_DECLARE(download_client, CONFIG_DOWNLOAD_CLIENT_LOG_LEVEL); "Connection: keep-alive\r\n" \ "\r\n" +struct transport_params_http { + /** The server has closed the connection. */ + bool connection_close; + /** Is using ranged query. */ + bool ranged; + /** Ranged progress */ + size_t ranged_progress; + /** HTTP header */ + struct { + /** Header length */ + size_t hdr_len; + /** Status code */ + unsigned long status_code; + /** Whether the HTTP header for + * the current fragment has been processed. + */ + bool has_end; + } header; + + struct { + /** Socket descriptor. */ + int fd; + /** Protocol for current download. */ + int proto; + /** Socket type */ + int type; + /** Port */ + uint16_t port; + /** Destination address storage */ + struct sockaddr remote_addr; + } sock; + + /** Request new data */ + bool new_data_req; +}; + +BUILD_ASSERT(CONFIG_DOWNLOAD_CLIENT_TRANSPORT_PARAMS_SIZE >= sizeof(struct transport_params_http)); + extern char *strnstr(const char *haystack, const char *needle, size_t haystack_sz); int http_get_request_send(struct download_client *dlc) @@ -51,27 +91,15 @@ int http_get_request_send(struct download_client *dlc) int err; int len; size_t off = 0; - char host[HOSTNAME_SIZE]; - char file[FILENAME_SIZE]; bool tls_force_range; + struct transport_params_http *http; - __ASSERT_NO_MSG(dlc->host_config.hostname); - __ASSERT_NO_MSG(dlc->file); + http = (struct transport_params_http *)dlc->transport_internal; - dlc->http.header.has_end = false; - - err = url_parse_host(dlc->host_config.hostname, host, sizeof(host)); - if (err) { - return err; - } - - err = url_parse_file(dlc->file, file, sizeof(file)); - if (err) { - return err; - } + http->header.has_end = false; /* nRF91 series has a limitation of decoding ~2k of data at once when using TLS */ - tls_force_range = (dlc->sock.proto == IPPROTO_TLS_1_2 && + tls_force_range = (http->sock.proto == IPPROTO_TLS_1_2 && !dlc->host_config.set_native_tls && IS_ENABLED(CONFIG_SOC_SERIES_NRF91X)); @@ -94,20 +122,21 @@ int http_get_request_send(struct download_client *dlc) len = snprintf(dlc->config.buf, dlc->config.buf_size, - HTTP_GET_RANGE, file, host, dlc->progress, off); - dlc->http.ranged = true; - dlc->http.ranged_progress = 0; + HTTP_GET_RANGE, dlc->file, dlc->hostname, dlc->progress, off); + http->ranged = true; + http->ranged_progress = 0; + LOG_DBG("Range request up to %d bytes", dlc->host_config.range_override); goto send; } else if (dlc->progress) { len = snprintf(dlc->config.buf, dlc->config.buf_size, - HTTP_GET_OFFSET, file, host, dlc->progress); - dlc->http.ranged = false; + HTTP_GET_OFFSET, dlc->file, dlc->hostname, dlc->progress); + http->ranged = false; } else { len = snprintf(dlc->config.buf, dlc->config.buf_size, - HTTP_GET, file, host); - dlc->http.ranged = false; + HTTP_GET, dlc->file, dlc->hostname); + http->ranged = false; } send: @@ -120,7 +149,7 @@ int http_get_request_send(struct download_client *dlc) LOG_HEXDUMP_DBG(dlc->config.buf, len, "HTTP request"); } - err = client_socket_send(dlc, len, 0); + err = client_socket_send(http->sock.fd, dlc->config.buf, len); if (err) { LOG_ERR("Failed to send HTTP request, errno %d", errno); return err; @@ -139,11 +168,14 @@ static int http_header_parse(struct download_client *dlc, size_t buf_len) char *q; size_t parse_len; unsigned int expected_status; + struct transport_params_http *http; + + http = (struct transport_params_http *)dlc->transport_internal; p = strnstr(dlc->config.buf, "\r\n\r\n", dlc->config.buf_size); - if (p && p <= dlc->config.buf + dlc->buf_offset) { + if (p) { /* End of header received */ - dlc->http.header.has_end = true; + http->header.has_end = true; parse_len = p + strlen("\r\n\r\n") - (char *)dlc->config.buf; } else { parse_len = buf_len; @@ -160,7 +192,7 @@ static int http_header_parse(struct download_client *dlc, size_t buf_len) if (q) { /* Received entire line */ p += strlen("http/1.1 "); - dlc->http.header.status_code = strtoul(p, &q, 10); + http->header.status_code = strtoul(p, &q, 10); } } @@ -170,7 +202,7 @@ static int http_header_parse(struct download_client *dlc, size_t buf_len) */ do { if (dlc->file_size == 0) { - if (dlc->http.ranged) { + if (http->ranged) { p = strnstr(dlc->config.buf, "\r\ncontent-range", parse_len); if (!p) { break; @@ -212,10 +244,10 @@ static int http_header_parse(struct download_client *dlc, size_t buf_len) p = strnstr(dlc->config.buf, "\r\nconnection: close", parse_len); if (p) { LOG_WRN("Peer closed connection, will re-connect"); - dlc->http.connection_close = true; + http->connection_close = true; } - if (dlc->http.header.has_end) { + if (http->header.has_end) { /* We have received the end of the header. * Verify that we have received everything that we need. */ @@ -225,14 +257,14 @@ static int http_header_parse(struct download_client *dlc, size_t buf_len) return -EBADMSG; } - if (!dlc->http.header.status_code) { + if (!http->header.status_code) { LOG_ERR("Server response malformed: status code not found"); return -EBADMSG; } - expected_status = (dlc->http.ranged || dlc->progress) ? 206 : 200; - if (dlc->http.header.status_code != expected_status) { - LOG_ERR("Unexpected HTTP response code %ld", dlc->http.header.status_code); + expected_status = (http->ranged || dlc->progress) ? 206 : 200; + if (http->header.status_code != expected_status) { + LOG_ERR("Unexpected HTTP response code %ld", http->header.status_code); return -EBADMSG; } @@ -258,14 +290,17 @@ static int http_header_parse(struct download_client *dlc, size_t buf_len) } /* Returns: - * 0 on success + * length of data payload received on success * Negative errno on error. */ int http_parse(struct download_client *dlc, size_t len) { int parsed_len; + struct transport_params_http *http; + + http = (struct transport_params_http *)dlc->transport_internal; - if (!dlc->http.header.has_end) { + if (!http->header.has_end) { /* Parse what we can from the header */ parsed_len = http_header_parse(dlc, len); if (parsed_len < 0) { @@ -273,6 +308,8 @@ int http_parse(struct download_client *dlc, size_t len) return -EBADMSG; } + LOG_DBG("Parsed len: %d", parsed_len); + if (parsed_len == len) { len = 0; dlc->buf_offset = 0; @@ -283,7 +320,7 @@ int http_parse(struct download_client *dlc, size_t len) dlc->buf_offset = len; } - if (!dlc->http.header.has_end) { + if (!http->header.has_end) { if (dlc->config.buf_size == dlc->buf_offset) { LOG_ERR("Could not parse HTTP header lines from server (> %d)", dlc->config.buf_size); @@ -294,26 +331,214 @@ int http_parse(struct download_client *dlc, size_t len) } } - /* Accumulate overall file progress. */ - dlc->progress += len; - /* Have we received a whole fragment or the whole file? */ - if (dlc->progress != dlc->file_size) { - if (dlc->http.ranged) { - dlc->http.ranged_progress += len; - if (dlc->http.ranged_progress < (dlc->host_config.range_override ? + if (dlc->progress + len != dlc->file_size) { + if (http->ranged) { + http->ranged_progress += len; + if (http->ranged_progress < (dlc->host_config.range_override ? dlc->host_config.range_override : - TLS_RANGE_MAX)) { + TLS_RANGE_MAX - 1)) { /* Ranged query: read until a full fragment */ - return 0; + return len; } } else { /* Non-ranged query: just keep on reading, ignore fragment size */ - return 0; + return len; } } /* Either we have a full file, or we need to request a next fragment */ - dlc->new_data_req = true; + http->new_data_req = true; + return len; +} + +static bool dlc_http_proto_supported(struct download_client *dlc, const char *uri) +{ + if (strncmp(uri, "https://", 8) == 0) { + return true; + } + + if (strncmp(uri, "http://", 7) == 0) { + return true; + } + + return false; +} + +static int dlc_http_init(struct download_client *dlc, struct download_client_host_cfg *host_cfg, const char *uri) +{ + int err; + struct transport_params_http *http; + + http = (struct transport_params_http *)dlc->transport_internal; + + http->sock.proto = IPPROTO_TCP; + http->sock.type = SOCK_STREAM; + + if (strncmp(uri, "https://", 8) == 0) { + http->sock.proto = IPPROTO_TLS_1_2; + http->sock.type = SOCK_STREAM; + + if (host_cfg->sec_tag_list == NULL || host_cfg->sec_tag_count == 0) { + LOG_WRN("No security tag provided for TLS/DTLS"); + return -EINVAL; + } + } + + err = url_parse_port(uri, &http->sock.port); + if (err) { + switch (http->sock.proto) { + case IPPROTO_TLS_1_2: + http->sock.port = 443; + break; + case IPPROTO_TCP: + http->sock.port = 80; + break; + } + LOG_DBG("Port not specified, using default: %d", http->sock.port); + } + + if (host_cfg->set_native_tls) { + LOG_DBG("Enabled native TLS"); + http->sock.type |= SOCK_NATIVE_TLS; + } + + return 0; +} + +static int dlc_http_deinit(struct download_client *dlc) +{ + struct transport_params_http *http; + + http = (struct transport_params_http *)dlc->transport_internal; + + if (http->sock.fd != -1) { + client_socket_close(&http->sock.fd); + dlc_transport_evt_disconnected(dlc); + } + return 0; } + +static int dlc_http_connect(struct download_client *dlc) +{ + int err; + int ns_err; + struct transport_params_http *http; + + http = (struct transport_params_http *)dlc->transport_internal; + + err = -1; + ns_err = -1; + + if (!http->sock.remote_addr.sa_family) { + client_socket_host_lookup(dlc->hostname, dlc->host_config.pdn_id, &http->sock.remote_addr); + } + + err = client_socket_configure_and_connect( + &http->sock.fd, http->sock.proto, http->sock.type, http->sock.port, + &http->sock.remote_addr, dlc->hostname, &dlc->host_config); + if (err) { + return err; + } + + err = client_socket_recv_timeout_set(http->sock.fd, + CONFIG_DOWNLOAD_CLIENT_HTTP_TIMEO_MS); + if (err) { + /* Unable to connect, close socket */ + client_socket_close(&http->sock.fd); + return err; + } + + http->new_data_req = true; + + dlc_transport_evt_connected(dlc); + + return err; +} + +static int dlc_http_close(struct download_client *dlc) +{ + int err; + struct transport_params_http *http; + + http = (struct transport_params_http *)dlc->transport_internal; + + if (http->sock.fd != -1) { + err = client_socket_close(&http->sock.fd); + dlc_transport_evt_disconnected(dlc); + return err; + } + + return -EBADF; +} + +static int dlc_http_download(struct download_client *dlc) +{ + int ret, len; + struct transport_params_http *http; + + http = (struct transport_params_http *)dlc->transport_internal; + + if (http->connection_close) { + return -ECONNRESET; + } + + if (http->new_data_req) { + /* Request next fragment */ + dlc->buf_offset = 0; + ret = http_get_request_send(dlc); + if (ret) { + LOG_DBG("data_req failed, err %d", ret); + /** Attempt reconnection. */ + return -ECONNRESET; + } + + http->new_data_req = false; + } + + __ASSERT(dlc->buf_offset < dlc->config.buf_size, "Buffer overflow"); + + LOG_DBG("Receiving up to %d bytes at %p...", + (dlc->config.buf_size - dlc->buf_offset), + (void *)(dlc->config.buf + dlc->buf_offset)); + + len = client_socket_recv(http->sock.fd, + dlc->config.buf + dlc->buf_offset, + dlc->config.buf_size - dlc->buf_offset); + if (len < 0) { + return len; + } + + if (len == 0) { + return -ECONNRESET; + } + + ret = http_parse(dlc, len); + if (ret <= 0) { + return ret; + } + + if (http->header.has_end ) { + /* Accumulate progress */ + dlc->progress += ret; + dlc_transport_evt_data(dlc, dlc->config.buf, ret); + if (dlc->progress == dlc->file_size) { + dlc_transport_evt_download_complete(dlc); + } + dlc->buf_offset = 0; + } + + return 0; +} + +static struct dlc_transport dlc_transport_http = { + .proto_supported = dlc_http_proto_supported, + .init = dlc_http_init, + .deinit = dlc_http_deinit, + .connect = dlc_http_connect, + .close = dlc_http_close, + .download = dlc_http_download, +}; + +DLC_TRANSPORT(http, &dlc_transport_http); \ No newline at end of file diff --git a/subsys/net/lib/fota_download/src/fota_download.c b/subsys/net/lib/fota_download/src/fota_download.c index 86325d3c269d..7907ea75eb85 100644 --- a/subsys/net/lib/fota_download/src/fota_download.c +++ b/subsys/net/lib/fota_download/src/fota_download.c @@ -36,6 +36,13 @@ static const char *dl_file; static uint32_t dl_host_hash; static uint32_t dl_file_hash; static struct download_client dlc; +static int download_client_callback(const struct download_client_evt *event); +static char dlc_buf[CONFIG_FOTA_DOWNLOAD_FULL_MODEM_BUF_SZ]; +static struct download_client_cfg dlc_config = { + .callback = download_client_callback, + .buf = dlc_buf, + .buf_size = sizeof(dlc_buf), +}; /** SMP MCUBoot image type */ static bool use_smp_dfu_target; static struct k_work_delayable dlc_with_offset_work; @@ -410,19 +417,6 @@ static void download_with_offset(struct k_work *unused) return; } -static bool is_ip_address(const char *host) -{ - struct sockaddr sa; - - if (zsock_inet_pton(AF_INET, host, sa.data) == 1) { - return true; - } else if (zsock_inet_pton(AF_INET6, host, sa.data) == 1) { - return true; - } - - return false; -} - int fota_download_b1_file_parse(char *s0_s1_files) { #if !defined(PM_S1_ADDRESS) @@ -607,10 +601,6 @@ int fota_download(const char *host, const char *file, config.sec_tag_count = sec_tag_count; config.sec_tag_list = sec_tag_list_copy; - - if (!is_ip_address(host)) { - config.set_tls_hostname = true; - } } socket_retries_left = CONFIG_FOTA_SOCKET_RETRIES; @@ -679,7 +669,7 @@ static int fota_download_object_init(void) k_work_init_delayable(&dlc_with_offset_work, download_with_offset); - err = download_client_init(&dlc, download_client_callback); + err = download_client_init(&dlc, &dlc_config); if (err != 0) { return err; } diff --git a/subsys/net/lib/nrf_cloud/src/nrf_cloud_pgps_utils.c b/subsys/net/lib/nrf_cloud/src/nrf_cloud_pgps_utils.c index 01b7de954541..6c4df816361c 100644 --- a/subsys/net/lib/nrf_cloud/src/nrf_cloud_pgps_utils.c +++ b/subsys/net/lib/nrf_cloud/src/nrf_cloud_pgps_utils.c @@ -47,13 +47,20 @@ static struct nrf_cloud_pgps_header saved_header; static K_SEM_DEFINE(dl_active, 1, 1); +static char dlc_buf[2048]; static struct download_client dlc; +static int download_client_callback(const struct download_client_evt *event); +static struct download_client_cfg dlc_config = { + .callback = download_client_callback, + .buf = dlc_buf, + .buf_size = sizeof(dlc_buf), +}; + static int sec_tag_list[1]; static int socket_retries_left; static npgps_buffer_handler_t buffer_handler; static npgps_eot_handler_t eot_handler; -static int download_client_callback(const struct download_client_evt *event); static int settings_set(const char *key, size_t len_rd, settings_read_cb read_cb, void *cb_arg); @@ -473,7 +480,7 @@ int npgps_download_init(npgps_buffer_handler_t buf_handler, npgps_eot_handler_t buffer_handler = buf_handler; eot_handler = end_handler; - return download_client_init(&dlc, download_client_callback); + return download_client_init(&dlc, &dlc_config); } int npgps_download_start(const char *host, const char *file, int sec_tag, @@ -502,7 +509,6 @@ int npgps_download_start(const char *host, const char *file, int sec_tag, .sec_tag_list = NULL, .pdn_id = pdn_id, .range_override = fragment_size, - .set_tls_hostname = false, .family = family }, .dlc = &dlc @@ -512,7 +518,6 @@ int npgps_download_start(const char *host, const char *file, int sec_tag, sec_tag_list[0] = sec_tag; dl.dl_cfg.sec_tag_list = sec_tag_list; dl.dl_cfg.sec_tag_count = 1; - dl.dl_cfg.set_tls_hostname = true; } socket_retries_left = SOCKET_RETRIES; diff --git a/tests/subsys/net/lib/download_client/src/main.c b/tests/subsys/net/lib/download_client/src/main.c index a87b34f4a4ce..37866e6a94d5 100644 --- a/tests/subsys/net/lib/download_client/src/main.c +++ b/tests/subsys/net/lib/download_client/src/main.c @@ -14,7 +14,14 @@ #include "mock/dl_coap.h" K_PIPE_DEFINE(event_pipe, 10, _Alignof(struct download_client_evt)); +static int download_client_callback(const struct download_client_evt *event); static struct download_client client; +static char dlc_buf[2048]; +static struct download_client_cfg dlc_config = { + .callback = download_client_callback, + .buf = dlc_buf, + .buf_size = sizeof(dlc_buf), +}; static const struct sockaddr_in addr_coap_me_http = { .sin_family = AF_INET, .sin_port = htons(80), @@ -91,7 +98,7 @@ static void init(void) if (!initialized) { memset(&client, 0, sizeof(struct download_client)); - err = download_client_init(&client, download_client_callback); + err = download_client_init(&client, &dlc_config); zassert_ok(err, NULL); initialized = true; } diff --git a/tests/subsys/net/lib/fota_download/src/test_fota_download.c b/tests/subsys/net/lib/fota_download/src/test_fota_download.c index 43178306e053..c0f0c70d4e7c 100644 --- a/tests/subsys/net/lib/fota_download/src/test_fota_download.c +++ b/tests/subsys/net/lib/fota_download/src/test_fota_download.c @@ -86,10 +86,10 @@ int download_client_file_size_get(struct download_client *client, size_t *size) return 0; } -int download_client_init(struct download_client *client, - download_client_callback_t callback) +int download_client_init(struct download_client *const dlc, + struct download_client_cfg *config) { - download_client_event_handler = callback; + download_client_event_handler = config->callback; client->fd = -1; return 0; }