1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 """
36 Warning: do not use this library. It is unstable and most of the routines
37 here have been superceded by other libraries (e.g. rosgraph). These
38 routines will likely be *deleted* in future releases.
39 """
40
41 import os
42 import socket
43 import struct
44 import sys
45 import platform
46
47 try:
48 from cStringIO import StringIO
49 python3 = 0
50 except ImportError:
51 from io import BytesIO
52 python3 = 1
53
54 try:
55 import urllib.parse as urlparse
56 except ImportError:
57 import urlparse
58
59
60 ROS_IP = 'ROS_IP'
61 ROS_HOSTNAME = 'ROS_HOSTNAME'
62
63 SIOCGIFCONF = 0x8912
64 SIOCGIFADDR = 0x8915
65 if platform.system() == 'FreeBSD':
66 SIOCGIFADDR = 0xc0206921
67 if platform.architecture()[0] == '64bit':
68 SIOCGIFCONF = 0xc0106924
69 else:
70 SIOCGIFCONF = 0xc0086924
71
72 if 0:
73
74 try:
75 import netifaces
76 _use_netifaces = True
77 except:
78
79
80
81 _use_netifaces = False
82 else:
83 _use_netifaces = False
84
92
94 """
95 @return: ROS_IP/ROS_HOSTNAME override or None
96 @rtype: str
97 @raise ValueError: if ROS_IP/ROS_HOSTNAME/__ip/__hostname are invalidly specified
98 """
99
100 for arg in sys.argv:
101 if arg.startswith('__hostname:=') or arg.startswith('__ip:='):
102 try:
103 _, val = arg.split(':=')
104 return val
105 except:
106 raise ValueError("invalid ROS command-line remapping argument '%s'"%arg)
107
108
109
110 if ROS_HOSTNAME in os.environ:
111 return os.environ[ROS_HOSTNAME]
112 elif ROS_IP in os.environ:
113 return os.environ[ROS_IP]
114 return None
115
117 """
118 @param hostname: host name/address
119 @type hostname: str
120 @return True: if hostname maps to a local address, False otherwise. False conditions include invalid hostnames.
121 """
122 try:
123 reverse_ip = socket.gethostbyname(hostname)
124 except socket.error:
125 return False
126
127 if reverse_ip not in get_local_addresses() and not reverse_ip.startswith('127.'):
128 return False
129 return True
130
132 """
133 @return: default local IP address (e.g. eth0). May be overriden by ROS_IP/ROS_HOSTNAME/__ip/__hostname
134 @rtype: str
135 """
136 override = get_address_override()
137 if override:
138 return override
139 addrs = get_local_addresses()
140 if len(addrs) == 1:
141 return addrs[0]
142 for addr in addrs:
143
144 if not addr.startswith('127.'):
145 return addr
146 else:
147 return '127.0.0.1'
148
149
150 _local_addrs = None
152 """
153 @return: known local addresses. Not affected by ROS_IP/ROS_HOSTNAME
154 @rtype: [str]
155 """
156
157 global _local_addrs
158 if _local_addrs is not None:
159 return _local_addrs
160
161 local_addrs = None
162 if _use_netifaces:
163
164
165 local_addrs = []
166
167 for i in netifaces.interfaces():
168 try:
169 local_addrs.extend([d['addr'] for d in netifaces.ifaddresses(i)[netifaces.AF_INET]])
170 except KeyError: pass
171 elif _is_unix_like_platform():
172
173
174
175
176 import fcntl
177 import array
178
179 ifsize = 32
180 if platform.system() == 'Linux' and platform.architecture()[0] == '64bit':
181 ifsize = 40
182
183
184
185 max_bytes = 32 * ifsize
186
187 buff = array.array('B', '\0' * max_bytes)
188
189 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
190 info = fcntl.ioctl(sock.fileno(), SIOCGIFCONF,
191 struct.pack('iL', max_bytes, buff.buffer_info()[0]))
192 retbytes = struct.unpack('iL', info)[0]
193 buffstr = buff.tostring()
194 if platform.system() == 'Linux':
195 local_addrs = [socket.inet_ntoa(buffstr[i+20:i+24]) for i in range(0, retbytes, ifsize)]
196 else:
197
198
199
200
201 local_addrs = []
202 bufpos = 0
203 while bufpos < retbytes:
204 bufpos += 16
205 ifreqsize = ord(buffstr[bufpos])
206 if ifreqsize == 16:
207 local_addrs += [socket.inet_ntoa(buffstr[bufpos+4:bufpos+8])]
208 bufpos += ifreqsize
209 else:
210
211 local_addrs = [socket.gethostbyname(socket.gethostname())]
212 _local_addrs = local_addrs
213 return local_addrs
214
215
217 """
218 @param address: (optional) address to compare against
219 @type address: str
220 @return: address TCP/IP sockets should use for binding. This is
221 generally 0.0.0.0, but if \a address or ROS_IP/ROS_HOSTNAME is set
222 to localhost it will return 127.0.0.1
223 @rtype: str
224 """
225 if address is None:
226 address = get_address_override()
227 if address and \
228 (address == 'localhost' or address.startswith('127.')):
229
230 return '127.0.0.1'
231 else:
232 return '0.0.0.0'
233
234
236 """
237 Determine host-name for use in host-name-based addressing (e.g. XML-RPC URIs):
238 - if ROS_IP/ROS_HOSTNAME is set, use that address
239 - if the hostname returns a non-localhost value, use that
240 - use whatever L{get_local_address()} returns
241 """
242 hostname = get_address_override()
243 if not hostname:
244 try:
245 hostname = socket.gethostname()
246 except:
247 pass
248 if not hostname or hostname == 'localhost' or hostname.startswith('127.'):
249 hostname = get_local_address()
250 return hostname
251
253 """
254 Determine the XMLRPC URI for local servers. This handles the search
255 logic of checking ROS environment variables, the known hostname,
256 and local interface IP addresses to determine the best possible
257 URI.
258
259 @param port: port that server is running on
260 @type port: int
261 @return: XMLRPC URI
262 @rtype: str
263 """
264
265
266 return 'http://%s:%s/'%(get_host_name(), port)
267
268
269
270
272 """
273 Exception to represent errors decoding handshake
274 """
275 pass
276
278 """
279 Decode serialized ROS handshake header into a Python dictionary
280
281 header is a list of string key=value pairs, each prefixed by a
282 4-byte length field. It is preceeded by a 4-byte length field for
283 the entire header.
284
285 @param header_str: encoded header string. May contain extra data at the end.
286 @type header_str: str
287 @return: key value pairs encoded in \a header_str
288 @rtype: {str: str}
289 """
290 (size, ) = struct.unpack('<I', header_str[0:4])
291 size += 4
292 header_len = len(header_str)
293 if size > header_len:
294 raise ROSHandshakeException("Incomplete header. Expected %s bytes but only have %s"%((size+4), header_len))
295
296 d = {}
297 start = 4
298 while start < size:
299 (field_size, ) = struct.unpack('<I', header_str[start:start+4])
300 if field_size == 0:
301 raise ROSHandshakeException("Invalid 0-length handshake header field")
302 start += field_size + 4
303 if start > size:
304 raise ROSHandshakeException("Invalid line length in handshake header: %s"%size)
305 line = header_str[start-field_size:start]
306
307
308 if python3 == 1:
309 line = line.decode()
310
311 idx = line.find("=")
312 if idx < 0:
313 raise ROSHandshakeException("Invalid line in handshake header: [%s]"%line)
314 key = line[:idx]
315 value = line[idx+1:]
316 d[key.strip()] = value
317 return d
318
320 """
321 Read in tcpros header off the socket \a sock using buffer \a b.
322
323 @param sock: socket must be in blocking mode
324 @type sock: socket
325 @param b: buffer to use
326 @type b: StringIO for Python2, BytesIO for Python 3
327 @param buff_size: incoming buffer size to use
328 @type buff_size: int
329 @return: key value pairs encoded in handshake
330 @rtype: {str: str}
331 @raise ROSHandshakeException: If header format does not match expected
332 """
333 header_str = None
334 while not header_str:
335 d = sock.recv(buff_size)
336 if not d:
337 raise ROSHandshakeException("connection from sender terminated before handshake header received. %s bytes were received. Please check sender for additional details."%b.tell())
338 b.write(d)
339 btell = b.tell()
340 if btell > 4:
341
342
343 bval = b.getvalue()
344 (size,) = struct.unpack('<I', bval[0:4])
345 if btell - 4 >= size:
346 header_str = bval
347
348
349 leftovers = bval[size+4:]
350 b.truncate(len(leftovers))
351 b.seek(0)
352 b.write(leftovers)
353 header_recvd = True
354
355
356 return decode_ros_handshake_header(bval)
357
359 """
360 Encode ROS handshake header as a byte string. Each header
361 field is a string key value pair. The encoded header is
362 prefixed by a length field, as is each field key/value pair.
363 key/value pairs a separated by a '=' equals sign.
364
365 FORMAT: (4-byte length + [4-byte field length + field=value ]*)
366
367 @param header: header field keys/values
368 @type header: dict
369 @return: header encoded as byte string
370 @rtype: str
371 """
372 fields = ["%s=%s"%(k,v) for k,v in header.items()]
373
374
375 if python3 == 0:
376
377 s = ''.join(["%s%s"%(struct.pack('<I', len(f)), f) for f in fields])
378 return struct.pack('<I', len(s)) + s
379 else:
380
381 s = b''.join([(struct.pack('<I', len(f)) + f.encode("utf-8")) for f in fields])
382 return struct.pack('<I', len(s)) + s
383
385 """
386 Write ROS handshake header header to socket sock
387 @param sock: socket to write to (must be in blocking mode)
388 @type sock: socket.socket
389 @param header: header field keys/values
390 @type header: {str : str}
391 @return: Number of bytes sent (for statistics)
392 @rtype: int
393 """
394 s = encode_ros_handshake_header(header)
395 sock.sendall(s)
396 return len(s)
397