| /* client.c */ |
| |
| /* |
| * Copyright (c) 2017 Intel Corporation. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #if defined(CONFIG_NET_DEBUG_APP) |
| #define SYS_LOG_DOMAIN "net/app" |
| #define NET_SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG |
| #define NET_LOG_ENABLED 1 |
| #endif |
| |
| #include <zephyr.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <stdlib.h> |
| |
| #include <net/net_core.h> |
| #include <net/net_ip.h> |
| #include <net/net_if.h> |
| #include <net/dns_resolve.h> |
| |
| #include <net/net_app.h> |
| |
| #include "../../ip/udp_internal.h" |
| |
| #include "net_app_private.h" |
| |
| #if defined(CONFIG_NET_APP_TLS) || defined(CONFIG_NET_APP_DTLS) |
| #define TLS_STARTUP_TIMEOUT K_SECONDS(5) |
| static int start_tls_client(struct net_app_ctx *ctx); |
| #endif /* CONFIG_NET_APP_TLS || CONFIG_NET_APP_DTLS */ |
| |
| #if defined(CONFIG_DNS_RESOLVER) |
| static void dns_cb(enum dns_resolve_status status, |
| struct dns_addrinfo *info, |
| void *user_data) |
| { |
| struct net_app_ctx *ctx = user_data; |
| |
| if (!(status == DNS_EAI_INPROGRESS && info)) { |
| return; |
| } |
| |
| if (info->ai_family == AF_INET) { |
| #if defined(CONFIG_NET_IPV4) |
| net_ipaddr_copy(&net_sin(&ctx->ipv4.remote)->sin_addr, |
| &net_sin(&info->ai_addr)->sin_addr); |
| ctx->ipv4.remote.sa_family = info->ai_family; |
| #else |
| goto out; |
| #endif |
| } else if (info->ai_family == AF_INET6) { |
| #if defined(CONFIG_NET_IPV6) |
| net_ipaddr_copy(&net_sin6(&ctx->ipv6.remote)->sin6_addr, |
| &net_sin6(&info->ai_addr)->sin6_addr); |
| ctx->ipv6.remote.sa_family = info->ai_family; |
| #else |
| goto out; |
| #endif |
| } else { |
| goto out; |
| } |
| |
| out: |
| k_sem_give(&ctx->client.dns_wait); |
| } |
| |
| static int resolve_name(struct net_app_ctx *ctx, |
| const char *peer_addr_str, |
| enum dns_query_type type, |
| s32_t timeout) |
| { |
| int ret; |
| |
| k_sem_init(&ctx->client.dns_wait, 0, 1); |
| |
| ret = dns_get_addr_info(peer_addr_str, type, &ctx->client.dns_id, |
| dns_cb, ctx, timeout); |
| if (ret < 0) { |
| NET_ERR("Cannot resolve %s (%d)", peer_addr_str, ret); |
| ctx->client.dns_id = 0; |
| return ret; |
| } |
| |
| /* Wait a little longer for the DNS to finish so that |
| * the DNS will timeout before the semaphore. |
| */ |
| if (k_sem_take(&ctx->client.dns_wait, timeout + K_SECONDS(1))) { |
| NET_ERR("Timeout while resolving %s", peer_addr_str); |
| ctx->client.dns_id = 0; |
| return -ETIMEDOUT; |
| } |
| |
| ctx->client.dns_id = 0; |
| |
| if (ctx->default_ctx->remote.sa_family == AF_UNSPEC) { |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| #endif /* CONFIG_DNS_RESOLVER */ |
| |
| static int try_resolve(struct net_app_ctx *ctx, |
| const char *peer_addr_str, |
| enum dns_query_type type, |
| s32_t timeout) |
| { |
| #if !defined(CONFIG_DNS_RESOLVER) |
| NET_ERR("Invalid IP address %s", peer_addr_str); |
| return -EINVAL; |
| #else |
| int ret; |
| |
| ret = resolve_name(ctx, peer_addr_str, type, timeout); |
| if (ret < 0) { |
| NET_ERR("Cannot resolve %s (%d)", peer_addr_str, ret); |
| } |
| |
| return ret; |
| #endif |
| } |
| |
| static int set_remote_addr(struct net_app_ctx *ctx, |
| struct sockaddr *remote_addr, |
| const char *peer_addr_str, |
| bool peer_addr_ok, |
| s32_t timeout) |
| { |
| int ret; |
| |
| if (peer_addr_ok && remote_addr->sa_family == AF_INET6) { |
| #if defined(CONFIG_NET_IPV6) |
| memcpy(&ctx->ipv6.remote, remote_addr, |
| sizeof(struct sockaddr)); |
| ctx->default_ctx = &ctx->ipv6; |
| return 0; |
| #else |
| return -EAFNOSUPPORT; |
| #endif |
| } |
| |
| if (peer_addr_ok && remote_addr->sa_family == AF_INET) { |
| #if defined(CONFIG_NET_IPV4) |
| memcpy(&ctx->ipv4.remote, remote_addr, |
| sizeof(struct sockaddr)); |
| ctx->default_ctx = &ctx->ipv4; |
| return 0; |
| #else |
| return -EAFNOSUPPORT; |
| #endif |
| } |
| |
| #if defined(CONFIG_NET_IPV6) && !defined(CONFIG_NET_IPV4) |
| /* Could be hostname, try DNS if configured. */ |
| ret = try_resolve(ctx, peer_addr_str, DNS_QUERY_TYPE_AAAA, timeout); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| ctx->default_ctx = &ctx->ipv6; |
| return 0; |
| #endif /* IPV6 && !IPV4 */ |
| |
| #if defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_IPV6) |
| ret = try_resolve(ctx, peer_addr_str, DNS_QUERY_TYPE_A, timeout); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| ctx->default_ctx = &ctx->ipv4; |
| return 0; |
| #endif /* IPV6 && !IPV4 */ |
| |
| #if defined(CONFIG_NET_IPV4) && defined(CONFIG_NET_IPV6) |
| ret = try_resolve(ctx, peer_addr_str, DNS_QUERY_TYPE_A, timeout); |
| if (ret < 0) { |
| ret = try_resolve(ctx, peer_addr_str, DNS_QUERY_TYPE_AAAA, |
| timeout); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| ctx->default_ctx = &ctx->ipv6; |
| return 0; |
| } |
| |
| ctx->default_ctx = &ctx->ipv4; |
| return 0; |
| #endif /* IPV4 && IPV6 */ |
| } |
| |
| static int get_port_number(const char *peer_addr_str, |
| char *buf, |
| size_t buf_len) |
| { |
| u16_t port = 0; |
| char *ptr; |
| int count, i; |
| |
| if (peer_addr_str[0] == '[') { |
| #if defined(CONFIG_NET_IPV6) |
| /* IPv6 address with port number */ |
| int end; |
| |
| ptr = strstr(peer_addr_str, "]:"); |
| if (!ptr) { |
| return -EINVAL; |
| } |
| |
| end = min(INET6_ADDRSTRLEN, ptr - (peer_addr_str + 1)); |
| memcpy(buf, peer_addr_str + 1, end); |
| buf[end] = '\0'; |
| |
| port = strtol(ptr + 2, NULL, 10); |
| |
| return port; |
| #else |
| return -EAFNOSUPPORT; |
| #endif /* CONFIG_NET_IPV6 */ |
| } |
| |
| count = i = 0; |
| while (peer_addr_str[i]) { |
| if (peer_addr_str[i] == ':') { |
| count++; |
| } |
| |
| i++; |
| } |
| |
| if (count == 1) { |
| #if defined(CONFIG_NET_IPV4) |
| /* IPv4 address with port number */ |
| int end; |
| |
| ptr = strstr(peer_addr_str, ":"); |
| if (!ptr) { |
| return -EINVAL; |
| } |
| |
| end = min(NET_IPV4_ADDR_LEN, ptr - peer_addr_str); |
| memcpy(buf, peer_addr_str, end); |
| buf[end] = '\0'; |
| |
| port = strtol(ptr + 1, NULL, 10); |
| |
| return port; |
| #else |
| return -EAFNOSUPPORT; |
| #endif /* CONFIG_NET_IPV4 */ |
| } |
| |
| return 0; |
| } |
| |
| static void close_net_ctx(struct net_app_ctx *ctx) |
| { |
| #if defined(CONFIG_NET_IPV6) |
| if (ctx->ipv6.ctx) { |
| net_context_put(ctx->ipv6.ctx); |
| ctx->ipv6.ctx = NULL; |
| } |
| #endif |
| #if defined(CONFIG_NET_IPV4) |
| if (ctx->ipv4.ctx) { |
| net_context_put(ctx->ipv4.ctx); |
| ctx->ipv4.ctx = NULL; |
| } |
| #endif |
| #if defined(CONFIG_NET_APP_SERVER) |
| { |
| int i; |
| |
| for (i = 0; i < CONFIG_NET_APP_SERVER_NUM_CONN; i++) { |
| if (ctx->server.net_ctxs[i]) { |
| net_context_put(ctx->server.net_ctxs[i]); |
| ctx->server.net_ctxs[i] = NULL; |
| } |
| } |
| } |
| #endif |
| } |
| |
| static int bind_local(struct net_app_ctx *ctx) |
| { |
| int ret = 0; |
| |
| #if defined(CONFIG_NET_IPV4) |
| if (ctx->ipv4.remote.sa_family == AF_INET && ctx->ipv4.ctx) { |
| ctx->ipv4.local.sa_family = AF_INET; |
| _net_app_set_local_addr(&ctx->ipv4.local, NULL, |
| net_sin(&ctx->ipv4.local)->sin_port); |
| |
| ret = _net_app_set_net_ctx(ctx, ctx->ipv4.ctx, |
| &ctx->ipv4.local, |
| sizeof(struct sockaddr_in), |
| ctx->proto); |
| if (ret < 0) { |
| net_context_put(ctx->ipv4.ctx); |
| ctx->ipv4.ctx = NULL; |
| } |
| } |
| #endif |
| |
| #if defined(CONFIG_NET_IPV6) |
| if (ctx->ipv6.remote.sa_family == AF_INET6 && ctx->ipv6.ctx) { |
| ctx->ipv6.local.sa_family = AF_INET6; |
| _net_app_set_local_addr(&ctx->ipv6.local, NULL, |
| net_sin6(&ctx->ipv6.local)->sin6_port); |
| |
| ret = _net_app_set_net_ctx(ctx, ctx->ipv6.ctx, |
| &ctx->ipv6.local, |
| sizeof(struct sockaddr_in6), |
| ctx->proto); |
| if (ret < 0) { |
| net_context_put(ctx->ipv6.ctx); |
| ctx->ipv6.ctx = NULL; |
| } |
| } |
| #endif |
| |
| return ret; |
| } |
| |
| int net_app_init_client(struct net_app_ctx *ctx, |
| enum net_sock_type sock_type, |
| enum net_ip_protocol proto, |
| struct sockaddr *client_addr, |
| struct sockaddr *peer_addr, |
| const char *peer_addr_str, |
| u16_t peer_port, |
| s32_t timeout, |
| void *user_data) |
| { |
| const char *base_peer_addr = peer_addr_str; |
| char base_addr_str[INET6_ADDRSTRLEN + 1]; |
| struct sockaddr remote_addr; |
| struct sockaddr addr; |
| int ret, addr_ok = false; |
| |
| if (!ctx) { |
| return -EINVAL; |
| } |
| |
| if (ctx->is_init) { |
| return -EALREADY; |
| } |
| |
| memset(&addr, 0, sizeof(addr)); |
| memset(&remote_addr, 0, sizeof(remote_addr)); |
| |
| if (peer_addr) { |
| memcpy(&remote_addr, peer_addr, sizeof(remote_addr)); |
| } else if (peer_addr_str) { |
| /* If the peer string contains port number, use that and |
| * ignore the port number parameter. |
| */ |
| ret = get_port_number(peer_addr_str, base_addr_str, |
| sizeof(base_addr_str)); |
| if (ret > 0) { |
| base_peer_addr = base_addr_str; |
| peer_port = ret; |
| } else { |
| strncpy(base_addr_str, peer_addr_str, |
| sizeof(base_addr_str) - 1); |
| } |
| |
| addr_ok = net_ipaddr_parse(base_peer_addr, |
| strlen(base_peer_addr), |
| &remote_addr); |
| |
| #if defined(CONFIG_NET_IPV6) |
| if (remote_addr.sa_family == AF_INET6) { |
| net_sin6(&remote_addr)->sin6_port = htons(peer_port); |
| } |
| #endif |
| #if defined(CONFIG_NET_IPV4) |
| if (remote_addr.sa_family == AF_INET) { |
| net_sin(&remote_addr)->sin_port = htons(peer_port); |
| } |
| #endif |
| |
| /* The remote_addr will be used by set_remote_addr() to |
| * set the actual peer address. |
| */ |
| } |
| |
| if (client_addr) { |
| memcpy(&addr, client_addr, sizeof(addr)); |
| |
| if (addr.sa_family != remote_addr.sa_family) { |
| NET_DBG("Address family mismatch %d vs %d", |
| addr.sa_family, remote_addr.sa_family); |
| return -EINVAL; |
| } |
| } else { |
| addr.sa_family = remote_addr.sa_family; |
| } |
| |
| ctx->app_type = NET_APP_CLIENT; |
| ctx->user_data = user_data; |
| ctx->send_data = net_context_sendto; |
| ctx->recv_cb = _net_app_received; |
| ctx->proto = proto; |
| ctx->sock_type = sock_type; |
| ctx->is_enabled = true; |
| |
| ret = _net_app_config_local_ctx(ctx, sock_type, proto, &addr); |
| if (ret < 0) { |
| close_net_ctx(ctx); |
| goto fail; |
| } |
| |
| if (peer_addr) { |
| if (peer_addr->sa_family == AF_INET) { |
| #if defined(CONFIG_NET_IPV4) |
| memcpy(&ctx->ipv4.remote, peer_addr, |
| sizeof(ctx->ipv4.remote)); |
| ctx->default_ctx = &ctx->ipv4; |
| #else |
| return -EPROTONOSUPPORT; |
| #endif |
| } else if (peer_addr->sa_family == AF_INET6) { |
| #if defined(CONFIG_NET_IPV6) |
| memcpy(&ctx->ipv6.remote, peer_addr, |
| sizeof(ctx->ipv6.remote)); |
| ctx->default_ctx = &ctx->ipv6; |
| #else |
| return -EPROTONOSUPPORT; |
| #endif |
| } |
| |
| goto out; |
| } |
| |
| if (!peer_addr_str) { |
| NET_ERR("Cannot know where to connect."); |
| ret = -EINVAL; |
| close_net_ctx(ctx); |
| goto fail; |
| } |
| |
| ret = set_remote_addr(ctx, &remote_addr, base_addr_str, |
| addr_ok, timeout); |
| if (ret < 0) { |
| close_net_ctx(ctx); |
| goto fail; |
| } |
| |
| /* If we have not yet figured out what is the protocol family, |
| * then we cannot continue. |
| */ |
| if (!ctx->default_ctx || |
| ctx->default_ctx->remote.sa_family == AF_UNSPEC) { |
| NET_ERR("Unknown protocol family."); |
| return -EPFNOSUPPORT; |
| } |
| |
| ret = bind_local(ctx); |
| if (ret < 0) { |
| goto fail; |
| } |
| |
| _net_app_print_info(ctx); |
| |
| out: |
| ctx->is_init = true; |
| |
| _net_app_register(ctx); |
| |
| fail: |
| return ret; |
| |
| } |
| |
| static void _app_connected(struct net_context *net_ctx, |
| int status, |
| void *user_data) |
| { |
| struct net_app_ctx *ctx = user_data; |
| int ret; |
| |
| #if defined(CONFIG_NET_APP_TLS) || defined(CONFIG_NET_APP_DTLS) |
| if (ctx->is_tls) { |
| k_sem_give(&ctx->client.connect_wait); |
| } |
| #endif |
| |
| ret = net_context_recv(net_ctx, ctx->recv_cb, K_NO_WAIT, ctx); |
| if (ret < 0) { |
| NET_DBG("Cannot set recv_cb (%d)", ret); |
| } |
| |
| #if defined(CONFIG_NET_APP_TLS) || defined(CONFIG_NET_APP_DTLS) |
| if (ctx->is_tls) { |
| /* If we have TLS connection, the connect cb is called |
| * after TLS handshakes are done. |
| */ |
| NET_DBG("Postponing TLS connection cb for ctx %p", ctx); |
| } else |
| #endif |
| { |
| if (ctx->cb.connect) { |
| ctx->cb.connect(ctx, status, ctx->user_data); |
| } |
| } |
| } |
| |
| #if defined(CONFIG_NET_APP_DTLS) |
| static int connect_dtls(struct net_app_ctx *ctx, struct net_context *orig, |
| struct sockaddr *remote) |
| { |
| struct net_context *dtls_context; |
| struct sockaddr local_addr; |
| int ret; |
| |
| /* We create a new context that starts to send data and get replies |
| * directly into correct callback. |
| */ |
| ret = net_context_get(net_context_get_family(orig), SOCK_DGRAM, |
| IPPROTO_UDP, &dtls_context); |
| if (ret < 0) { |
| NET_DBG("Cannot get connect context"); |
| goto out; |
| } |
| |
| memcpy(&dtls_context->remote, remote, sizeof(dtls_context->remote)); |
| |
| #if defined(CONFIG_NET_IPV6) |
| if (net_context_get_family(orig) == AF_INET6) { |
| struct sockaddr_in6 *local_addr6 = net_sin6(&local_addr); |
| |
| net_sin6(&dtls_context->remote)->sin6_family = AF_INET6; |
| |
| local_addr6->sin6_family = AF_INET6; |
| local_addr6->sin6_port = net_sin6_ptr(&orig->local)->sin6_port; |
| net_ipaddr_copy(&local_addr6->sin6_addr, |
| net_sin6_ptr(&orig->local)->sin6_addr); |
| } else |
| #endif /* CONFIG_NET_IPV6 */ |
| |
| #if defined(CONFIG_NET_IPV4) |
| if (net_context_get_family(orig) == AF_INET) { |
| struct sockaddr_in *local_addr4 = net_sin(&local_addr); |
| |
| net_sin(&dtls_context->remote)->sin_family = AF_INET; |
| |
| local_addr4->sin_family = AF_INET; |
| local_addr4->sin_port = net_sin_ptr(&orig->local)->sin_port; |
| net_ipaddr_copy(&local_addr4->sin_addr, |
| net_sin_ptr(&orig->local)->sin_addr); |
| } else |
| #endif /* CONFIG_NET_IPV4 */ |
| { |
| NET_ASSERT_INFO(false, "Invalid protocol family %d", |
| net_context_get_family(orig)); |
| goto ctx_drop; |
| } |
| |
| ret = net_context_bind(dtls_context, &local_addr, sizeof(local_addr)); |
| if (ret < 0) { |
| NET_DBG("Cannot bind connect DTLS context"); |
| goto ctx_drop; |
| } |
| |
| dtls_context->flags |= NET_CONTEXT_REMOTE_ADDR_SET; |
| |
| ret = net_udp_register(&dtls_context->remote, |
| &local_addr, |
| ntohs(net_sin(&dtls_context->remote)->sin_port), |
| ntohs(net_sin(&local_addr)->sin_port), |
| _net_app_dtls_established, |
| ctx, |
| &dtls_context->conn_handler); |
| if (ret < 0) { |
| NET_DBG("Cannot register connect DTLS handler (%d)", ret); |
| goto ctx_drop; |
| } |
| |
| NET_DBG("New DTLS connection context %p created", dtls_context); |
| |
| ctx->dtls.ctx = dtls_context; |
| |
| return 0; |
| |
| ctx_drop: |
| net_context_unref(dtls_context); |
| |
| out: |
| return -ECONNABORTED; |
| } |
| #endif /* CONFIG_NET_APP_DTLS */ |
| |
| static void check_local_address(struct net_app_ctx *ctx, |
| struct net_context *net_ctx) |
| { |
| #if defined(CONFIG_NET_IPV6) |
| if (net_context_get_family(net_ctx) == AF_INET6) { |
| const struct in6_addr *laddr; |
| struct in6_addr *raddr; |
| |
| laddr = &net_sin6(&ctx->ipv6.local)->sin6_addr; |
| if (!net_is_ipv6_addr_unspecified(laddr)) { |
| return; |
| } |
| |
| raddr = &net_sin6(&ctx->ipv6.remote)->sin6_addr; |
| |
| laddr = net_if_ipv6_select_src_addr(NULL, raddr); |
| if (laddr && laddr != net_ipv6_unspecified_address()) { |
| net_ipaddr_copy(&net_sin6(&ctx->ipv6.local)->sin6_addr, |
| laddr); |
| } else { |
| NET_WARN("Source address is unspecified!"); |
| } |
| } |
| #endif |
| |
| #if defined(CONFIG_NET_IPV4) |
| if (net_context_get_family(net_ctx) == AF_INET) { |
| struct in_addr *laddr; |
| struct net_if *iface; |
| |
| laddr = &net_sin(&ctx->ipv4.local)->sin_addr; |
| if (!net_is_ipv4_addr_unspecified(laddr)) { |
| return; |
| } |
| |
| /* Just take the first IPv4 address of an interface */ |
| iface = net_context_get_iface(net_ctx); |
| if (iface) { |
| laddr = &iface->ipv4.unicast[0].address.in_addr; |
| net_ipaddr_copy(&net_sin(&ctx->ipv4.local)->sin_addr, |
| laddr); |
| } else { |
| NET_WARN("Source address is unspecified!"); |
| } |
| } |
| #endif |
| } |
| |
| int net_app_connect(struct net_app_ctx *ctx, s32_t timeout) |
| { |
| struct net_context *net_ctx; |
| bool started = false; |
| int ret; |
| |
| if (!ctx) { |
| return -EINVAL; |
| } |
| |
| if (!ctx->is_init) { |
| return -ENOENT; |
| } |
| |
| if (ctx->app_type != NET_APP_CLIENT) { |
| return -EINVAL; |
| } |
| |
| net_ctx = _net_app_select_net_ctx(ctx, NULL); |
| if (!net_ctx && ctx->is_enabled) { |
| return -EAFNOSUPPORT; |
| } |
| |
| if (!ctx->is_enabled) { |
| ret = _net_app_config_local_ctx(ctx, ctx->sock_type, |
| ctx->proto, NULL); |
| if (ret < 0) { |
| NET_DBG("Cannot get local endpoint (%d)", ret); |
| return -EINVAL; |
| } |
| |
| net_ctx = _net_app_select_net_ctx(ctx, NULL); |
| |
| NET_DBG("Re-conncting to net_ctx %p", net_ctx); |
| |
| ret = bind_local(ctx); |
| if (ret < 0) { |
| NET_DBG("Cannot bind local endpoint (%d)", ret); |
| return -EINVAL; |
| } |
| |
| ctx->is_enabled = true; |
| |
| _net_app_print_info(ctx); |
| } else { |
| /* We cannot bind to local unspecified address when sending. |
| * Select proper address depending on remote one in this case. |
| */ |
| check_local_address(ctx, net_ctx); |
| } |
| |
| #if defined(CONFIG_NET_APP_TLS) || defined(CONFIG_NET_APP_DTLS) |
| if (ctx->is_tls && !ctx->tls.tid && |
| (ctx->proto == IPPROTO_TCP || |
| (IS_ENABLED(CONFIG_NET_APP_DTLS) && ctx->proto == IPPROTO_UDP))) { |
| /* TLS thread is not yet running, start it now */ |
| ret = start_tls_client(ctx); |
| if (ret < 0) { |
| NET_DBG("TLS thread cannot be started (%d)", ret); |
| return ret; |
| } |
| |
| started = true; |
| |
| /* Let the TLS thread run first */ |
| k_yield(); |
| } |
| #else |
| ARG_UNUSED(started); |
| #endif /* CONFIG_NET_APP_TLS || CONFIG_NET_APP_DTLS */ |
| |
| #if defined(CONFIG_NET_APP_DTLS) |
| if (ctx->proto == IPPROTO_UDP) { |
| if (!ctx->dtls.ctx) { |
| ret = connect_dtls(ctx, net_ctx, |
| &ctx->default_ctx->remote); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| ret = net_context_connect(ctx->dtls.ctx, |
| &ctx->dtls.ctx->remote, |
| sizeof(ctx->dtls.ctx->remote), |
| _app_connected, |
| timeout, |
| ctx); |
| } else { |
| /* If we have already a connection, then we cannot |
| * really continue. |
| */ |
| ret = -EAGAIN; |
| } |
| } else |
| #endif /* CONFIG_NET_APP_DTLS */ |
| { |
| ret = net_context_connect(net_ctx, |
| &ctx->default_ctx->remote, |
| sizeof(ctx->default_ctx->remote), |
| _app_connected, |
| timeout, |
| ctx); |
| } |
| |
| if (ret < 0) { |
| NET_DBG("Cannot connect to peer (%d)", ret); |
| |
| #if defined(CONFIG_NET_APP_TLS) || defined(CONFIG_NET_APP_DTLS) |
| if (started) { |
| _net_app_tls_handler_stop(ctx); |
| } |
| #endif |
| } |
| |
| return ret; |
| } |
| |
| #if defined(CONFIG_NET_APP_TLS) || defined(CONFIG_NET_APP_DTLS) |
| static void tls_client_handler(struct net_app_ctx *ctx, |
| struct k_sem *startup_sync) |
| { |
| int ret; |
| |
| NET_DBG("Starting TLS client thread for %p", ctx); |
| |
| ret = _net_app_tls_init(ctx, MBEDTLS_SSL_IS_CLIENT); |
| if (ret < 0) { |
| NET_DBG("TLS client init failed"); |
| return; |
| } |
| |
| k_sem_give(startup_sync); |
| |
| while (1) { |
| /* We wait until TLS connection is established */ |
| k_sem_take(&ctx->client.connect_wait, K_FOREVER); |
| |
| ret = _net_app_ssl_mainloop(ctx); |
| if (ctx->tls.connection_closing) { |
| mbedtls_ssl_close_notify(&ctx->tls.mbedtls.ssl); |
| |
| if (ctx->cb.close) { |
| ctx->cb.close(ctx, -ESHUTDOWN, ctx->user_data); |
| } |
| |
| ctx->tls.connection_closing = false; |
| ctx->is_enabled = false; |
| |
| /* Wait more connection requests from user */ |
| continue; |
| } |
| |
| if (ret < 0) { |
| NET_ERR("TLS mainloop startup failed (%d)", ret); |
| break; |
| } |
| } |
| |
| NET_DBG("Shutting down TLS handler"); |
| |
| /* If there is any pending data that have not been processed |
| * yet, we need to free it here. |
| */ |
| if (ctx->tls.mbedtls.ssl_ctx.rx_pkt) { |
| net_pkt_unref(ctx->tls.mbedtls.ssl_ctx.rx_pkt); |
| ctx->tls.mbedtls.ssl_ctx.rx_pkt = NULL; |
| ctx->tls.mbedtls.ssl_ctx.frag = NULL; |
| } |
| |
| if (ctx->cb.close) { |
| ctx->cb.close(ctx, -ESHUTDOWN, ctx->user_data); |
| } |
| |
| _net_app_tls_handler_stop(ctx); |
| } |
| |
| static int start_tls_client(struct net_app_ctx *ctx) |
| { |
| struct k_sem startup_sync; |
| |
| /* Start the thread that handles TLS traffic. */ |
| if (ctx->tls.tid) { |
| return -EALREADY; |
| } |
| |
| k_sem_init(&startup_sync, 0, 1); |
| |
| ctx->tls.tid = k_thread_create(&ctx->tls.thread, |
| ctx->tls.stack, |
| ctx->tls.stack_size, |
| (k_thread_entry_t)tls_client_handler, |
| ctx, &startup_sync, 0, |
| K_PRIO_COOP(7), 0, 0); |
| |
| /* Wait until we know that the TLS thread startup was ok */ |
| if (k_sem_take(&startup_sync, TLS_STARTUP_TIMEOUT) < 0) { |
| _net_app_tls_handler_stop(ctx); |
| return -ECANCELED; |
| } |
| |
| return 0; |
| } |
| |
| int net_app_client_tls(struct net_app_ctx *ctx, |
| u8_t *request_buf, |
| size_t request_buf_len, |
| u8_t *personalization_data, |
| size_t personalization_data_len, |
| net_app_ca_cert_cb_t cert_cb, |
| const char *cert_host, |
| net_app_entropy_src_cb_t entropy_src_cb, |
| struct k_mem_pool *pool, |
| k_thread_stack_t *stack, |
| size_t stack_size) |
| { |
| if (!request_buf || request_buf_len == 0) { |
| NET_ERR("Request buf must be set"); |
| return -EINVAL; |
| } |
| |
| /* mbedtls cannot receive or send larger buffer as what is defined |
| * in a file pointed by CONFIG_MBEDTLS_CFG_FILE. |
| */ |
| if (request_buf_len > MBEDTLS_SSL_MAX_CONTENT_LEN) { |
| NET_ERR("Request buf too large, max len is %d", |
| MBEDTLS_SSL_MAX_CONTENT_LEN); |
| return -EINVAL; |
| } |
| |
| if (!cert_cb) { |
| NET_ERR("Cert callback must be set"); |
| return -EINVAL; |
| } |
| |
| ctx->is_tls = true; |
| ctx->send_data = _net_app_tls_sendto; |
| ctx->recv_cb = _net_app_tls_received; |
| ctx->tls.request_buf = request_buf; |
| ctx->tls.request_buf_len = request_buf_len; |
| ctx->tls.cert_host = cert_host; |
| ctx->tls.stack = stack; |
| ctx->tls.stack_size = stack_size; |
| ctx->tls.mbedtls.ca_cert_cb = cert_cb; |
| ctx->tls.pool = pool; |
| ctx->tls.mbedtls.personalization_data = personalization_data; |
| ctx->tls.mbedtls.personalization_data_len = personalization_data_len; |
| |
| if (entropy_src_cb) { |
| ctx->tls.mbedtls.entropy_src_cb = entropy_src_cb; |
| } else { |
| ctx->tls.mbedtls.entropy_src_cb = _net_app_entropy_source; |
| } |
| |
| /* The semaphore is released when the client calls net_app_connect() */ |
| k_sem_init(&ctx->client.connect_wait, 0, 1); |
| |
| /* The mbedtls is initialized in TLS thread because of mbedtls stack |
| * requirements. TLS thread is started when we get the first client |
| * request to send data. |
| */ |
| return 0; |
| } |
| #endif /* CONFIG_NET_APP_TLS || CONFIG_NET_APP_DTLS */ |