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