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