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