1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """miscellaneous network functions.
23 """
24
25 import array
26 import errno
27 import platform
28 import re
29 import socket
30 import struct
31
32 from twisted.internet import address
33
34 from flumotion.common import avltree
35
36 __version__ = "$Rev$"
37
38
39
40
41
42
43
44
45
46
47 system = platform.system()
48 if system == 'SunOS':
49 SIOCGIFCONF = 0xC008695C
50 SIOCGIFADDR = 0xC020690D
51 else:
52 SIOCGIFCONF = 0x8912
53 SIOCGIFADDR = 0x8915
54
55
57 """
58 Find the names of all available network interfaces
59 """
60 import fcntl
61 ptr_size = len(struct.pack('P', 0))
62 size = 24 + 2 * (ptr_size)
63 max_possible = 128
64 bytes = max_possible * size
65 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
66 names = array.array('B', '\0' * bytes)
67 outbytes = struct.unpack('iP', fcntl.ioctl(
68 s.fileno(),
69 SIOCGIFCONF,
70 struct.pack('iP', bytes, names.buffer_info()[0])))[0]
71 namestr = names.tostring()
72 return [namestr[i:i+size].split('\0', 1)[0]
73 for i in range(0, outbytes, size)]
74
75
77 """
78 Get the IP address for an interface
79 """
80 import fcntl
81 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
82 return socket.inet_ntoa(fcntl.ioctl(
83 s.fileno(),
84 SIOCGIFADDR,
85 struct.pack('256s', ifname[:15]))[20:24])
86
87
103
104
106 """
107 Attempt to guess a public hostname for this system.
108 """
109 ip = guess_public_ip()
110
111 try:
112 return socket.gethostbyaddr(ip)[0]
113 except socket.error:
114 return ip
115
116
118 try:
119 b1, b2, b3, b4 = map(int, s.split('.'))
120 except TypeError:
121 raise ValueError(s)
122
123 ret = 0
124 for n in b1, b2, b3, b4:
125 ret <<= 8
126 if n < 0 or n > 255:
127 raise ValueError(s)
128 ret += n
129 return ret
130
131
133 l = []
134 for i in range(4):
135 l.append((n>>(i*8)) & 0xff)
136 l.reverse()
137 return '.'.join(map(str, l))
138
139
141 tz = 0
142 if n == 0:
143
144 tz = 32
145 else:
146 while not (n & (1<<tz)):
147 tz += 1
148 return tz
149
150
152
153 - def fromFile(klass, f, requireNames=True, defaultRouteName='*default*'):
154 """
155 Make a new routing table, populated from entries in an open
156 file object.
157
158 The entries are expected to have the form:
159 IP-ADDRESS/MASK-BITS ROUTE-NAME
160
161 The `#' character denotes a comment. Empty lines are allowed.
162
163 @param f: file from whence to read a routing table
164 @type f: open file object
165 @param requireNames: whether to require route names in the file
166 @type requireNames: boolean, default to True
167 @param defaultRouteName: default name to give to a route if it
168 does not have a name in the file; only
169 used if requireNames is False
170 @type defaultRouteName: anything, defaults to '*default*'
171 """
172 comment = re.compile(r'^\s*#')
173 empty = re.compile(r'^\s*$')
174 entry = re.compile(r'^\s*'
175 r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'
176 r'/'
177 r'(\d{1,2})'
178 r'(\s+([^\s](.*[^\s])?))?\s*$')
179 ret = klass()
180 n = 0
181 for line in f:
182 n += 1
183 if comment.match(line) or empty.match(line):
184 continue
185 m = entry.match(line)
186 if not m:
187 raise ValueError('While loading routing table from file'
188 ' %s: line %d: invalid syntax: %r'
189 % (f, n, line))
190 route = m.group(4)
191 if route is None:
192 if requireNames:
193 raise ValueError('%s:%d: Missing required route name: %r'
194 % (f, n, line))
195 else:
196 route = defaultRouteName
197 ret.addSubnet(route, m.group(1), int(m.group(2)))
198 if route not in ret.routeNames:
199 ret.routeNames.append(route)
200
201 return ret
202 fromFile = classmethod(fromFile)
203
207
209 return self.routeNames
210
212 return (ipv4StringToInt(ipv4String),
213 ~((1 << (32 - maskBits)) - 1))
214
215 - def addSubnet(self, route, ipv4String, maskBits=32):
216 ipv4Int, mask = self._parseSubnet(ipv4String, maskBits)
217 if not ipv4Int & mask == ipv4Int:
218 raise ValueError('Net %s too specific for mask with %d bits'
219 % (ipv4String, maskBits))
220 self.avltree.insert((mask, ipv4Int, route))
221
225
228
232
235
237 """
238 Return the preferred route for this IP.
239
240 @param ip: The IP to use for routing decisions.
241 @type ip: An integer or string representing an IPv4 address
242 """
243 if isinstance(ip, str):
244 ip = ipv4StringToInt(ip)
245
246 for netmask, net, route in self:
247 if ip & netmask == net:
248 return route
249
250 return None
251
253 """
254 Return an iterator yielding routes in order of preference.
255
256 @param ip: The IP to use for routing decisions.
257 @type ip: An integer or string representing an IPv4 address
258 """
259 if isinstance(ip, str):
260 ip = ipv4StringToInt(ip)
261 for mask, net, route in self:
262 if ip & mask == net:
263 yield route
264
265 yield None
266
267
269 """
270 Get the host name of an IPv4 address.
271
272 @type a: L{twisted.internet.address.IPv4Address}
273 """
274 if not isinstance(a, address.IPv4Address) and not isinstance(a,
275 address.UNIXAddress):
276 raise TypeError("object %r is not an IPv4Address or UNIXAddress" % a)
277 if isinstance(a, address.UNIXAddress):
278 return 'localhost'
279
280 try:
281 host = a.host
282 except AttributeError:
283 host = a[1]
284 return host
285
286
288 """
289 Get the port number of an IPv4 address.
290
291 @type a: L{twisted.internet.address.IPv4Address}
292 """
293 assert(isinstance(a, address.IPv4Address))
294 try:
295 port = a.port
296 except AttributeError:
297 port = a[2]
298 return port
299
300
302 """Checks if the given port is unused
303 @param port: the port number or 0 for a random port
304 @type port: integer
305 @returns: port number or None if in use
306 @rtype: integer or None
307 """
308
309 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
310
311 try:
312 try:
313 s.bind(('', port))
314 port = s.getsockname()[1]
315 except socket.error, e:
316 if e.args[0] != errno.EADDRINUSE:
317 raise
318 port = None
319 finally:
320 s.close()
321
322 return port
323