/* server.c */ /* * Copyright (c) 2017 Intel Corporation. * * SPDX-License-Identifier: Apache-2.0 */ #define LOG_MODULE_NAME net_app_server #define NET_LOG_LEVEL CONFIG_NET_APP_LOG_LEVEL #include #include #include #include #include #include #include #include #include "net_app_private.h" #if defined(CONFIG_NET_TCP) static void new_client(struct net_context *net_ctx, const struct sockaddr *addr) { #if NET_LOG_LEVEL >= LOG_LEVEL_WRN #if defined(CONFIG_NET_IPV6) #define PORT_STR sizeof("[]:xxxxx") char buf[NET_IPV6_ADDR_LEN + PORT_STR]; #elif defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_IPV6) #define PORT_STR sizeof(":xxxxx") char buf[NET_IPV4_ADDR_LEN + PORT_STR]; #endif NET_INFO("Connection from %s (%p)", log_strdup(_net_app_sprint_ipaddr(buf, sizeof(buf), addr)), net_ctx); #endif } static int get_avail_net_ctx(struct net_app_ctx *ctx) { int i; for (i = 0; i < CONFIG_NET_APP_SERVER_NUM_CONN; i++) { if (!ctx->server.net_ctxs[i] || !net_context_is_used(ctx->server.net_ctxs[i])) { return i; } } return -1; } void _net_app_accept_cb(struct net_context *net_ctx, struct sockaddr *addr, socklen_t addrlen, int status, void *data) { struct net_app_ctx *ctx = data; int i, ret; ARG_UNUSED(addr); ARG_UNUSED(addrlen); i = get_avail_net_ctx(ctx); if (i < 0 || status != 0 || !ctx->is_enabled) { /* We are already connected and there are no free context slots * available so this new connection must be closed. */ net_context_put(net_ctx); if (ctx->cb.connect) { if (!status) { status = -ECONNREFUSED; } ctx->cb.connect(ctx, status, ctx->user_data); } if (i < 0) { NET_DBG("All connection slots occupied, new " "connection dropped"); } return; } NET_DBG("[%d] Accepted net_ctx %p", i, net_ctx); ret = net_context_recv(net_ctx, ctx->recv_cb, K_NO_WAIT, ctx); if (ret < 0) { NET_DBG("Cannot set recv_cb (%d)", ret); } ctx->server.net_ctxs[i] = net_ctx; /* We need to set the backpointer here, as otherwise it is impossible * to find the correct net_ctx from a list of net_ctxs. */ net_ctx->net_app = ctx; new_client(net_ctx, addr); if (ctx->cb.connect) { ctx->cb.connect(ctx, 0, ctx->user_data); } } #endif /* CONFIG_NET_TCP */ int net_app_listen(struct net_app_ctx *ctx) { bool dual = false, v4_failed = false; int ret; if (!ctx) { return -EINVAL; } if (!ctx->is_init) { return -ENOENT; } if (ctx->app_type != NET_APP_SERVER) { return -EINVAL; } #if defined(CONFIG_NET_IPV4) if (ctx->ipv4.local.sa_family == AF_UNSPEC) { ctx->ipv4.local.sa_family = AF_INET; dual = true; _net_app_set_local_addr(ctx, &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) { if (ctx->ipv4.ctx) { net_context_put(ctx->ipv4.ctx); ctx->ipv4.ctx = NULL; } v4_failed = true; } #if defined(CONFIG_NET_APP_DTLS) else { if (ctx->is_tls && ctx->proto == IPPROTO_UDP) { net_context_recv(ctx->ipv4.ctx, ctx->recv_cb, K_NO_WAIT, ctx); } } #endif #endif /* CONFIG_NET_IPV4 */ /* We ignore the IPv4 error if IPv6 is enabled */ #if defined(CONFIG_NET_IPV6) if (ctx->ipv6.local.sa_family == AF_UNSPEC || dual) { ctx->ipv6.local.sa_family = AF_INET6; _net_app_set_local_addr(ctx, &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) { if (ctx->ipv6.ctx) { net_context_put(ctx->ipv6.ctx); ctx->ipv6.ctx = NULL; } if (!v4_failed) { ret = 0; } } #if defined(CONFIG_NET_APP_DTLS) else { if (ctx->is_tls && ctx->proto == IPPROTO_UDP) { net_context_recv(ctx->ipv6.ctx, ctx->recv_cb, K_NO_WAIT, ctx); } } #endif #endif /* CONFIG_NET_IPV6 */ return ret; } int net_app_init_server(struct net_app_ctx *ctx, enum net_sock_type sock_type, enum net_ip_protocol proto, struct sockaddr *server_addr, u16_t port, void *user_data) { int ret; if (!ctx) { return -EINVAL; } if (ctx->is_init) { return -EALREADY; } #if defined(CONFIG_NET_IPV4) (void)memset(&ctx->ipv4.local, 0, sizeof(ctx->ipv4.local)); ctx->ipv4.local.sa_family = AF_INET; #endif #if defined(CONFIG_NET_IPV6) (void)memset(&ctx->ipv6.local, 0, sizeof(ctx->ipv6.local)); ctx->ipv6.local.sa_family = AF_INET6; #endif if (server_addr) { if (server_addr->sa_family == AF_INET) { #if defined(CONFIG_NET_IPV4) memcpy(&ctx->ipv4.local, server_addr, sizeof(ctx->ipv4.local)); #else return -EPROTONOSUPPORT; #endif } if (server_addr->sa_family == AF_INET6) { #if defined(CONFIG_NET_IPV6) memcpy(&ctx->ipv6.local, server_addr, sizeof(ctx->ipv6.local)); #else return -EPROTONOSUPPORT; #endif } if (server_addr->sa_family == AF_UNSPEC) { #if defined(CONFIG_NET_IPV4) net_sin(&ctx->ipv4.local)->sin_port = net_sin(server_addr)->sin_port; #endif #if defined(CONFIG_NET_IPV6) net_sin6(&ctx->ipv6.local)->sin6_port = net_sin6(server_addr)->sin6_port; #endif } } else { if (port == 0) { return -EINVAL; } #if defined(CONFIG_NET_IPV4) net_sin(&ctx->ipv4.local)->sin_port = htons(port); #endif #if defined(CONFIG_NET_IPV6) net_sin6(&ctx->ipv6.local)->sin6_port = htons(port); #endif } ctx->app_type = NET_APP_SERVER; 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; ret = _net_app_config_local_ctx(ctx, sock_type, proto, server_addr); if (ret < 0) { goto fail; } NET_ASSERT_INFO(ctx->default_ctx, "Default ctx not selected"); ctx->is_init = true; _net_app_register(ctx); fail: return ret; } #if defined(CONFIG_NET_APP_TLS) || defined(CONFIG_NET_APP_DTLS) static inline void new_server(struct net_app_ctx *ctx, const char *server_banner) { #if NET_LOG_LEVEL >= LOG_LEVEL_WRN #if defined(CONFIG_NET_IPV6) #define PORT_STR sizeof("[]:xxxxx") char buf[NET_IPV6_ADDR_LEN + PORT_STR]; #elif defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_IPV6) #define PORT_STR sizeof(":xxxxx") char buf[NET_IPV4_ADDR_LEN + PORT_STR]; #endif #if defined(CONFIG_NET_IPV6) NET_INFO("%s %s (%p)", log_strdup(server_banner), log_strdup(_net_app_sprint_ipaddr(buf, sizeof(buf), &ctx->ipv6.local)), ctx); #endif #if defined(CONFIG_NET_IPV4) NET_INFO("%s %s (%p)", log_strdup(server_banner), log_strdup(_net_app_sprint_ipaddr(buf, sizeof(buf), &ctx->ipv4.local)), ctx); #endif #endif } static struct net_context *find_net_ctx(struct net_app_ctx *ctx, int *idx) { int i; for (i = 0; i < CONFIG_NET_APP_SERVER_NUM_CONN; i++) { if (ctx->server.net_ctxs[i] && ctx->server.net_ctxs[i]->net_app == ctx && net_context_is_used(ctx->server.net_ctxs[i])) { *idx = i; return ctx->server.net_ctxs[i]; } } return NULL; } static void tls_server_handler(struct net_app_ctx *ctx, struct k_sem *startup_sync) { int ret, i; NET_DBG("Starting TLS server thread for %p", ctx); ret = _net_app_tls_init(ctx, MBEDTLS_SSL_IS_SERVER); if (ret < 0) { NET_DBG("TLS server init failed"); return; } k_sem_give(startup_sync); while (1) { struct net_context *net_ctx; _net_app_ssl_mainloop(ctx); NET_DBG("Closing %p connection", ctx); ctx->tls.close_requested = false; mbedtls_ssl_close_notify(&ctx->tls.mbedtls.ssl); ctx->tls.tx_pending = false; if (ctx->cb.close) { ctx->cb.close(ctx, -ESHUTDOWN, ctx->user_data); } net_ctx = find_net_ctx(ctx, &i); if (net_ctx) { NET_DBG("Server context %p removed", net_ctx); net_context_put(net_ctx); ctx->server.net_ctxs[i] = NULL; } } } #define TLS_STARTUP_TIMEOUT K_SECONDS(5) bool _net_app_server_tls_enable(struct net_app_ctx *ctx) { struct k_sem startup_sync; NET_ASSERT(ctx); if (!(ctx->tls.stack && ctx->tls.stack_size > 0)) { /* No stack or stack size is 0, we cannot enable */ return false; } /* Start the thread that handles TLS traffic. */ if (!ctx->tls.tid) { 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_server_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_ERR("TLS server handler start failed"); _net_app_tls_handler_stop(ctx); return false; } } return true; } bool _net_app_server_tls_disable(struct net_app_ctx *ctx) { NET_ASSERT(ctx); if (!ctx->tls.tid) { return false; } _net_app_tls_handler_stop(ctx); return true; } int net_app_server_tls(struct net_app_ctx *ctx, u8_t *request_buf, size_t request_buf_len, const char *server_banner, u8_t *personalization_data, size_t personalization_data_len, net_app_cert_cb_t cert_cb, 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; } if (server_banner) { new_server(ctx, server_banner); } ctx->tls.request_buf = request_buf; ctx->tls.request_buf_len = request_buf_len; ctx->is_tls = true; ctx->tls.stack = stack; ctx->tls.stack_size = stack_size; ctx->tls.mbedtls.cert_cb = cert_cb; ctx->tls.pool = pool; if (entropy_src_cb) { ctx->tls.mbedtls.entropy_src_cb = entropy_src_cb; } else { ctx->tls.mbedtls.entropy_src_cb = _net_app_entropy_source; } ctx->tls.mbedtls.personalization_data = personalization_data; ctx->tls.mbedtls.personalization_data_len = personalization_data_len; ctx->send_data = _net_app_tls_sendto; ctx->recv_cb = _net_app_tls_received; /* Then mbedtls specific initialization */ return 0; } #endif /* CONFIG_NET_APP_TLS || CONFIG_NET_APP_DTLS */ bool net_app_server_enable(struct net_app_ctx *ctx) { bool old; NET_ASSERT(ctx); old = ctx->is_enabled; ctx->is_enabled = true; #if defined(CONFIG_NET_APP_TLS) || defined(CONFIG_NET_APP_DTLS) if (ctx->is_tls) { _net_app_server_tls_enable(ctx); } #endif return old; } bool net_app_server_disable(struct net_app_ctx *ctx) { bool old; NET_ASSERT(ctx); old = ctx->is_enabled; ctx->is_enabled = false; #if defined(CONFIG_NET_APP_TLS) || defined(CONFIG_NET_APP_DTLS) if (ctx->is_tls) { _net_app_server_tls_disable(ctx); } #endif return old; }