Line data Source code
1 : #ifndef TCPConnect_hh
2 : #define TCPConnect_hh
3 : #include <arpa/inet.h> // inet_aton
4 : #include <netdb.h> // gethostbyname
5 : #include <netinet/in.h>
6 : #include <netinet/in.h> // struct sockaddr_in
7 : #include <netinet/in.h> // inet_aton
8 : #include <sys/socket.h> // socket, bind, listen, accept
9 : #include <sys/socket.h> // inet_aton
10 : #include <sys/types.h> // socket, bind, listen, accept
11 : #include <unistd.h> // close
12 : #include <cstdio> // printf
13 : #include <cstdlib> // exit
14 : #include <cstring> // bzero
15 :
16 : #include <ifaddrs.h>
17 : #include <linux/if_link.h>
18 :
19 : #include <regex>
20 : #include <string>
21 :
22 : #include "TRACE/trace.h"
23 :
24 : // This file (TCPConnect.hh) was created by Ron Rechenmacher <ron@fnal.gov> on
25 : // Sep 15, 2016. "TERMS AND CONDITIONS" governing this file are in the README
26 : // or COPYING file. If you do not have such a file, one can be obtained by
27 : // contacting Ron or Fermi Lab in Batavia IL, 60510, phone: 630-840-3000.
28 : // $RCSfile: .emacs.gnu,v $
29 : // rev="$Revision: 1.30 $$Date: 2016/03/01 14:27:27 $";
30 :
31 : /**
32 : * \file TCPConnect.hh
33 : * Provides utility functions for connecting TCP sockets
34 : */
35 :
36 : /**
37 : * \brief Convert a string hostname to a in_addr suitable for socket communication
38 : * \param host_in Name or IP of host to resolve
39 : * \param[out] addr in_addr object populated with resolved host
40 : * \return 0 if success, -1 if gethostbyname fails
41 : */
42 0 : inline int ResolveHost(char const* host_in, in_addr& addr)
43 : {
44 0 : std::string host;
45 : struct hostent* hostent_sp;
46 0 : std::cmatch mm;
47 : // Note: the regex expression used by regex_match has an implied ^ and $
48 : // at the beginning and end respectively.
49 0 : if (regex_match(host_in, mm, std::regex("([^:]+):(\\d+)")))
50 : {
51 0 : host = mm[1].str();
52 : }
53 0 : else if (regex_match(host_in, mm, std::regex(":{0,1}(\\d+)")))
54 : {
55 0 : host = std::string("127.0.0.1");
56 : }
57 0 : else if (regex_match(host_in, mm, std::regex("([^:]+):{0,1}")))
58 : {
59 0 : host = mm[1].str().c_str();
60 : }
61 : else
62 : {
63 0 : host = std::string("127.0.0.1");
64 : }
65 0 : TLOG(TLVL_INFO) << "Resolving host " << host;
66 :
67 0 : memset(&addr, 0, sizeof(addr));
68 :
69 0 : if (regex_match(host.c_str(), mm, std::regex("\\d+(\\.\\d+){3}")))
70 0 : inet_aton(host.c_str(), &addr);
71 : else
72 : {
73 0 : hostent_sp = gethostbyname(host.c_str());
74 0 : if (!hostent_sp)
75 : {
76 0 : perror("gethostbyname");
77 0 : return (-1);
78 : }
79 0 : addr = *reinterpret_cast<struct in_addr*>(hostent_sp->h_addr_list[0]); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic,cppcoreguidelines-pro-type-reinterpret-cast)
80 : }
81 0 : return 0;
82 0 : }
83 :
84 : /**
85 : * \brief Convert an IP address to the network address of the interface sharing the subnet mask
86 : * \param host_in IP to resolve
87 : * \param[out] addr in_addr object populated with resolved host
88 : * \return 0 if success, -1 if gethostbyname fails, 2 if defaulted to 0.0.0.0 (No matching interfaces)
89 : */
90 0 : inline int GetInterfaceForNetwork(char const* host_in, in_addr& addr)
91 : {
92 0 : std::string host;
93 : struct hostent* hostent_sp;
94 0 : std::cmatch mm;
95 0 : int sts = 0;
96 : // Note: the regex expression used by regex_match has an implied ^ and $
97 : // at the beginning and end respectively.
98 0 : if (regex_match(host_in, mm, std::regex("([^:]+):(\\d+)")))
99 : {
100 0 : host = mm[1].str();
101 : }
102 0 : else if (regex_match(host_in, mm, std::regex(":{0,1}(\\d+)")))
103 : {
104 0 : host = std::string("127.0.0.1");
105 : }
106 0 : else if (regex_match(host_in, mm, std::regex("([^:]+):{0,1}")))
107 : {
108 0 : host = mm[1].str().c_str();
109 : }
110 : else
111 : {
112 0 : host = std::string("127.0.0.1");
113 : }
114 0 : TLOG(TLVL_INFO) << "Resolving ip " << host;
115 :
116 0 : memset(&addr, 0, sizeof(addr));
117 :
118 0 : if (regex_match(host.c_str(), mm, std::regex("\\d+(\\.\\d+){3}")))
119 : {
120 : in_addr desired_host;
121 0 : inet_aton(host.c_str(), &desired_host);
122 : struct ifaddrs *ifaddr, *ifa;
123 :
124 0 : if (getifaddrs(&ifaddr) == -1)
125 : {
126 0 : perror("getifaddrs");
127 0 : return -1;
128 : }
129 :
130 : /* Walk through linked list, maintaining head pointer so we
131 : can free list later */
132 :
133 0 : for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next)
134 : {
135 0 : if (ifa->ifa_addr == nullptr) continue;
136 :
137 : /* For an AF_INET* interface address, display the address */
138 :
139 0 : if (ifa->ifa_addr->sa_family == AF_INET)
140 : {
141 0 : auto if_addr = reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
142 0 : auto sa = reinterpret_cast<struct sockaddr_in*>(ifa->ifa_netmask); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
143 :
144 0 : TLOG(TLVL_DEBUG + 40) << "IF: " << ifa->ifa_name << " Desired: " << desired_host.s_addr
145 0 : << " netmask: " << sa->sin_addr.s_addr << " this interface: " << if_addr->sin_addr.s_addr;
146 :
147 0 : if ((if_addr->sin_addr.s_addr & sa->sin_addr.s_addr) == (desired_host.s_addr & sa->sin_addr.s_addr))
148 : {
149 0 : TLOG(TLVL_INFO) << "Using interface " << ifa->ifa_name;
150 0 : memcpy(&addr, &if_addr->sin_addr, sizeof(addr));
151 0 : break;
152 : }
153 : }
154 : }
155 0 : if (ifa == nullptr)
156 : {
157 0 : TLOG(TLVL_WARNING) << "No matches for ip " << host << ", using 0.0.0.0";
158 0 : inet_aton("0.0.0.0", &addr);
159 0 : sts = 2;
160 : }
161 :
162 0 : freeifaddrs(ifaddr);
163 : }
164 : else
165 : {
166 0 : hostent_sp = gethostbyname(host.c_str());
167 0 : if (!hostent_sp)
168 : {
169 0 : perror("gethostbyname");
170 0 : return (-1);
171 : }
172 0 : addr = *reinterpret_cast<struct in_addr*>(hostent_sp->h_addr_list[0]); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-pointer-arithmetic)
173 : }
174 0 : return sts;
175 0 : }
176 :
177 : /**
178 : * \brief Convert a string hostname and port to a sockaddr_in suitable for socket communication
179 : * \param host_in Name or IP of host to resolve
180 : * \param dflt_port POrt to populate in output
181 : * \param[out] sin sockaddr_in object populated with resolved host and port
182 : * \return 0 if success, -1 if gethostbyname fails
183 : */
184 0 : inline int ResolveHost(char const* host_in, int dflt_port, sockaddr_in& sin)
185 : {
186 : int port;
187 0 : std::string host;
188 : struct hostent* hostent_sp;
189 0 : std::cmatch mm;
190 : // Note: the regex expression used by regex_match has an implied ^ and $
191 : // at the beginning and end respectively.
192 0 : if (regex_match(host_in, mm, std::regex("([^:]+):(\\d+)")))
193 : {
194 0 : host = mm[1].str();
195 0 : port = strtoul(mm[2].str().c_str(), nullptr, 0);
196 : }
197 0 : else if (regex_match(host_in, mm, std::regex(":{0,1}(\\d+)")))
198 : {
199 0 : host = std::string("127.0.0.1");
200 0 : port = strtoul(mm[1].str().c_str(), nullptr, 0);
201 : }
202 0 : else if (regex_match(host_in, mm, std::regex("([^:]+):{0,1}")))
203 : {
204 0 : host = mm[1].str().c_str();
205 0 : port = dflt_port;
206 : }
207 : else
208 : {
209 0 : host = std::string("127.0.0.1");
210 0 : port = dflt_port;
211 : }
212 0 : TLOG(TLVL_INFO) << "Resolving host " << host << ", on port " << port;
213 :
214 0 : if (host == "localhost") host = "127.0.0.1";
215 :
216 0 : memset(&sin, 0, sizeof(sin));
217 0 : sin.sin_family = AF_INET;
218 0 : sin.sin_port = htons(port); // just a guess at an open port
219 :
220 0 : if (regex_match(host.c_str(), mm, std::regex("\\d+(\\.\\d+){3}")))
221 0 : inet_aton(host.c_str(), &sin.sin_addr);
222 : else
223 : {
224 0 : hostent_sp = gethostbyname(host.c_str());
225 0 : if (!hostent_sp)
226 : {
227 0 : perror("gethostbyname");
228 0 : return (-1);
229 : }
230 0 : sin.sin_addr = *reinterpret_cast<struct in_addr*>(hostent_sp->h_addr_list[0]); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-pointer-arithmetic)
231 : }
232 0 : return 0;
233 0 : }
234 :
235 : /**
236 : * \brief Connect to a host on a given port
237 : * \param host_in Name or IP of the host to connect to
238 : * \param dflt_port Port to connect to
239 : * \param flags TCP flags to use for the socket
240 : * \param sndbufsiz Size of the send buffer. Set to 0 for automatic send buffer management
241 : * \return File descriptor of connected socket.
242 : */
243 : inline int TCPConnect(char const* host_in, int dflt_port, int64_t flags = 0, int sndbufsiz = 0)
244 : {
245 : int s_fd, sts;
246 : struct sockaddr_in sin;
247 :
248 : s_fd = socket(PF_INET, SOCK_STREAM /*|SOCK_NONBLOCK*/, 0); // man socket,man TCP(7P)
249 :
250 : if (s_fd == -1)
251 : {
252 : perror("socket error");
253 : return (-1);
254 : }
255 :
256 : sts = ResolveHost(host_in, dflt_port, sin);
257 : if (sts == -1)
258 : {
259 : close(s_fd);
260 : return -1;
261 : }
262 :
263 : sts = connect(s_fd, reinterpret_cast<struct sockaddr*>(&sin), sizeof(sin)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
264 : if (sts == -1)
265 : {
266 : // perror( "connect error" );
267 : close(s_fd);
268 : return (-1);
269 : }
270 :
271 : if (flags)
272 : {
273 : sts = fcntl(s_fd, F_SETFL, flags);
274 : TLOG(TLVL_DEBUG + 33) << "TCPConnect fcntl(fd=" << s_fd << ",flags=0x" << std::hex << flags << std::dec << ") =" << sts;
275 : }
276 :
277 : if (sndbufsiz > 0)
278 : {
279 : int len;
280 : socklen_t lenlen = sizeof(len);
281 : len = 0;
282 : sts = getsockopt(s_fd, SOL_SOCKET, SO_SNDBUF, &len, &lenlen);
283 : TLOG(TLVL_DEBUG + 32) << "TCPConnect SNDBUF initial: " << len << " sts/errno=" << sts << "/" << errno
284 : << " lenlen=" << lenlen;
285 : len = sndbufsiz;
286 : sts = setsockopt(s_fd, SOL_SOCKET, SO_SNDBUF, &len, lenlen);
287 : if (sts == -1) TLOG(TLVL_ERROR) << "Error with setsockopt SNDBUF " << errno;
288 : len = 0;
289 : sts = getsockopt(s_fd, SOL_SOCKET, SO_SNDBUF, &len, &lenlen);
290 : if (len < (sndbufsiz * 2))
291 : TLOG(TLVL_WARNING) << "SNDBUF " << len << " not expected (" << sndbufsiz << " sts/errno=" << sts << "/" << errno
292 : << ")";
293 : else
294 : TLOG(TLVL_DEBUG + 32) << "SNDBUF " << len << " sts/errno=" << sts << "/" << errno;
295 : }
296 : return (s_fd);
297 : }
298 :
299 : #endif // TCPConnect_hh
|