1#   Copyright 2016 - The Android Open Source Project
2#
3#   Licensed under the Apache License, Version 2.0 (the "License");
4#   you may not use this file except in compliance with the License.
5#   You may obtain a copy of the License at
6#
7#       http://www.apache.org/licenses/LICENSE-2.0
8#
9#   Unless required by applicable law or agreed to in writing, software
10#   distributed under the License is distributed on an "AS IS" BASIS,
11#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12#   See the License for the specific language governing permissions and
13#   limitations under the License.
14
15import socket
16
17
18def get_available_host_port():
19    """Finds a semi-random available port.
20
21    A race condition is still possible after the port number is returned, if
22    another process happens to bind it.
23
24    Returns:
25        A port number that is unused on both TCP and UDP.
26    """
27    # On the 2.6 kernel, calling _try_bind() on UDP socket returns the
28    # same port over and over. So always try TCP first.
29    while True:
30        # Ask the OS for an unused port.
31        port = _try_bind(0, socket.SOCK_STREAM, socket.IPPROTO_TCP)
32        # Check if this port is unused on the other protocol.
33        if port and _try_bind(port, socket.SOCK_DGRAM, socket.IPPROTO_UDP):
34            return port
35
36
37def is_port_available(port):
38    """Checks if a given port number is available on the system.
39
40    Args:
41        port: An integer which is the port number to check.
42
43    Returns:
44        True if the port is available; False otherwise.
45    """
46    return (_try_bind(port, socket.SOCK_STREAM, socket.IPPROTO_TCP) and
47            _try_bind(port, socket.SOCK_DGRAM, socket.IPPROTO_UDP))
48
49
50def _try_bind(port, socket_type, socket_proto):
51    s = socket.socket(socket.AF_INET, socket_type, socket_proto)
52    try:
53        try:
54            s.bind(('', port))
55            # The result of getsockname() is protocol dependent, but for both
56            # IPv4 and IPv6 the second field is a port number.
57            return s.getsockname()[1]
58        except socket.error:
59            return None
60    finally:
61        s.close()
62