Source code for BAC0.core.functions.GetIPAddr
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015 by Christian Tremblay, P.Eng <christian.tremblay@servisys.com>
#
# Licensed under LGPLv3, see file LICENSE in this source tree.
"""
Utility function to retrieve a functionnal IP and a correct broadcast IP
address.
Goal : not use 255.255.255.255 as a broadcast IP address as it is not
accepted by every devices (>3.8.38.1 bacnet.jar of Tridium Jace for example)
"""
import ipaddress
import socket
import typing as t
from bacpypes.pdu import Address
from ...core.utils.notes import note_and_log
from ..io.IOExceptions import NetworkInterfaceException
DEFAULT_PORT = 47808
[docs]@note_and_log
class HostIP:
"""
Special class to identify host IP informations
"""
def __init__(self, port: t.Optional[int] = None) -> None:
ip = self._findIPAddr()
mask = self._findSubnetMask(ip)
if port is not None:
self._port = port
else:
self._port = DEFAULT_PORT
self.interface = ipaddress.IPv4Interface("{}/{}".format(ip, mask))
@property
def ip_address_subnet(self):
"""
IP Address/subnet
"""
return "{}/{}".format(
self.interface.ip.compressed, self.interface.exploded.split("/")[-1]
)
@property
def ip_address(self):
"""
IP Address/subnet
"""
return "{}".format(self.interface.ip.compressed)
@property
def address(self) -> Address:
"""
IP Address using bacpypes Address format
"""
port = ""
if self._port:
port = ":{}".format(self._port)
return Address(
"{}/{}{}".format(
self.interface.ip.compressed,
self.interface.exploded.split("/")[-1],
port,
)
)
@property
def mask(self):
"""
Subnet mask
"""
return self.interface.exploded.split("/")[-1]
@property
def port(self):
"""
IP Port used
"""
return self._port
def _findIPAddr(self) -> str:
"""
Retrieve the IP address connected to internet... used as
a default IP address when defining Script
:returns: IP Adress as String
"""
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
s.connect(("google.com", 443))
addr = s.getsockname()[0]
s.close()
except socket.error:
raise NetworkInterfaceException(
"Impossible to retrieve IP, please provide one manually"
)
return addr
def _findSubnetMask(self, ip: str) -> str:
"""
Retrieve the broadcast IP address connected to internet... used as
a default IP address when defining Script
:param ip: (str) optionnal IP address. If not provided, default to getIPAddr()
:returns: broadcast IP Adress as String
"""
try:
import netifaces
interfaces = netifaces.interfaces()
for nic in interfaces:
addresses = netifaces.ifaddresses(nic)
try:
for address in addresses[netifaces.AF_INET]:
if address["addr"] == ip:
return address["netmask"]
except KeyError:
pass
return "255.255.255.255"
except ImportError:
self._log.warning(
"Netifaces not installed on your system. BAC0 can't detect the subnet.\nPlease provide subnet for now, "
"we'll consider 255.255.255.0 (/24).\nYou can install netifaces using 'pip install netifaces'."
)
return "255.255.255.0"
[docs]def validate_ip_address(ip: Address) -> bool:
result = True
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
if not isinstance(ip, Address):
raise ValueError("Provide Address as bacpypes.Address object")
s.bind(ip.addrTuple)
except OSError:
result = False
finally:
s.close()
return result