diff --git a/examples/common_components/protocol_examples_common/CMakeLists.txt b/examples/common_components/protocol_examples_common/CMakeLists.txt index 7c4ebe34d..5c19957ba 100644 --- a/examples/common_components/protocol_examples_common/CMakeLists.txt +++ b/examples/common_components/protocol_examples_common/CMakeLists.txt @@ -1,4 +1,4 @@ -idf_component_register(SRCS "connect.c" "stdin_out.c" +idf_component_register(SRCS "connect.c" "stdin_out.c" "addr_from_stdin.c" INCLUDE_DIRS "include" PRIV_REQUIRES esp_netif ) diff --git a/examples/common_components/protocol_examples_common/addr_from_stdin.c b/examples/common_components/protocol_examples_common/addr_from_stdin.c new file mode 100644 index 000000000..460fe9a2e --- /dev/null +++ b/examples/common_components/protocol_examples_common/addr_from_stdin.c @@ -0,0 +1,65 @@ +#include +#include "esp_system.h" +#include "esp_log.h" +#include "esp_netif.h" +#include "protocol_examples_common.h" + +#include "lwip/sockets.h" +#include +#include + +#define HOST_IP_SIZE 128 + +esp_err_t get_addr_from_stdin(int port, int sock_type, int *ip_protocol, int *addr_family, struct sockaddr_in6 *dest_addr) +{ + char host_ip[HOST_IP_SIZE]; + int len; + static bool already_init = false; + + // this function could be called multiple times -> make sure UART init runs only once + if (!already_init) { + example_configure_stdin_stdout(); + already_init = true; + } + + // ignore empty or LF only string (could receive from DUT class) + do { + fgets(host_ip, HOST_IP_SIZE, stdin); + len = strlen(host_ip); + } while (len<=1 && host_ip[0] == '\n'); + host_ip[len - 1] = '\0'; + + struct addrinfo hints, *addr_list, *cur; + memset( &hints, 0, sizeof( hints ) ); + + // run getaddrinfo() to decide on the IP protocol + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = sock_type; + hints.ai_protocol = IPPROTO_TCP; + if( getaddrinfo( host_ip, NULL, &hints, &addr_list ) != 0 ) { + return ESP_FAIL; + } + for( cur = addr_list; cur != NULL; cur = cur->ai_next ) { + memcpy(dest_addr, cur->ai_addr, sizeof(*dest_addr)); + if (cur->ai_family == AF_INET) { + *ip_protocol = IPPROTO_IP; + *addr_family = AF_INET; + // add port number and return on first IPv4 match + ((struct sockaddr_in*)dest_addr)->sin_port = htons(port); + freeaddrinfo( addr_list ); + return ESP_OK; + + } else if (cur->ai_family == AF_INET6) { + *ip_protocol = IPPROTO_IPV6; + *addr_family = AF_INET6; + // add port and interface number and return on first IPv6 match + dest_addr->sin6_port = htons(port); + dest_addr->sin6_scope_id = esp_netif_get_netif_impl_index(EXAMPLE_INTERFACE); + freeaddrinfo( addr_list ); + return ESP_OK; + } + } + // no match found + freeaddrinfo( addr_list ); + return ESP_FAIL; +} \ No newline at end of file diff --git a/examples/common_components/protocol_examples_common/include/addr_from_stdin.h b/examples/common_components/protocol_examples_common/include/addr_from_stdin.h new file mode 100644 index 000000000..ab5043aed --- /dev/null +++ b/examples/common_components/protocol_examples_common/include/addr_from_stdin.h @@ -0,0 +1,44 @@ +/* Common utilities for socket address input interface: + The API get_addr_from_stdin() is mainly used by socket client examples which read IP address from stdin (if configured). + This option is typically used in the CI, but could be enabled in the project configuration. + In that case this component is used to receive a string that is evaluated and processed to output + socket structures to open a connectio + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lwip/sys.h" +#include +#include + +/** + * @brief Read and evaluate IP address from stdin + * + * This API reads stdin and parses the input address using getaddrinfo() + * to fill in struct sockaddr_in6 (for both IPv4 and IPv6) used to open + * a socket. IP protocol is guessed from the IP address string. + * + * @param[in] port port number of expected connection + * @param[in] sock_type expected protocol: SOCK_STREAM or SOCK_DGRAM + * @param[out] ip_protocol resultant IP protocol: IPPROTO_IP or IPPROTO_IP6 + * @param[out] addr_family resultant address family: AF_INET or AF_INET6 + * @param[out] dest_addr sockaddr_in6 structure (for both IPv4 and IPv6) + * @return ESP_OK on success, ESP_FAIL otherwise + */ +esp_err_t get_addr_from_stdin(int port, int sock_type, + int *ip_protocol, + int *addr_family, + struct sockaddr_in6 *dest_addr); + +#ifdef __cplusplus +} +#endif \ No newline at end of file