This is Unofficial EPICS BASE Doxygen Site
inetAddressUtil.cpp
Go to the documentation of this file.
1 
7 #include <vector>
8 #include <cstring>
9 #include <cstdlib>
10 #include <sstream>
11 
12 #include <osiSock.h>
13 #include <ellLib.h>
14 #include <errlog.h>
15 
16 #include <pv/pvType.h>
17 #include <pv/byteBuffer.h>
18 #include <pv/epicsException.h>
19 
20 #define epicsExportSharedSymbols
21 #include <pv/inetAddressUtil.h>
22 
23 // RTEMS 4.9 doesn't define this, but does implement SIOCGIFNETMASK
24 // and stores under the ifr_addr union member.
25 #ifndef ifr_netmask
26 # define ifr_netmask ifr_addr
27 #endif
28 
29 using namespace std;
30 using namespace epics::pvData;
31 
32 namespace epics {
33 namespace pvAccess {
34 
35 void encodeAsIPv6Address(ByteBuffer* buffer, const osiSockAddr* address) {
36  // IPv4 compatible IPv6 address
37  // first 80-bit are 0
38  buffer->putLong(0);
39  buffer->putShort(0);
40  // next 16-bits are 1
41  buffer->putShort(0xFFFF);
42  // following IPv4 address in big-endian (network) byte order
43  uint32_t ipv4Addr = ntohl(address->ia.sin_addr.s_addr);
44  buffer->putByte((int8)((ipv4Addr>>24)&0xFF));
45  buffer->putByte((int8)((ipv4Addr>>16)&0xFF));
46  buffer->putByte((int8)((ipv4Addr>>8)&0xFF));
47  buffer->putByte((int8)(ipv4Addr&0xFF));
48 }
49 
50 bool decodeAsIPv6Address(ByteBuffer* buffer, osiSockAddr* address) {
51 
52  // IPv4 compatible IPv6 address expected
53  // first 80-bit are 0
54  if (buffer->getLong() != 0) return false;
55  if (buffer->getShort() != 0) return false;
56  int16 ffff = buffer->getShort();
57  // allow all zeros address
58  //if (ffff != (int16)0xFFFF) return false;
59 
60  uint32 ipv4Addr = uint8(buffer->getByte());
61  ipv4Addr <<= 8;
62  ipv4Addr |= uint8(buffer->getByte());
63  ipv4Addr <<= 8;
64  ipv4Addr |= uint8(buffer->getByte());
65  ipv4Addr <<= 8;
66  ipv4Addr |= uint8(buffer->getByte());
67 
68  if (ffff != (int16)0xFFFF && ipv4Addr != (uint32_t)0)
69  return false;
70 
71  address->ia.sin_addr.s_addr = htonl(ipv4Addr);
72 
73  return true;
74 }
75 
76 bool isMulticastAddress(const osiSockAddr* address) {
77  uint32_t ipv4Addr = ntohl(address->ia.sin_addr.s_addr);
78  uint8_t msB = (uint8_t)((ipv4Addr>>24)&0xFF);
79  return msB >= 224 && msB <= 239;
80 }
81 
83  const std::string & list, int defaultPort,
84  const InetAddrVector* appendList) {
85  ret.clear();
86 
87  // skip leading spaces
88  size_t len = list.length();
89  size_t subStart = 0;
90  while (subStart < len && isspace(list[subStart]))
91  subStart++;
92 
93  // parse string
94  size_t subEnd;
95  while((subEnd = list.find(' ', subStart))!=std::string::npos) {
96  string address = list.substr(subStart, (subEnd-subStart));
97  osiSockAddr addr;
98  if (aToIPAddr(address.c_str(), defaultPort, &addr.ia) == 0)
99  ret.push_back(addr);
100  subStart = list.find_first_not_of(" \t\r\n\v", subEnd);
101  }
102 
103  if(subStart!=std::string::npos && subStart<len) {
104  osiSockAddr addr;
105  if (aToIPAddr(list.substr(subStart).c_str(), defaultPort, &addr.ia) == 0)
106  ret.push_back(addr);
107  }
108 
109  if(appendList!=NULL) {
110  for(size_t i = 0; i<appendList->size(); i++)
111  ret.push_back((*appendList)[i]);
112  }
113 }
114 
115 string inetAddressToString(const osiSockAddr &addr,
116  bool displayPort, bool displayHex) {
117  stringstream saddr;
118 
119  int ipa = ntohl(addr.ia.sin_addr.s_addr);
120 
121  saddr<<((int)(ipa>>24)&0xFF)<<'.';
122  saddr<<((int)(ipa>>16)&0xFF)<<'.';
123  saddr<<((int)(ipa>>8)&0xFF)<<'.';
124  saddr<<((int)ipa&0xFF);
125  if(displayPort) saddr<<":"<<ntohs(addr.ia.sin_port);
126  if(displayHex) saddr<<" ("<<hex<<ntohl(addr.ia.sin_addr.s_addr)
127  <<")";
128 
129  return saddr.str();
130 }
131 
132 ifaceNode::ifaceNode()
133 {
134  memset(&addr, 0, sizeof(addr));
135  memset(&peer, 0, sizeof(peer));
136  memset(&bcast, 0, sizeof(bcast));
137  memset(&mask, 0, sizeof(mask));
138  validBcast = validP2P = loopback = false;
139 }
140 
141 static
142 void checkNode(ifaceNode& node)
143 {
144  if(node.validBcast) {
145  /* Cross-check between addr, mask, and bcast to detect incorrect broadcast
146  * address. Why do admins insist on setting this seperately?!?
147  */
148  uint32 addr = ntohl(node.addr.ia.sin_addr.s_addr),
149  mask = ntohl(node.mask.ia.sin_addr.s_addr),
150  bcast= ntohl(node.bcast.ia.sin_addr.s_addr),
151  bcast_expect = (addr & mask) | ~mask;
152 
153  if(bcast == ntohl(INADDR_BROADCAST)) {
154  // translate global broadcast to iface broadcast.
155  // Windows (at least) will give us this sometimes.
156  bcast = bcast_expect;
157  node.bcast.ia.sin_addr.s_addr = htonl(bcast);
158  }
159 
160  if(bcast != bcast_expect) {
161  errlogPrintf("Warning: Inconsistent broadcast address on interface %08x/%08x. expect %08x found %08x.\n",
162  (unsigned)addr, (unsigned)mask, (unsigned)bcast_expect, (unsigned)bcast);
163  }
164  }
165 }
166 
167 #if !defined(_WIN32)
168 
169 /*
170  * Determine the size of an ifreq structure
171  * Made difficult by the fact that addresses larger than the structure
172  * size may be returned from the kernel.
173  */
174 static size_t ifreqSize ( struct ifreq *pifreq )
175 {
176  size_t size;
177 
178  size = ifreq_size ( pifreq );
179  if ( size < sizeof ( *pifreq ) ) {
180  size = sizeof ( *pifreq );
181  }
182  return size;
183 }
184 
185 /*
186  * Move to the next ifreq structure
187  */
188 static struct ifreq * ifreqNext ( struct ifreq *pifreq )
189 {
190  struct ifreq *ifr;
191 
192  ifr = ( struct ifreq * )( ifreqSize (pifreq) + ( char * ) pifreq );
193  return ifr;
194 }
195 
196 int discoverInterfaces(IfaceNodeVector &list, SOCKET socket, const osiSockAddr *pMatchAddr)
197 {
198  static const unsigned nelem = 100;
199  int status;
200  struct ifconf ifconf;
201  struct ifreq *pIfreqList;
202  struct ifreq *pIfreqListEnd;
203  struct ifreq *pifreq;
204  struct ifreq *pnextifreq;
205  int match;
206 
207  /*
208  * use pool so that we avoid using too much stack space
209  *
210  * nelem is set to the maximum interfaces
211  * on one machine here
212  */
213  pIfreqList = (struct ifreq *) calloc ( nelem, sizeof(*pifreq) );
214  if (!pIfreqList) {
215  errlogPrintf ("discoverInterfaces(): no memory to complete request\n");
216  return -1;
217  }
218 
219  ifconf.ifc_len = nelem * sizeof(*pifreq);
220  ifconf.ifc_req = pIfreqList;
221  status = socket_ioctl (socket, SIOCGIFCONF, &ifconf);
222  if (status < 0 || ifconf.ifc_len == 0) {
223  errlogPrintf ("discoverInterfaces(): unable to fetch network interface configuration\n");
224  free (pIfreqList);
225  return -1;
226  }
227 
228  pIfreqListEnd = (struct ifreq *) (ifconf.ifc_len + (char *) pIfreqList);
229  pIfreqListEnd--;
230 
231  for ( pifreq = pIfreqList; pifreq <= pIfreqListEnd; pifreq = pnextifreq ) {
232  uint32_t current_ifreqsize;
233 
234  /*
235  * find the next ifreq
236  */
237  pnextifreq = ifreqNext (pifreq);
238 
239  /* determine ifreq size */
240  current_ifreqsize = ifreqSize ( pifreq );
241  /* copy current ifreq to aligned bufferspace (to start of pIfreqList buffer) */
242  /* be careful as we re-use part of this space several times below.
243  * Any member other than ifr_name is invalidated by an ioctl() call
244  */
245  memmove(pIfreqList, pifreq, current_ifreqsize);
246 
247  /*
248  * If its not an internet interface then dont use it
249  */
250  if ( pIfreqList->ifr_addr.sa_family != AF_INET ) {
251  continue;
252  }
253 
254  /*
255  * if it isnt a wildcarded interface then look for
256  * an exact match
257  */
258  match = 0;
259  if ( pMatchAddr && pMatchAddr->sa.sa_family != AF_UNSPEC ) {
260  if ( pMatchAddr->sa.sa_family != AF_INET ) {
261  continue;
262  }
263  if ( pMatchAddr->ia.sin_addr.s_addr != htonl (INADDR_ANY) ) {
264  struct sockaddr_in *pInetAddr = (struct sockaddr_in *) &pIfreqList->ifr_addr;
265  if ( pInetAddr->sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr ) {
266  continue;
267  }
268  else
269  match = 1;
270  }
271  }
272 
273  ifaceNode node;
274  node.addr.sa = pIfreqList->ifr_addr;
275 
276  status = socket_ioctl ( socket, SIOCGIFFLAGS, pIfreqList );
277  if ( status ) {
278  errlogPrintf ("discoverInterfaces(): net intf flags fetch for \"%s\" failed\n", pIfreqList->ifr_name);
279  continue;
280  }
281 
282  unsigned short ifflags = pIfreqList->ifr_flags;
283  node.loopback = ifflags & IFF_LOOPBACK;
284 
285  /*
286  * dont bother with interfaces that have been disabled
287  */
288  if ( ! ( ifflags & IFF_UP ) ) {
289  continue;
290  }
291 
292  /*
293  * dont use the loop back interface, unless it maches pMatchAddr
294  */
295  if (!match) {
296  if ( ifflags & IFF_LOOPBACK ) {
297  continue;
298  }
299  }
300 
301  /*
302  * If this is an interface that supports
303  * broadcast fetch the broadcast address.
304  *
305  * Otherwise if this is a point to point
306  * interface then use the destination address.
307  *
308  * Otherwise CA will not query through the
309  * interface.
310  */
311  if ( ifflags & IFF_BROADCAST ) {
312  status = socket_ioctl (socket, SIOCGIFBRDADDR, pIfreqList);
313  if ( status ) {
314  errlogPrintf ("discoverInterfaces(): net intf \"%s\": bcast addr fetch fail\n", pIfreqList->ifr_name);
315  continue;
316  }
317  node.bcast.sa = pIfreqList->ifr_broadaddr;
318 
319  status = socket_ioctl (socket, SIOCGIFNETMASK, pIfreqList);
320  if ( status ) {
321  errlogPrintf ("discoverInterfaces(): net intf \"%s\": netmask fetch fail\n", pIfreqList->ifr_name);
322  continue;
323  }
324  node.mask.sa = pIfreqList->ifr_netmask;
325 
326  checkNode(node);
327 
328  node.validBcast = true;
329  }
330 #if defined (IFF_POINTOPOINT)
331  else if ( ifflags & IFF_POINTOPOINT ) {
332  status = socket_ioctl ( socket, SIOCGIFDSTADDR, pIfreqList);
333  if ( status ) {
334  continue;
335  }
336  node.peer.sa = pIfreqList->ifr_dstaddr;
337  node.validP2P = true;
338  }
339 #endif
340  else {
341  // if it is a match, accept the interface even if it does not support broadcast (i.e. 127.0.0.1 or point to point)
342  if (!match)
343  {
344  continue;
345  }
346  }
347 
348  list.push_back(node);
349  }
350 
351  free ( pIfreqList );
352  return 0;
353 }
354 
355 
356 #else
357 
358 #define VC_EXTRALEAN
359 #include <winsock2.h>
360 #include <ws2tcpip.h>
361 
362 int discoverInterfaces(IfaceNodeVector &list, SOCKET socket, const osiSockAddr *pMatchAddr)
363 {
364  int status;
365  INTERFACE_INFO *pIfinfo;
366  INTERFACE_INFO *pIfinfoList;
367  unsigned nelem;
368  int numifs;
369  DWORD cbBytesReturned;
370  int match;
371 
372  /* only valid for winsock 2 and above
373  TODO resolve dllimport compilation problem and uncomment this check
374  if (wsaMajorVersion() < 2 ) {
375  fprintf(stderr, "Need to set EPICS_CA_AUTO_ADDR_LIST=NO for winsock 1\n");
376  return -1;
377  }
378  */
379 
380  nelem = 100;
381  pIfinfoList = (INTERFACE_INFO *) calloc(nelem, sizeof(INTERFACE_INFO));
382  if(!pIfinfoList) {
383  return -1;
384  }
385 
386  status = WSAIoctl (socket, SIO_GET_INTERFACE_LIST,
387  NULL, 0,
388  (LPVOID)pIfinfoList, nelem*sizeof(INTERFACE_INFO),
389  &cbBytesReturned, NULL, NULL);
390 
391  if (status != 0 || cbBytesReturned == 0) {
392  fprintf(stderr, "WSAIoctl SIO_GET_INTERFACE_LIST failed %d\n",WSAGetLastError());
393  free(pIfinfoList);
394  return -1;
395  }
396 
397  numifs = cbBytesReturned/sizeof(INTERFACE_INFO);
398  for (pIfinfo = pIfinfoList; pIfinfo < (pIfinfoList+numifs); pIfinfo++) {
399 
400  /*
401  * dont bother with interfaces that have been disabled
402  */
403  if (!(pIfinfo->iiFlags & IFF_UP)) {
404  continue;
405  }
406 
407  /*
408  * If its not an internet interface then dont use it
409  * + work around WS2 bug
410  */
411  if (pIfinfo->iiAddress.Address.sa_family != AF_INET) {
412  if (pIfinfo->iiAddress.Address.sa_family == 0) {
413  pIfinfo->iiAddress.Address.sa_family = AF_INET;
414  }
415  else
416  continue;
417  }
418 
419  /*
420  * if it isnt a wildcarded interface then look for
421  * an exact match
422  */
423  match = 0;
424  if (pMatchAddr && pMatchAddr->sa.sa_family != AF_UNSPEC) {
425  if (pIfinfo->iiAddress.Address.sa_family != pMatchAddr->sa.sa_family) {
426  continue;
427  }
428  if (pIfinfo->iiAddress.Address.sa_family != AF_INET) {
429  continue;
430  }
431  if (pMatchAddr->sa.sa_family != AF_INET) {
432  continue;
433  }
434  if (pMatchAddr->ia.sin_addr.s_addr != htonl(INADDR_ANY)) {
435  if (pIfinfo->iiAddress.AddressIn.sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr) {
436  continue;
437  }
438  else
439  match = 1;
440  }
441  }
442 
443  /*
444  * dont use the loop back interface, unless it maches pMatchAddr
445  */
446  if (!match) {
447  if (pIfinfo->iiFlags & IFF_LOOPBACK) {
448  continue;
449  }
450  }
451 
452  ifaceNode node;
453  node.loopback = pIfinfo->iiFlags & IFF_LOOPBACK;
454  node.addr.ia = pIfinfo->iiAddress.AddressIn;
455 
456  if (pIfinfo->iiFlags & IFF_BROADCAST) {
457  node.mask.ia = pIfinfo->iiNetmask.AddressIn;
458  node.bcast.ia = pIfinfo->iiBroadcastAddress.AddressIn;
459  node.validBcast = true;
460  }
461  else if (pIfinfo->iiFlags & IFF_POINTTOPOINT) {
462  node.peer.ia = pIfinfo->iiNetmask.AddressIn;
463  node.validP2P = true;
464  }
465 
466  checkNode(node);
467 
468  list.push_back(node);
469  }
470 
471  free (pIfinfoList);
472  return 0;
473 }
474 
475 #endif
476 
477 }
478 }
int8_t int8
Definition: pvType.h:75
LIBCOM_API int epicsStdCall aToIPAddr(const char *pAddrString, unsigned short defaultPort, struct sockaddr_in *pIP)
Definition: aToIPAddr.c:78
EPICS_ALWAYS_INLINE int8 getByte()
Definition: byteBuffer.h:617
pvd::Status status
bool validBcast
true if bcast and mask have been set
int i
Definition: scan.c:967
struct sockaddr sa
Definition: osiSock.h:158
struct sockaddr_in ia
Definition: osiSock.h:157
Definition: memory.hpp:41
TODO only here because of the Lockable.
Definition: ntaggregate.cpp:16
#define NULL
Definition: catime.c:38
osiSockAddr mask
Net mask.
osiSockAddr peer
point to point peer
#define ifreq_size(pifreq)
Definition: osdSock.h:71
osiSockAddr addr
Our address.
#define socket_ioctl(A, B, C)
Definition: osdSock.h:34
A doubly-linked list library.
pvData
Definition: monitor.h:428
std::vector< osiSockAddr > InetAddrVector
void encodeAsIPv6Address(ByteBuffer *buffer, const osiSockAddr *address)
std::vector< ifaceNode > IfaceNodeVector
EPICS_ALWAYS_INLINE void putByte(int8 value)
Definition: byteBuffer.h:525
This class implements a Bytebuffer that is like the java.nio.ByteBuffer.
Definition: byteBuffer.h:233
int SOCKET
Definition: osdSock.h:31
EPICS_ALWAYS_INLINE int16 getShort()
Definition: byteBuffer.h:623
int errlogPrintf(const char *pFormat,...)
Definition: errlog.c:105
bool decodeAsIPv6Address(ByteBuffer *buffer, osiSockAddr *address)
bool isMulticastAddress(const osiSockAddr *address)
void getSocketAddressList(InetAddrVector &ret, const std::string &list, int defaultPort, const InetAddrVector *appendList)
bool validP2P
true if peer has been set.
EPICS_ALWAYS_INLINE int64 getLong()
Definition: byteBuffer.h:635
Definition: tool_lib.h:64
if(yy_init)
Definition: scan.c:972
int16_t int16
Definition: pvType.h:79
#define stderr
Definition: epicsStdio.h:32
EPICS_ALWAYS_INLINE void putLong(int64 value)
Definition: byteBuffer.h:543
osiSockAddr bcast
sub-net broadcast address
string inetAddressToString(const osiSockAddr &addr, bool displayPort, bool displayHex)
int discoverInterfaces(IfaceNodeVector &list, SOCKET socket, const osiSockAddr *pMatchAddr)
EPICS_ALWAYS_INLINE void putShort(int16 value)
Definition: byteBuffer.h:531
uint8_t uint8
Definition: pvType.h:91
uint32_t uint32
Definition: pvType.h:99