Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
SocketClient.cxx
1
35#include "openmrn_features.h"
36
37#if OPENMRN_FEATURE_BSD_SOCKETS
38
39#define LOGLEVEL INFO
40
42
43#include <memory>
44#include <netdb.h>
45#ifndef ESP_PLATFORM
46// these don't exist on the ESP32 with LWiP
47#include <netinet/in.h>
48#include <netinet/tcp.h>
49#endif // ESP_PLATFORM
50#include <sys/socket.h>
51#include <sys/types.h>
52#include <unistd.h>
53#include <signal.h>
54
56
58#include "utils/macros.h"
59#include "utils/logging.h"
60
61#ifdef ESP_PLATFORM
62// this is not declared in netdb.h on ESP32
63const char *gai_strerror (int __ecode);
64#endif // ESP_PLATFORM
65
66int ConnectSocket(const char *host, int port)
67{
68 return SocketClient::connect(host, port);
69}
70
71int ConnectSocket(const char *host, const char* port_str)
72{
73 return SocketClient::connect(host, port_str);
74}
75
77 const char *host, const char *port_str)
78{
79 struct addrinfo *addr;
80 struct addrinfo hints;
81 memset(&hints, 0, sizeof(hints));
82 hints.ai_family = AF_INET;
83 hints.ai_socktype = SOCK_STREAM;
84 hints.ai_flags = 0;
85 hints.ai_protocol = IPPROTO_TCP;
86
87 if (int ai_ret = getaddrinfo(host, port_str, &hints, &addr) != 0 || !addr)
88 {
89 LOG_ERROR("getaddrinfo failed for '%s': %s", host,
90 gai_strerror(ai_ret));
91 return {nullptr};
92 }
93
94 AddrinfoPtr ai_deleter(addr);
95 return ai_deleter;
96}
97
98int SocketClient::connect(const char *host, const char *port_str)
99{
100 int fd = connect(string_to_address(host, port_str).get());
101 if (fd >= 0)
102 {
103 LOG(INFO, "Connected to %s:%s. fd=%d", host ? host : "mDNS", port_str,
104 fd);
105 }
106 return fd;
107}
108
109int SocketClient::connect(struct addrinfo *addr)
110{
111#if OPENMRN_FEATURE_BSD_SOCKETS_IGNORE_SIGPIPE
112 // We expect write failures to occur but we want to handle them where
113 // the error occurs rather than in a SIGPIPE handler.
114 signal(SIGPIPE, SIG_IGN);
115#endif // OPENMRN_FEATURE_BSD_SOCKETS_IGNORE_SIGPIPE
116
117 if (!addr)
118 {
119 return -1;
120 }
121 int fd = ::socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
122 if (fd < 0)
123 {
124 LOG_ERROR("socket: %s", strerror(errno));
125 return -1;
126 }
127
128 int ret = ::connect(fd, addr->ai_addr, addr->ai_addrlen);
129 if (ret < 0)
130 {
131 LOG_ERROR("connect: %s", strerror(errno));
132 close(fd);
133 return -1;
134 }
135
136 int val = 1;
137 ERRNOCHECK("setsockopt(nodelay)",
138 ::setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)));
139
140 return fd;
141}
142
144 struct addrinfo *addr, string *host, int *port)
145{
146 HASSERT(host && port);
147 *port = -1;
148 host->clear();
149 if (!addr || !addr->ai_addr)
150 {
151 return false;
152 }
153 const char *n = nullptr;
154 char buf[35];
155
156 switch (addr->ai_family)
157 {
158 case AF_INET:
159 {
160 auto *sa = (struct sockaddr_in *)addr->ai_addr;
161 *port = ntohs(sa->sin_port);
162 n = inet_ntop(addr->ai_family, &sa->sin_addr, buf, sizeof(buf));
163 break;
164 }
165#if OPENMRN_HAVE_BSD_SOCKETS_IPV6
166 case AF_INET6:
167 {
168 auto *sa = (struct sockaddr_in6 *)addr->ai_addr;
169 *port = ntohs(sa->sin6_port);
170 n = inet_ntop(addr->ai_family, &sa->sin6_addr, buf, sizeof(buf));
171 break;
172 }
173#endif // OPENMRN_HAVE_BSD_SOCKETS_IPV6
174 default:
175 LOG(INFO, "unsupported address type.");
176 errno = EAFNOSUPPORT;
177 return false;
178 }
179 if (!n)
180 {
181 // failed to convert to string.
182 LOG(INFO, "Failed to convert sockaddr to string: %s", strerror(errno));
183 return false;
184 }
185 *host = buf;
186 return true;
187}
188
189/*
190 * SocketClient::local_test()
191 */
192bool SocketClient::local_test(struct addrinfo *addr)
193{
194 bool local = false;
195 struct ifaddrs *ifa;
196 int result = getifaddrs(&ifa);
197 if (result == 0)
198 {
199 struct ifaddrs *ifa_free = ifa;
200 while (ifa)
201 {
202 if (ifa->ifa_addr)
203 {
204 /* ifa_addr pointer valid */
205 if (ifa->ifa_addr->sa_family == AF_INET)
206 {
207 /* have a valid IPv4 address */
208 struct sockaddr_in *ai_addr_in =
209 (struct sockaddr_in*)addr->ai_addr;
210 struct sockaddr_in *if_addr_in =
211 (struct sockaddr_in*)ifa->ifa_addr;
212 if (ai_addr_in->sin_addr.s_addr ==
213 if_addr_in->sin_addr.s_addr)
214 {
215 /* trying to connected to myself */
216 local = true;
217 break;
218 }
219 }
220 }
221 ifa = ifa->ifa_next;
222 }
223 freeifaddrs(ifa_free);
224 }
225
226 return local;
227}
228
229std::unique_ptr<SocketClientParams> SocketClientParams::from_static(
230 string hostname, int port)
231{
232 std::unique_ptr<DefaultSocketClientParams> p(new DefaultSocketClientParams);
233 p->staticHost_ = std::move(hostname);
234 p->staticPort_ = port;
235 return p;
236}
237
238std::unique_ptr<SocketClientParams> SocketClientParams::from_static_and_mdns(
239 string hostname, int port, string mdns_service)
240{
241 std::unique_ptr<DefaultSocketClientParams> p(new DefaultSocketClientParams);
242 p->staticHost_ = std::move(hostname);
243 p->staticPort_ = port;
244 p->mdnsService_ = std::move(mdns_service);
245 return p;
246}
247
248#endif // OPENMRN_FEATURE_BSD_SOCKETS
int setsockopt(int socket, int level, int option_name, const void *option_value, socklen_t option_len)
Set the socket options.
Definition Socket.cxx:255
int socket(int domain, int type, int protocol)
Create an unbound socket in a communications domain.
Definition Socket.cxx:144
Default implementation that supplies parametrized values for static and mdns connection methods.
static std::unique_ptr< SocketClientParams > from_static(string hostname, int port)
static std::unique_ptr< SocketClientParams > from_static_and_mdns(string hostname, int port, string mdns_service)
static bool local_test(struct addrinfo *addr)
Test if a given address is local.
static bool address_to_string(struct addrinfo *addr, string *host, int *port)
Converts a struct addrinfo to a dotted-decimal notation IP address.
static int connect(const char *host, int port)
Connects a tcp socket to the specified remote host:port.
std::unique_ptr< struct addrinfo, AddrInfoDeleter > AddrinfoPtr
Custom unique pointer that knows how to delete a struct addrinfo.
static AddrinfoPtr string_to_address(const char *host, int port)
Converts a hostname string and port number to a struct addrinfo.
#define IPPROTO_TCP
TCP Raw Socket.
Definition in.h:70
#define ntohs(x)
Converts a network endian short value to host endian.
Definition in.h:89
#define ERRNOCHECK(where, x...)
Calls the function x, and if the return value is negative, prints errno as error message to stderr an...
Definition logging.h:174
#define LOG(level, message...)
Conditionally write a message to the logging output.
Definition logging.h:99
static const int INFO
Loglevel that is printed by default, reporting some status information.
Definition logging.h:57
#define LOG_ERROR(message...)
Shorthand for LOG(LEVEL_ERROR, message...). See LOG.
Definition logging.h:124
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
const char * gai_strerror(int __ecode)
see 'man gai_strerror'
int getifaddrs(struct ifaddrs **ifap)
Create a linked list of structures describing the network interfaces of the local system.
int getaddrinfo(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res)
see 'man getaddrinfo'
const char * inet_ntop(int af, const void *src, char *dst, socklen_t size)
Convert the network address in src to a character string in src.
void freeifaddrs(struct ifaddrs *ifa)
Free a previously generated linked list of structures describing the network interfaces of the local ...
#define SOCK_STREAM
TCP Socket.
Definition socket.h:45
#define AF_INET
IPv4 Socket (UDP, TCP, etc...)
Definition socket.h:54
#define AF_INET6
IPv6 Socket (UDP, TCP, etc...)
Definition socket.h:57
int ConnectSocket(const char *host, int port)
Connects a tcp socket to the specified remote host:port.
Structure to contain information about address of a service provider.
Definition netdb.h:48
struct sockaddr * ai_addr
Socket address for socket.
Definition netdb.h:54
int ai_socktype
Socket type.
Definition netdb.h:51
int ai_protocol
Protocol for socket.
Definition netdb.h:52
socklen_t ai_addrlen
Length of socket address.
Definition netdb.h:53
int ai_family
Protocol family for socket.
Definition netdb.h:50
network interface address list member
struct ifaddrs * ifa_next
next item in list
struct sockaddr * ifa_addr
address of interface
Structure describing an Internet socket address.
Definition in.h:56
uint16_t sa_family
address family (e.g.
Definition socket.h:84
#define TCP_NODELAY
don't delay send to coalesce packets
Definition tcp.h:42