diff --git a/include/netutils/netlib.h b/include/netutils/netlib.h index b28b880dfcc..43febfcda1e 100644 --- a/include/netutils/netlib.h +++ b/include/netutils/netlib.h @@ -526,6 +526,10 @@ int netlib_check_ifconnectivity(FAR const char *ifname, int netlib_get_iobinfo(FAR struct iob_stats_s *iob); #endif +int netlib_check_httpconnectivity(FAR const char *host, + FAR const char *getmsg, + int port, int expect_code); + #undef EXTERN #ifdef __cplusplus } diff --git a/netutils/netlib/CMakeLists.txt b/netutils/netlib/CMakeLists.txt index be87f33fa40..49e8aa81ec6 100644 --- a/netutils/netlib/CMakeLists.txt +++ b/netutils/netlib/CMakeLists.txt @@ -161,6 +161,8 @@ if(CONFIG_NETUTILS_NETLIB) list(APPEND SRCS netlib_checkipconnectivity.c netlib_checkifconnectivity.c) endif() + list(APPEND SRCS netlib_checkhttpconnectivity.c) + target_sources(apps PRIVATE ${SRCS}) endif() diff --git a/netutils/netlib/Makefile b/netutils/netlib/Makefile index a55340c1f21..4bfc8d9d40b 100644 --- a/netutils/netlib/Makefile +++ b/netutils/netlib/Makefile @@ -160,4 +160,6 @@ ifeq ($(CONFIG_NETUTILS_PING),y) CSRCS += netlib_checkipconnectivity.c netlib_checkifconnectivity.c endif +CSRCS += netlib_checkhttpconnectivity.c + include $(APPDIR)/Application.mk diff --git a/netutils/netlib/netlib_checkhttpconnectivity.c b/netutils/netlib/netlib_checkhttpconnectivity.c new file mode 100644 index 00000000000..c29d86292a0 --- /dev/null +++ b/netutils/netlib/netlib_checkhttpconnectivity.c @@ -0,0 +1,265 @@ +/**************************************************************************** + * apps/netutils/netlib/netlib_checkhttpconnectivity.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_LIBC_NETDB +# include +#endif + +#include +#include + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: eai_to_errno + * + * Description: + * Convert EAI_* error code to errno value for unified error handling. + * + * Parameters: + * eai_error The EAI_* error code from getaddrinfo(). + * + * Return: + * Corresponding errno value. + * + ****************************************************************************/ + +#ifdef CONFIG_LIBC_NETDB +static int eai_to_errno(int eai_error) +{ + switch (eai_error) + { + case EAI_AGAIN: + return EAGAIN; /* Temporary failure, try again */ + + case EAI_BADFLAGS: + return EINVAL; /* Invalid flags */ + + case EAI_FAIL: + return EHOSTUNREACH; /* Non-recoverable failure */ + + case EAI_FAMILY: + return EAFNOSUPPORT; /* Address family not supported */ + + case EAI_MEMORY: + return ENOMEM; /* Memory allocation failure */ + + case EAI_NONAME: + return EHOSTUNREACH; /* Host name not found */ + + case EAI_SERVICE: + return EINVAL; /* Service not recognized */ + + case EAI_SOCKTYPE: + return EINVAL; /* Socket type not supported */ + + case EAI_SYSTEM: + return errno; /* System error, use errno */ + + case EAI_OVERFLOW: + return EOVERFLOW; /* Buffer overflow */ + + default: + return EINVAL; /* Unknown error, use invalid argument */ + } +} +#endif + +/**************************************************************************** + * Name: httpget_gethostip + * + * Description: + * Check the http status code + * + * Parameters: + * hostname The host name to use in the nslookup. + * dest The location to return the IPv4 address. + * + * Return: + * 0 on success; a negative errno value on failure. + * + ****************************************************************************/ + +static int httpget_gethostip(FAR const char *hostname, + FAR struct in_addr *dest) +{ +#ifdef CONFIG_LIBC_NETDB + /* Netdb DNS client support is enabled */ + + int ret; + FAR struct addrinfo hint; + FAR struct addrinfo *info; + FAR struct sockaddr_in *addr; + + memset(&hint, 0, sizeof(hint)); + hint.ai_family = AF_INET; + + ret = getaddrinfo(hostname, NULL, &hint, &info); + if (ret != OK) + { + return -eai_to_errno(ret); + } + + addr = (FAR struct sockaddr_in *)info->ai_addr; + memcpy(dest, &addr->sin_addr, sizeof(struct in_addr)); + + freeaddrinfo(info); + return OK; + +#else /* CONFIG_LIBC_NETDB */ + /* No host name support */ + + /* Convert strings to numeric IPv4 address */ + + int ret = inet_pton(AF_INET, hostname, dest); + + /* The inet_pton() function returns 1 if the conversion succeeds. It will + * return 0 if the input is not a valid IPv4 dotted-decimal string or -1 + * with errno set to EAFNOSUPPORT if the address family argument is + * unsupported. + */ + + if (ret != 1) + { + /* ret == 0: invalid IP address string, set errno to EINVAL + * ret == -1: system error, errno is already set to EAFNOSUPPORT + */ + + if (ret == 0) + { + set_errno(EINVAL); + } + + return -errno; + } + + return OK; + +#endif /* CONFIG_LIBC_NETDB */ +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: netlib_check_httpconnectivity + * + * Description: + * Check the http status code + * + * Parameters: + * host The remote host to check + * getmsg The http GET message + * port The port of remote host + * expect_code The except http code of http code + * + * Return: + * 0 on success; a nagtive on failure. + * + ****************************************************************************/ + +int netlib_check_httpconnectivity(FAR const char *host, + FAR const char *getmsg, + int port, int expect_code) +{ + int sock; + int ret; + int status_code = -1; + struct sockaddr_in server_addr; + char buf[256]; + + ret = httpget_gethostip(host, &server_addr.sin_addr); + if (ret < 0) + { + nerr("Failed to get host ip"); + return ret; + } + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) + { + nerr("Failed to create socket"); + return -errno; + } + + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(port); + + ret = connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)); + if (ret < 0) + { + perror("Connection failed"); + goto exit_with_socket; + } + + snprintf(buf, sizeof(buf), + "GET /%s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", + getmsg, host); + + ret = send(sock, buf, strlen(buf), 0); + if (ret < 0) + { + perror("send failed"); + goto exit_with_socket; + } + + if ((ret = recv(sock, buf, sizeof(buf), 0)) < 0) + { + perror("recv failed"); + goto exit_with_socket; + } + + /* get the http code, and check with except code */ + + if (sscanf(buf, "HTTP/1.1 %d", &status_code) == 1 && + status_code == expect_code) + { + ninfo("HTTP connectivity is ok"); + ret = OK; + } + else + { + nerr("HTTP connectivity is not ok status code: %d\n", status_code); + ret = -status_code; + } + +exit_with_socket: + close(sock); + return ret; +}