artdaq  v3_12_02
TCPConnect.cc
1 // This file (TCPConnect.cc) was created by Ron Rechenmacher <ron@fnal.gov> on
2 // Apr 26, 2010. "TERMS AND CONDITIONS" governing this file are in the README
3 // or COPYING file. If you do not have such a file, one can be obtained by
4 // contacting Ron or Fermi Lab in Batavia IL, 60510, phone: 630-840-3000.
5 // $RCSfile: TCPConnect.cpp,v $
6 // rev="$Revision: 1.4 $$Date: 2010/06/24 03:49:45 $";
7 
9 
10 #include "TRACE/tracemf.h"
11 #include "artdaq/DAQdata/Globals.hh"
12 #define TRACE_NAME (app_name + "_TCPConnect").c_str()
13 
14 #include <arpa/inet.h> // inet_aton
15 #include <netdb.h> // gethostbyname
16 #include <netinet/in.h> // struct sockaddr_in
17 #include <netinet/in.h> // inet_aton
18 #include <sys/socket.h> // socket, bind, listen, accept
19 #include <sys/socket.h> // inet_aton
20 #include <sys/types.h> // socket, bind, listen, accept
21 #include <unistd.h> // close
22 #include <cstdio> // printf
23 #include <cstdlib> // exit
24 #include <cstring> // bzero
25 
26 #include <ifaddrs.h>
27 #include <linux/if_link.h>
28 
29 #include <map>
30 #include <regex>
31 #include <string>
32 
33 // Return sts, put result in addr
34 int ResolveHost(char const *host_in, in_addr &addr)
35 {
36  std::string host;
37  struct hostent *hostent_sp;
38  std::cmatch mm;
39  // Note: the regex expression used by regex_match has an implied ^ and $
40  // at the beginning and end respectively.
41  if (regex_match(host_in, mm, std::regex("([^:]+):(\\d+)")))
42  {
43  host = mm[1].str();
44  }
45  else if (regex_match(host_in, mm, std::regex(":{0,1}(\\d+)")))
46  {
47  host = std::string("127.0.0.1");
48  }
49  else if (regex_match(host_in, mm, std::regex("([^:]+):{0,1}")))
50  {
51  host = mm[1].str();
52  }
53  else
54  {
55  host = std::string("127.0.0.1");
56  }
57  TLOG(TLVL_INFO) << "Resolving host " << host;
58 
59  memset(static_cast<void *>(&addr), 0, sizeof(addr));
60 
61  if (regex_match(host.c_str(), mm, std::regex(R"(\d+(\.\d+){3})")))
62  {
63  inet_aton(host.c_str(), &addr);
64  }
65  else
66  {
67  hostent_sp = gethostbyname(host.c_str());
68  if (hostent_sp == nullptr)
69  {
70  perror("gethostbyname");
71  return (-1);
72  }
73  addr = *reinterpret_cast<struct in_addr *>(hostent_sp->h_addr_list[0]); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-pointer-arithmetic)
74  }
75  return 0;
76 }
77 
78 int GetIPOfInterface(const std::string &interface_name, in_addr &addr)
79 {
80  int sts = 0;
81 
82  TLOG(TLVL_INFO) << "Finding address for interface " << interface_name;
83 
84  memset(static_cast<void *>(&addr), 0, sizeof(addr));
85 
86  struct ifaddrs *ifaddr, *ifa;
87 
88  if (getifaddrs(&ifaddr) == -1)
89  {
90  perror("getifaddrs");
91  return -1;
92  }
93 
94  /* Walk through linked list, maintaining head pointer so we
95  can free list later */
96 
97  for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next)
98  {
99  if (ifa->ifa_addr == nullptr)
100  {
101  continue;
102  }
103 
104  /* For an AF_INET* interface address, display the address */
105 
106  if (ifa->ifa_addr->sa_family == AF_INET)
107  {
108  auto if_addr = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
109 
110  TLOG(TLVL_DEBUG + 35) << "IF: " << ifa->ifa_name << " Desired: " << interface_name << " IP: " << if_addr->sin_addr.s_addr;
111 
112  if (std::string(ifa->ifa_name) == interface_name)
113  {
114  TLOG(TLVL_INFO) << "Interface " << ifa->ifa_name << " matches " << interface_name << " IP: " << if_addr->sin_addr.s_addr;
115  memcpy(&addr, &if_addr->sin_addr, sizeof(addr));
116  break;
117  }
118  }
119  }
120  if (ifa == nullptr)
121  {
122  TLOG(TLVL_WARNING) << "No matches for if " << interface_name << ", using 0.0.0.0";
123  inet_aton("0.0.0.0", &addr);
124  sts = 2;
125  }
126 
127  freeifaddrs(ifaddr);
128 
129  return sts;
130 }
131 
132 int AutodetectPrivateInterface(in_addr &addr)
133 {
134  int sts = 0;
135 
136  memset(static_cast<void *>(&addr), 0, sizeof(addr));
137 
138  struct ifaddrs *ifaddr, *ifa;
139 
140  enum ip_preference : int
141  {
142  IP_192 = 1,
143  IP_172 = 2,
144  IP_10 = 3,
145  IP_131 = 4
146  };
147 
148  struct in_addr addr_192, addr_172, addr_10, addr_131, nm_16, nm_12, nm_8;
149  inet_aton("192.168.0.0", &addr_192);
150  inet_aton("172.16.0.0", &addr_172);
151  inet_aton("10.0.0.0", &addr_10);
152  inet_aton("131.225.0.0", &addr_131);
153  inet_aton("255.255.0.0", &nm_16);
154  inet_aton("255.240.0.0", &nm_12);
155  inet_aton("255.0.0.0", &nm_8);
156 
157  std::map<ip_preference, in_addr> preference_map;
158 
159  if (getifaddrs(&ifaddr) == -1)
160  {
161  perror("getifaddrs");
162  return -1;
163  }
164 
165  /* Walk through linked list, maintaining head pointer so we
166  can free list later */
167 
168  for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next)
169  {
170  if (ifa->ifa_addr == nullptr)
171  {
172  continue;
173  }
174 
175  /* For an AF_INET* interface address, display the address */
176 
177  if (ifa->ifa_addr->sa_family == AF_INET)
178  {
179  auto if_addr = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
180 
181  TLOG(TLVL_DEBUG + 35) << "IF: " << ifa->ifa_name << " IP: " << if_addr->sin_addr.s_addr;
182 
183  if (preference_map.count(IP_192) == 0 && (if_addr->sin_addr.s_addr & nm_16.s_addr) == addr_192.s_addr)
184  {
185  preference_map[IP_192];
186  memcpy(&preference_map[IP_192], &if_addr->sin_addr, sizeof(addr));
187  }
188  else if (preference_map.count(IP_172) == 0 && (if_addr->sin_addr.s_addr & nm_12.s_addr) == addr_172.s_addr)
189  {
190  preference_map[IP_172];
191  memcpy(&preference_map[IP_172], &if_addr->sin_addr, sizeof(addr));
192  }
193  else if (preference_map.count(IP_10) == 0 && (if_addr->sin_addr.s_addr & nm_8.s_addr) == addr_10.s_addr)
194  {
195  preference_map[IP_10];
196  memcpy(&preference_map[IP_10], &if_addr->sin_addr, sizeof(addr));
197  }
198  else if (preference_map.count(IP_131) == 0 && (if_addr->sin_addr.s_addr & nm_16.s_addr) == addr_131.s_addr)
199  {
200  preference_map[IP_131];
201  memcpy(&preference_map[IP_131], &if_addr->sin_addr, sizeof(addr));
202  }
203  }
204  }
205 
206  if (preference_map.empty())
207  {
208  TLOG(TLVL_WARNING) << "AutodetectPrivateInterface: No matches, using 0.0.0.0";
209  inet_aton("0.0.0.0", &addr);
210  sts = 2;
211  }
212  else
213  {
214  TLOG(TLVL_INFO) << "AutodetectPrivateInterface: Using " << inet_ntoa(addr);
215  memcpy(&addr, &preference_map.begin()->second, sizeof(addr));
216  }
217 
218  freeifaddrs(ifaddr);
219 
220  return sts;
221 }
222 
223 // Return sts, put result in addr
224 int GetInterfaceForNetwork(char const *host_in, in_addr &addr)
225 {
226  std::string host;
227  struct hostent *hostent_sp;
228  std::cmatch mm;
229  int sts = 0;
230  // Note: the regex expression used by regex_match has an implied ^ and $
231  // at the beginning and end respectively.
232  if (regex_match(host_in, mm, std::regex("([^:]+):(\\d+)")))
233  {
234  host = mm[1].str();
235  }
236  else if (regex_match(host_in, mm, std::regex(":{0,1}(\\d+)")))
237  {
238  host = std::string("127.0.0.1");
239  }
240  else if (regex_match(host_in, mm, std::regex("([^:]+):{0,1}")))
241  {
242  host = mm[1].str();
243  }
244  else
245  {
246  host = std::string("127.0.0.1");
247  }
248  TLOG(TLVL_INFO) << "Resolving ip " << host;
249 
250  memset(static_cast<void *>(&addr), 0, sizeof(addr));
251 
252  if (regex_match(host.c_str(), mm, std::regex(R"(\d+(\.\d+){3})")))
253  {
254  in_addr desired_host;
255  inet_aton(host.c_str(), &desired_host);
256  struct ifaddrs *ifaddr, *ifa;
257 
258  if (getifaddrs(&ifaddr) == -1)
259  {
260  perror("getifaddrs");
261  return -1;
262  }
263 
264  /* Walk through linked list, maintaining head pointer so we
265  can free list later */
266 
267  for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next)
268  {
269  if (ifa->ifa_addr == nullptr)
270  {
271  continue;
272  }
273 
274  /* For an AF_INET* interface address, display the address */
275 
276  if (ifa->ifa_addr->sa_family == AF_INET)
277  {
278  auto if_addr = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
279  auto sa = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
280 
281  TLOG(TLVL_DEBUG + 35) << "IF: " << ifa->ifa_name << " Desired: " << desired_host.s_addr << " netmask: " << sa->sin_addr.s_addr << " this interface: " << if_addr->sin_addr.s_addr;
282 
283  if ((if_addr->sin_addr.s_addr & sa->sin_addr.s_addr) == (desired_host.s_addr & sa->sin_addr.s_addr))
284  {
285  TLOG(TLVL_INFO) << "Using interface " << ifa->ifa_name;
286  memcpy(&addr, &if_addr->sin_addr, sizeof(addr));
287  break;
288  }
289  }
290  }
291  if (ifa == nullptr)
292  {
293  if (host != std::string("0.0.0.0"))
294  {
295  TLOG(TLVL_WARNING) << "No matches for ip " << host << ", using 0.0.0.0";
296  }
297  inet_aton("0.0.0.0", &addr);
298  sts = 2;
299  }
300 
301  freeifaddrs(ifaddr);
302  }
303  else
304  {
305  hostent_sp = gethostbyname(host.c_str());
306  if (hostent_sp == nullptr)
307  {
308  perror("gethostbyname");
309  return (-1);
310  }
311  addr = *reinterpret_cast<struct in_addr *>(hostent_sp->h_addr_list[0]); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-pointer-arithmetic)
312  }
313  return sts;
314 }
315 
316 // Return sts, put result in sin
317 int ResolveHost(char const *host_in, int dflt_port, sockaddr_in &sin)
318 {
319  int port;
320  std::string host;
321  struct hostent *hostent_sp;
322  std::cmatch mm;
323  // Note: the regex expression used by regex_match has an implied ^ and $
324  // at the beginning and end respectively.
325  if (regex_match(host_in, mm, std::regex("([^:]+):(\\d+)")))
326  {
327  host = mm[1].str();
328  port = strtoul(mm[2].str().c_str(), nullptr, 0);
329  }
330  else if (regex_match(host_in, mm, std::regex(":{0,1}(\\d+)")))
331  {
332  host = std::string("127.0.0.1");
333  port = strtoul(mm[1].str().c_str(), nullptr, 0);
334  }
335  else if (regex_match(host_in, mm, std::regex("([^:]+):{0,1}")))
336  {
337  host = mm[1].str();
338  port = dflt_port;
339  }
340  else
341  {
342  host = std::string("127.0.0.1");
343  port = dflt_port;
344  }
345  TLOG(TLVL_INFO) << "Resolving host " << host << ", on port " << port;
346 
347  if (host == "localhost")
348  {
349  host = "127.0.0.1";
350  }
351 
352  memset(static_cast<void *>(&sin), 0, sizeof(sin));
353  sin.sin_family = AF_INET;
354  sin.sin_port = htons(port); // just a guess at an open port
355 
356  if (regex_match(host.c_str(), mm, std::regex(R"(\d+(\.\d+){3})")))
357  {
358  inet_aton(host.c_str(), &sin.sin_addr);
359  }
360  else
361  {
362  hostent_sp = gethostbyname(host.c_str());
363  if (hostent_sp == nullptr)
364  {
365  TLOG(TLVL_ERROR) << "Error calling gethostbyname: " << errno << " (" << strerror(errno) << ")";
366  perror("gethostbyname");
367  return (-1);
368  }
369  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)
370  }
371 
372  TLOG(TLVL_INFO) << "Host resolved as " << inet_ntoa(sin.sin_addr);
373  return 0;
374 }
375 // return connection fd.
376 //
377 int TCPConnect(char const *host_in, int dflt_port, int64_t flags, int sndbufsiz)
378 {
379  int s_fd, sts;
380  struct sockaddr_in sin;
381 
382  s_fd = socket(PF_INET, SOCK_STREAM /*|SOCK_NONBLOCK*/, 0); // man socket,man TCP(7P)
383 
384  if (s_fd == -1)
385  {
386  perror("socket error");
387  return (-1);
388  }
389 
390  sts = ResolveHost(host_in, dflt_port, sin);
391  if (sts == -1)
392  {
393  close(s_fd);
394  return -1;
395  }
396 
397  sts = connect(s_fd, reinterpret_cast<struct sockaddr *>(&sin), sizeof(sin)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
398  if (sts == -1)
399  {
400  // perror( "connect error" );
401  close(s_fd);
402  return (-1);
403  }
404 
405  if (flags != 0)
406  {
407  sts = fcntl(s_fd, F_SETFL, flags);
408  TLOG(TLVL_DEBUG + 33) << "TCPConnect fcntl(fd=" << s_fd << ",flags=0x" << std::hex << flags << std::dec << ") =" << sts;
409  }
410 
411  if (sndbufsiz > 0)
412  {
413  int len;
414  socklen_t lenlen = sizeof(len);
415  len = 0;
416  sts = getsockopt(s_fd, SOL_SOCKET, SO_SNDBUF, &len, &lenlen);
417  TLOG(TLVL_DEBUG + 32) << "TCPConnect SNDBUF initial: " << len << " sts/errno=" << sts << "/" << errno << " lenlen=" << lenlen;
418  len = sndbufsiz;
419  sts = setsockopt(s_fd, SOL_SOCKET, SO_SNDBUF, &len, lenlen);
420  if (sts == -1)
421  {
422  TLOG(TLVL_ERROR) << "Error with setsockopt SNDBUF " << errno;
423  }
424  len = 0;
425  sts = getsockopt(s_fd, SOL_SOCKET, SO_SNDBUF, &len, &lenlen);
426  if (len < (sndbufsiz * 2))
427  {
428  TLOG(TLVL_WARNING) << "SNDBUF " << len << " not expected (" << sndbufsiz << " sts/errno=" << sts << "/" << errno << ")";
429  }
430  else
431  {
432  TLOG(TLVL_DEBUG + 32) << "SNDBUF " << len << " sts/errno=" << sts << "/" << errno;
433  }
434  }
435  return (s_fd);
436 }
int ResolveHost(char const *host_in, in_addr &addr)
Convert a string hostname to a in_addr suitable for socket communication.
Definition: TCPConnect.cc:34
int GetIPOfInterface(const std::string &interface_name, in_addr &addr)
Get the IP address associated with a given interface name.
Definition: TCPConnect.cc:78
int TCPConnect(char const *host_in, int dflt_port, int64_t flags=0, int sndbufsiz=0)
Connect to a host on a given port.
Definition: TCPConnect.cc:377
int GetInterfaceForNetwork(char const *host_in, in_addr &addr)
Convert an IP address to the network address of the interface sharing the subnet mask.
Definition: TCPConnect.cc:224
int AutodetectPrivateInterface(in_addr &addr)
Pick a private IP address on this host.
Definition: TCPConnect.cc:132