Giter VIP home page Giter VIP logo

getmac's Introduction

Latest version on PyPI Coverage Status GitHub Actions Pipeline Status Weekly PyPI downloads PyPI downloads PyPI downloadss of the old name Code style: black

Pure-Python package to get the MAC address of network interfaces and hosts on the local network.

It provides a platform-independent interface to get the MAC addresses of:

  • System network interfaces (by interface name)
  • Remote hosts on the local network (by IPv4/IPv6 address or hostname)

It provides one function: get_mac_address()

asciicast

asciicast

Should you use this package?

If you only need the addresses of network interfaces, have a limited set of platforms to support, and are able to handle C-extension modules, then you should instead check out the excellent netifaces package by Alastair Houghton (@al45tair). It's significantly faster (thanks to it being C-code) and has been around a long time and seen widespread usage. However, unfortunately it is no longer maintained as of May 2021, so it may not be a great choice for new projects. Another great option that fits these requirements is the well-known and battle-hardened psutil package by Giampaolo Rodola.

If the only system you need to run on is Linux, you can run as root, and C-extensions modules are fine, then you should instead check out the arpreq package by Sebastian Schrader. In some cases it can be significantly faster.

If you want to use psutil, scapy, or netifaces, I have examples of how to do so in a GitHub Gist.

Installation

Stable release from PyPI

pip install getmac

Latest development version

pip install https://github.com/ghostofgoes/getmac/archive/main.tar.gz

Python examples

from getmac import get_mac_address
eth_mac = get_mac_address(interface="eth0")
win_mac = get_mac_address(interface="Ethernet 3")
ip_mac = get_mac_address(ip="192.168.0.1")
ip6_mac = get_mac_address(ip6="::1")
host_mac = get_mac_address(hostname="localhost")
updated_mac = get_mac_address(ip="10.0.0.1", network_request=True)

# Enable debugging
from getmac import getmac
getmac.DEBUG = 2  # DEBUG level 2
print(getmac.get_mac_address(interface="Ethernet 3"))

# Change the UDP port used for updating the ARP table (UDP packet)
from getmac import getmac
getmac.PORT = 44444  # Default is 55555
print(getmac.get_mac_address(ip="192.168.0.1", network_request=True))

Terminal examples

Python 2 users: use getmac2 or python -m getmac instead of getmac.

getmac --help
getmac --version

# Invoking with no arguments will return MAC of the default interface
getmac

# Usage as a module
python3 -m getmac

# Interface names, IPv4/IPv6 addresses, or Hostnames can be specified
getmac --interface ens33
getmac --ip 192.168.0.1
getmac --ip6 ::1
getmac --hostname home.router

# Running as a Python module with shorthands for the arguments
python -m getmac -i 'Ethernet 4'
python -m getmac -4 192.168.0.1
python -m getmac -6 ::1
python -m getmac -n home.router

# Getting the MAC address of a remote host requires the ARP table to be populated.
# By default, getmac will populate the table by sending a UDP packet to a high port on the host (defaults to 55555).
# This can be disabled with --no-network-request, as shown here:
getmac --no-network-request --ip 192.168.0.1
python -m getmac --no-network-request -n home.router

# Enable output messages
getmac --verbose

# Debug levels can be specified with '-d'
getmac -v --debug
python -m getmac -v -d -i enp11s4
python -m getmac -v -dd -n home.router

# Change the UDP port used for populating the ARP table when getting the MAC of a remote host
getmac --ip 192.168.0.1 --override-port 9001

# The platform detected by getmac can be overridden via '--override-platform'.
# This is useful when debugging issues or if you know a method
# for a different platform works on the current platform.
# Any values returned by platform.system() are valid.
getmac -i eth0 --override-platform linux
getmac --ip 192.168.0.1 --override-platform windows

# Force a specific method to be used, regardless of the consequences or if it even works
getmac -v -dddd --ip 192.168.0.1 --force-method ctypeshost

Function: get_mac_address()

  • interface: Name of a network interface on the system
  • ip: IPv4 address of a remote host
  • ip6: IPv6 address of a remote host
  • hostname: Hostname of a remote host
  • network_request: If an network request should be made to update and populate the ARP/NDP table of remote hosts used to lookup MACs in most circumstances. Disable this if you want to just use what's already in the table, or if you have requirements to prevent network traffic. The network request is a empty UDP packet sent to a high port, 55555 by default. This can be changed by setting getmac.PORT to the desired integer value. Additionally, on Windows, this will send a UDP packet to 1.1.1.1:53 to attempt to determine the default interface (Note: the IP is CloudFlare's DNS server).

Configuration

  • logging.getLogger("getmac"): Runtime messages and errors are recorded to the getmac logger using Python's logging module. They can be configured by using logging.basicConfig() or adding handlers to the "getmac" logger.
  • getmac.getmac.DEBUG: integer value that controls debugging output. The higher the value, the more output you get.
  • getmac.getmac.PORT: UDP port used to populate the ARP/NDP table (see the documentation of the network_request argument in get_mac_address() for details)
  • getmac.getmac.OVERRIDE_PLATFORM: Override the platform detection with the given value (e.g. "linux", "windows", "freebsd", etc.'). Any values returned by platform.system() are valid.
  • getmac.getmac.FORCE_METHOD: Force a specific method to be used, e.g. 'IpNeighborShow'. This will be used regardless of it's method type or platform compatibility, and Method.test() will NOT be checked! The list of available methods is in getmac.getmac.METHODS.

Features

  • Pure-Python (no compiled C-extensions required!)
  • Python 2.7 and 3.4+
  • Lightweight, with no dependencies and a small package size
  • Can be dropped into a project as a standalone .py file
  • Supports most interpreters: CPython, pypy, pypy3, IronPython 2.7, and Jython 2.7
  • Provides a simple command line tool (when installed as a package)
  • MIT licensed!

Legacy Python versions

If you are running a old Python (2.6/3.3 and older) or interpreter, then you can install an older version of getmac that supported that version. The wheels are available in the GitHub releases, or from PyPI with a current version of pip and some special arguments.

  • Python 2.5: get-mac 0.5.0
  • Python 2.6: getmac 0.6.0
  • Python 3.2: get-mac 0.3.0
  • Python 3.3: get-mac 0.3.0

NOTE: these versions do not have many of the performance improvements, platform support, and bug fixes that came with later releases. They generally work, just not as well. However, if you're using such an old Python, you probably don't care about all that :)

Notes

  • Python 3.10 and 3.11 should work, but are not automatically tested at the moment due to having to support 2.7
  • If none of the arguments are selected, the default network interface for the system will be used. If the default network interface cannot be determined, then it will attempt to fallback to typical defaults for the platform (Ethernet on Windows, em0 on BSD, en0 on OSX/Darwin, and eth0 otherwise). If that fails, then it will fallback to lo on POSIX systems.
  • "Remote hosts" refer to hosts in your local layer 2 network, also commonly referred to as a "broadcast domain", "LAN", or "VLAN". As far as I know, there is not a reliable method to get a MAC address for a remote host external to the LAN. If you know any methods otherwise, please open a GitHub issue or shoot me an email, I'd love to be wrong about this.
  • The first four arguments are mutually exclusive. network_request does not have any functionality when the interface argument is specified, and can be safely set if using in a script.
  • The physical transport is assumed to be Ethernet (802.3). Others, such as Wi-Fi (802.11), are currently not tested or considered. I plan to address this in the future, and am definitely open to pull requests or issues related to this, including error reports.
  • Exceptions will be handled silently and returned as a None. If you run into problems, you can set DEBUG to true and get more information about what's happening. If you're still having issues, please create an issue on GitHub and include the output with DEBUG enabled.

Commands and techniques by platform

  • Windows
    • Commands: getmac.exe, ipconfig.exe, arp.exe, wmic.exe
    • Libraries: uuid, ctypes, socket
  • Linux/Unix
    • Commands: arp, ip, ifconfig, netstat, ip link, lanscan
    • Libraries: uuid, fcntl, socket
    • Files: /sys/class/net/{iface}/address, /proc/net/arp
    • Default interfaces: /proc/net/route, route, ip route list
  • Mac OSX (Darwin)
    • networksetup
    • Same commands as Linux
  • WSL
    • Windows commands are used for remote hosts
    • Unix commands are used for interfaces
  • OpenBSD
    • Commands: ifconfig, arp
    • Default interfaces: route
  • FreeBSD
    • Commands: ifconfig, arp
    • Default interfaces: netstat
  • Android
    • Commands: ip link

Platforms currently supported

All or almost all features should work on "supported" platforms. While other versions of the same family or distro may work, they are untested and may have bugs or missing features.

  • Windows
    • Desktop: 7, 8, 8.1, 10, 11 (thanks @StevenLooman for testing Windows 11!)
    • Server: TBD
    • Partially supported (untested): 2000, XP, Vista
  • Linux distros
    • CentOS/RHEL 6+
    • Ubuntu 16.04+ (15.10 and older should work, but are untested)
    • Fedora (24+)
  • Mac OSX (Darwin)
    • The latest two versions probably (TBD)
  • Android (6+)
  • Windows Subsystem for Linux (WSL)
  • FreeBSD (11+)
  • OpenBSD
  • Docker

Docker

Add -v /proc/1/net/arp:/host/arp -e ARP_PATH=/host/arp to access arp table of host inside container in bridge network mode.

docker build -f packaging/Dockerfile -t getmac .
docker run -it getmac:latest --help
docker run -it getmac:latest --version
docker run -it getmac:latest -n localhost
docker run --rm -it -v /proc/1/net/arp:/host/arp -e ARP_PATH=/host/arp getmac:latest -n 192.168.0.1

Caveats

  • Depending on the platform, there could be a performance detriment, due to heavy usage of regular expressions.
  • Platform test coverage is imperfect. If you're having issues, then you might be using a platform I haven't been able to test. Keep calm, open a GitHub issue, and I'd be more than happy to help.

Known Issues

  • Linux, WSL: Getting the mac of a local interface IP does not currently work (getmac --ip 10.0.0.4 will fail if 10.0.0.4 is the IP address of a local interface). This issue may be present on other POSIX systems as well.
  • Hostnames for IPv6 devices are not yet supported.
  • Windows: the "default" (used when no arguments set or specified) of selecting the default route interface only works effectively if network_request is enabled. If not, Ethernet is used as the default.
  • IPv6 support is good but lags behind IPv4 in some places and isn't as well-tested across the supported platform set.

Background and history

The Python standard library has a robust set of networking functionality, such as urllib, ipaddress, ftplib, telnetlib, ssl, and more. Imagine my surprise, then, when I discovered there was not a way to get a seemingly simple piece of information: a MAC address. This package was born out of a need to get the MAC address of hosts on the network without needing admin permissions, and a cross-platform way get the addresses of local interfaces.

In Fall 2018 the package name changed to getmac from get-mac. This affected the package name, the CLI script, and some of the documentation. There were no changes to the core library code. While both package names will updated on PyPI, the use of getmac is preferred.

In Summer 2020, the code was significantly refactored, moving to a class-based structure and significantly improving performance and accuracy. See docs/rewrite.md for details.

Contributing

Contributors are more than welcome! See the contribution guide to get started, and checkout the todo list for a full list of tasks and bugs.

Before submitting a PR, please make sure you've completed the pull request checklist!

The Python Discord server is a good place to ask questions or discuss the project (Handle: @KnownError#0001).

Contributors

  • Christopher Goes (@ghostofgoes) - Author and maintainer
  • Calvin Tran (@cyberhobbes) - Windows interface detection improvements
  • Daniel Flanagan (@FlantasticDan) - Code cleanup
  • @emadmahdi - Android fixes
  • Izra Faturrahman (@Frizz925) - Unit tests using the platform samples
  • Jose Gonzalez (@Komish) - Docker container and Docker testing
  • @fortunate-man - Awesome usage videos
  • @martmists - legacy Python compatibility improvements
  • @hargoniX - scripts and specfiles for RPM packaging
  • Ville Skyttä (@scop) - arping lookup support
  • Tomasz Duda (@tomaszduda23) - support for docker in network bridge mode
  • Steven Looman (@StevenLooman) - Windows 11 testing
  • Reimund Renner (@raymanP) - macOS fixes

Sources

Many of the methods used to acquire an address and the core logic framework are attributed to the CPython project's UUID implementation.

Other notable sources

License

MIT. Feel free to copy, modify, and use to your heart's content. Enjoy :)

getmac's People

Contributors

double-fault avatar emadmahdi avatar flantasticdan avatar frizz925 avatar ghostofgoes avatar hargonix avatar kofrezo avatar komish avatar martmists-gh avatar raymanp avatar scop avatar tomaszduda23 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

getmac's Issues

Tox code quality checks fail on clean clone of repo

Describe the bug
tox -e check fails in several places on a fresh clone of the repository.

To Reproduce

  1. Clone Repo: git clone https://github.com/GhostofGoes/getmac.git
  2. Install developer tools:
python -m pip install --user -U tox
python -m pip install -e .
  1. Run Code Quality Checks tox -e check

Expected behavior
All tests pass.

System info
(please complete the following information):

  • OS name: Ubuntu on Raspberry Pi 4
  • OS Version: 20.04
  • Python version: 3.9.5

Additional context
It seems two of the tests in the code quality suite fail:

check-manifest fails and exports the following:

lists of files in version control and sdist do not match!
missing from sdist:
  docs/Makefile
  docs/TODO.md
  docs/conf.py
  docs/packaging.md
  docs/releasing.md
  docs/requirements.txt
suggested MANIFEST.in rules:
  recursive-include docs *.md
  recursive-include docs *.py
  recursive-include docs *.txt
  recursive-include docs Makefile
ERROR: InvocationError for command /home/ubuntu/dev/getmac/.tox/check/bin/check-manifest . (exited with code 1)

This error is easily fixed by appending the output's suggestion to MANIFEST.in, which allows tox to continue to the next failed test, flake8:

getmac/getmac.py:165:1: B902 blind except Exception: statement
        except Exception:
^
getmac/getmac.py:301:1: B902 blind except Exception: statement
    except Exception:
^
getmac/getmac.py:352:1: B902 blind except Exception: statement
    except Exception:
^
getmac/getmac.py:396:20: BLK100 Black would make changes.
    return _search(r"^%s$" % MAC_RE_COLON, _popen("arping", "-r -C 1 -c 1 %s" % host),)
                   ^
getmac/getmac.py:413:5: B014 Redundant exception types in `except (OSError, IOError):`.  Write `except OSError:`, which catches exactly the same exceptions.
    except (OSError, IOError):  # This is IOError on Python 2.7
    ^
getmac/getmac.py:585:1: B902 blind except Exception: statement
        except Exception as ex:
^

These errors can be suppressed by adding B902, BLK100, B014 to the flake8 ignore attribute in tox.ini.

Not sure if suppressing these errors is the desired outcome, or if I'm the only one experiencing them. I can open a pull request that implements my suppressions if that's useful.

Test functionality on a Raspberry-Pi

Test that this works on a R-Pi.

  • Run tox
  • Run get-mac -i <interface> with any interfaces on the device
  • Run get-mac -n <ip> with the IP of the router or another device the Pi is networked to

GitHub Actions

Move from TravisCI/Appveyor to GitHub actions and setup automatic tagged releases.

GitHub release: changelog, .deb, .whl
PyPI: push to both package names (getmac, and get-mac with updated package metadata)

get-mac returns None in Python 3.6

Hi,
I'm trying to integrate this get_mac_address function on my Python script, but found it returns None from Python 3.6 while it returns correct MAC address on Python 2.7 even though same script is used.

I've gone through getmac.py and found that inetaddr has different value between Python 2.7 and 3.6.
def _windows_get_remote_mac_ctypes(host):
try:
inetaddr = ctypes.windll.wsock32.inetaddr(host))

I tried inserting the inetaddr value received from Python 2.7 on Python 3.6's getmac.py, and found that it returns MAC address.
Seems like there's difference on ctypes library between Python 2.7 and 3.6.
Do you have any solution to fix this issue?

Thanks,
Minjin

Fail to run on Kali Linux 2021.3

Describe the bug
Although I have "pip install getmac" in the terminal, when I "import getmac" in my Python program, it keeps reporting no module named getmac.

System info
(please complete the following information):

  • OS name: Kali Linux
  • OS Version: 2021.3
  • Python version: 3.9.7 x64

Drop support for unsupported Python versions (2.7 and 3.4)

The first "stable" release should not be chock full of hacks for unsupported Python versions. Presently, Python 2.7 makes up <10% of package installs (according to pypistats.org), and 3.4 almost none.

If someone is using those versions they can simply use the last release to support them, which will be 0.9.0.

Call to get_mac_address hangs when called with unknown IP-address

Describe the bug
If I call get_mac_address and specify an IP-address for which the corresponding ARP entry doesn't exist on the system, the call will hang for a long time. It doesn't matter the value of network_request.

To Reproduce

import logging
logging.basicConfig(level=logging.DEBUG)
import getmac
getmac.getmac.DEBUG = 1000  # Something high
getmac.get_mac_address(ip='10.0.0.1', network_request=False)

Yields this:

DEBUG:getmac:Trying: '_read_arp_file' (to_find: '10.0.0.1')
DEBUG:getmac:Result: None

DEBUG:getmac:Trying: '<lambda>' (to_find: '10.0.0.1')
DEBUG:getmac:Running: '/bin/ip neighbor show 10.0.0.1'
DEBUG:getmac:Output from '/bin/ip' command: b''
DEBUG:getmac:Exception: list index out of range
DEBUG:getmac:Traceback (most recent call last):
  File "/home/postlund/pyatv_dev/pyatv/lib/python3.6/site-packages/getmac/getmac.py", line 525, in _try_methods
    found = m(to_find)
  File "/home/postlund/pyatv_dev/pyatv/lib/python3.6/site-packages/getmac/getmac.py", line 485, in <lambda>
    .partition(x)[2].partition('lladdr')[2].strip().split()[0],
IndexError: list index out of range

DEBUG:getmac:Trying: 'arp 10.0.0.1'
DEBUG:getmac:Running: '/usr/sbin/arp 10.0.0.1'
DEBUG:getmac:Output from '/usr/sbin/arp' command: b'10.0.0.1 (10.0.0.1) -- no entry\n'
DEBUG:getmac:Result: None

DEBUG:getmac:Trying: 'arp -an'
DEBUG:getmac:Running: '/usr/sbin/arp -an'
DEBUG:getmac:Output from '/usr/sbin/arp' command: <<stripped>>
DEBUG:getmac:Result: None

DEBUG:getmac:Trying: 'arp -an 10.0.0.1'
DEBUG:getmac:Running: '/usr/sbin/arp -an 10.0.0.1'
DEBUG:getmac:Output from '/usr/sbin/arp' command: b'arp: in 40 entries no match found.\n'
DEBUG:getmac:Result: None

DEBUG:getmac:Trying: 'arp 10.0.0.1'
DEBUG:getmac:Running: '/usr/sbin/arp 10.0.0.1'
DEBUG:getmac:Output from '/usr/sbin/arp' command: b'10.0.0.1 (10.0.0.1) -- no entry\n'
DEBUG:getmac:Result: None

DEBUG:getmac:Trying: 'arp -a'
DEBUG:getmac:Running: '/usr/sbin/arp -a'
<<HANGS HERE>>

The address resolution probably takes all the time. Since the same command has already been run (with expected -n flag), the last call shouldn't even be needed.

Expected behavior
Should return "fast".

System info
(please complete the following information):

  • OS name: Debian Linux
  • OS Version: Some old version, like 7.1 maybe
  • Python version: 3.6.1 x64

Additional context
The command takes long time to execute if I run it manually from a shell as well.

Scanner fails to get MACs for localhost

I ran get_mac_address against an IP range, and I notice that when running it from my laptop, the two interfaces reported from the laptop are marked as "None". It is a Linux machine. Is there any reason it would not be able to obtain the MAC addresses from the device?

OpenBSD support

  • Hosts
  • Interfaces
  • Default interface detection
  • Samples of command output (e.g. the output of ip link list)
  • Test cases (these will use the samples)

`getmac -4 ip` doesn't return result in Mac OS X Mojave

Describe the bug
When you run getmac -4 192.168.1.252 (or any valid ip address), the macaddress is not printed out as a result.

To Reproduce
Using Mac OS X Mojave, run:

> python2.7 -m getmac -4 192.168.1.252
> 

Returns with no output.

If you run it with the --debug flag:

python2.7 -m getmac -4 192.168.1.252 --debug --no-network-request
Trying: 'cat /proc/net/arp'
Exception: Command '['/bin/cat', '/proc/net/arp']' returned non-zero exit status 1
Trying: '<lambda>' (to_find: '192.168.1.252')
Exception: [Errno 2] No such file or directory
Trying: 'arp 192.168.1.252'
Result: 28:cf:e9:1a:e7:17

Trying: 'arp -an'
Result: 28:cf:e9:1a:e7:17

Trying: 'arp -an 192.168.1.252'
Exception: Command '['/usr/sbin/arp', '-an', '192.168.1.252']' returned non-zero exit status 1
Trying: 'arp 192.168.1.252'
Result: 28:cf:e9:1a:e7:17

Trying: 'arp -a'
Result: 28:cf:e9:1a:e7:17

Trying: 'arp -a 192.168.1.252'
Exception: Command '['/usr/sbin/arp', '-a', '192.168.1.252']' returned non-zero exit status 1
Trying: '_uuid_ip' (to_find: '192.168.1.252')
Result: None

Trying: '_scapy_ip' (to_find: '192.168.1.252')
Exception: No module named scapy.layers.l2
Trying: '<lambda>' (to_find: '192.168.1.252')
Exception: No module named arpreq
Raw MAC found: None

So it seems to find the mac address through one of its methods, but it doesn't report it to the CLI. I tried this programmatically as well, and the call to get_mac_address returned None.

Expected behavior
The mac address should have been reported to CLI/function caller.

System info
(please complete the following information):

  • OS name: Mac OS X
  • OS Version: Mojave
  • Python version: [2.7.10 x64]

Additional context
Only tested with Mojave, but I'd be curious if it were happening on other OSes as well.

Documentation

Finish writing proper documentation

  • Complete documentation of the API (auto-generate using Sphinx)
  • Static documentation of CLI arguments (using sphinxcontrib-autoprogram)
  • Add Glossary of terms and link to the glossary from inside docs/code (e.g. acronyms)
  • Integrate CONTRIBUTING doc
  • Integrate packaging and release docs
  • Improve the current usage guide and add more examples
  • Page detailing the architecture (after the method refactor is finished)
  • Setup ReadTheDocs
  • Configure ReadTheDocs hook to update with new releases
  • Add ReadTheDocs URL to project_urls in setup.py

Feature: specify interface using the interface "index"

Support for Unix and Windows interface indices as a new argument to get_mac_address. On Windows, we could use wmic, while on Unix and Python 3 we can use socket.if_indextoname().

  • Investigate if it's worth adding to the API (evaluating use cases) and if it can be done in a platform-agnostic manner
  • Determine level of effort for platforms
  • Add issues for each relevant platform.

Platforms with a high level of effort can be deferred post-1.0. Want investigate and implement a stable API with at least one platform implementation before locking the API for the stable release.

Issue after diconnecting a device from the WiFi

Description of the issue:
When I get a mac address with get_mac_address(), disconnect the device with that address and run the command again, it still shows the mac address. When I restart my computer and run the command again, it says "00:00:00:00:00:00" again.

To Reproduce:
get mac address from your phone with get_mac_address(), disconnect your phone from the WiFi and run the script again

Expected behavior:
I think it shouldn't show the mac address, if the device isn't connected to the WiFi

System info:

  • OS name: Ubuntu
  • OS Version: 20.04.1 LTS
  • Python version: 3.8

Detection of default interface on Windows

Add dynamic and proper detection of the default interface on Windows. Essentially, we want to determine what you use to get the Internet, and use as the default. This is used when no arguments are specified to get_mac_address() or getmac. Currently, the "default" interface is whatever happens to be named "Ethernet", which is obviously not always the case.

This will involve parsing the output of route -4 PRINT. This is going to be a bit more complicated since the highest metric routes are going to be IP addresses and not interfaces. We'll have to resolve those to an interface, then select that interface as the default route. Alternatively, a quick hack would be to use route -6 PRINT (suggested by nine#1076 on Discord).

Various approaches to try and implement:

  • IPv4: netsh interface ipv4 show route
  • IPv6: netsh interface ipv6 show route
  • ipconfig
  • IPv4: route print -4
  • IPv6: route print -6
  • Windows API

Improve Security

Need to spend some quality time evaluating the security boundaries of the package and looking for issues. Anything found should either be a) fixed or b) risk accepted and clearly documented for end users.

SECURITY CONCERNS:

  • Cache file is untrusted (refactor-specific)
  • Results from command invocations are untrusted
  • Double-check validation of results before returning
  • Validate arguments to get_mac_address() to avoid command injection
    • Data types
    • IPv4/IPv6 addresses
    • Interface names
  • Path traversal ( #51 )
  • Better document security concerns/boundaries
    • Ability to make network requests in (document instances)
    • Commands that are executed
    • File reads
  • Environment being passed to subprocesses + env variables used (for instance, we're invoking subprocess to a potentially untrusted executable with our parent environment, which could include secret tokens like API keys or credentials)
  • Modifying PATH with /sbin and /usr/sbin

Contributors: please feel free to help out with any of these! Open a PR and mention this issue in the description of the PR. It can be as simple as documentation of the risk or raising user awareness.

The majority of developers using this package are not security experts, and many likely have little or no training or experience with security issues. Therefore any documentation shouldn't assume knowledge and should take the opportunity to educate (when feasible).

NetBSD support

  • Hosts
  • Interfaces
  • Default interface detection
  • Samples of command output (e.g. the output of ip link list)
  • Test cases (these will use the samples)

IPv6 hostname resolution

Currently, hostnames only resolve to IPv4 addresses, as documented in the "Known Issues" section in the README. They should be able to resolve to IPv6 addresses as well as IPv4, and the IPv6 returns handled by the appropriate IPv6 logic in getmac.

The obvious solution is to use socket.getaddrinfo instead of socket.gethostbyname to resolve a hostname to an IP address, it just needs to be implemented and tested.

Understanding test failures

Describe the bug

I'm packaging getmac for Nix / NixOS: NixOS/nixpkgs#78015.
We try to enable unit tests for other python packages for good hygiene. I cannot do this currently for getmac as some tests are failing.

I'm just looking for suggestions on what I'm doing wrong. I'm getting a type error which makes me think that there is possibly a problem with an assumption the test is making. (For example, I'm wondering if our sandbox prevents the code from seeing any MAC address, hence None instead of <string>.

Do you have any suggestions? Shall we disable these specific tests? Is it worth modifying the tests to have a fallback dummy MAC or does it defeat the point of the test entirely? Thanks.

To Reproduce

  1. Install nix
  2. Run nix-build https://github.com/colemickens/nixpkgs/archive/nixpkgs-ha-pkgs-getmac.tar.gz -A python3Packages.getmac

Expected behavior

The package is built, and runs tests successfully during build.

Actual behavior


=================================== FAILURES ===================================
_____________________________ test_cli_main_basic ______________________________
tests/test_cli.py:20: in test_cli_main_basic
    assert run_cmd(BASE_CMD) == get_mac_address()
E   AssertionError: assert '' == None
E    +  where '' = run_cmd(['/nix/store/s7vw8y02cqzx8bnjxl4bkhlnwvz6ws1s-python3-3.7.6/bin/python3.7', '-m', 'getmac'])
E    +  and   None = get_mac_address()
____________________________ test_cli_main_verbose _____________________________
tests/test_cli.py:24: in test_cli_main_verbose
    assert get_mac_address() in run_cmd(BASE_CMD + ["--verbose"])
E   TypeError: 'in <string>' requires string as left operand, not NoneType
_____________________________ test_cli_main_debug ______________________________
tests/test_cli.py:28: in test_cli_main_debug
    assert get_mac_address() in run_cmd(BASE_CMD + ["--verbose", "--debug"])
E   TypeError: 'in <string>' requires string as left operand, not NoneType
________________________ test_cli_multiple_debug_levels ________________________
tests/test_cli.py:49: in test_cli_multiple_debug_levels
    assert get_mac_address() in run_cmd(BASE_CMD + ["-v", "-dd"])
E   TypeError: 'in <string>' requires string as left operand, not NoneType

System info
(please complete the following information):

  • OS name: [e.g. Windows 10 x64]
  • OS Version: [1804 build 17134]
  • Python version: [3.6.5 x64]

Additional context
Add any other context about the problem here.

Test Windows Server

Need to ensure Windows support is the same for the Server version of the supported Desktop versions of Windows (7/8/8.1/10+). Would like to also test against the Core and Nano variants if possible, as well as inside Windows Docker containers (which is the primary deploy case for Nano IIRC). The easiest way to accomplish this for most of the SKUs is to spin up some VMs using Vagrant. If someone has some servers (or a cluster) they'd be willing to run the tests on, that'd be awesome as well.

Server:

  • 2008 R2
  • 2012
  • 2012 R2
  • 2016
  • 2019

Server Core

  • 2008 R2
  • 2012
  • 2012 R2
  • 2016
  • 2019

Nano

  • 2016

Containers

  • 2019 Core
  • 2019 Nano

Complete unit test coverage

Complete coverage of all methods, including _popen(). This will require mocking, and some other related hackery I don't have experience with yet.

Use logging instead of print for debugging output

Current, bare print statements are used to output debugging information if DEBUG is non-zero. This is only useful if someone is using the command line interface. The primary interface for getmac is as a Python package, and using print does not serve that well.

Example:

if DEBUG:
    print("Result: %s\n" % found)

The logging module provides a standard interface for storing log messages that can be managed and configured by the application using getmac. The print statements should be converted to logging.debug calls on a logger object, and the default nullhandler should be added to that logger. More details are in the Python docs: Configuring Logging for a Library

The above example with logging:

log.debug("Result: %s", found)

Performance profiling

Profile the execution and memory performance and add the results in a directory profiling/. Need to figure out where bottlenecks are across Python versions and operating systems ("platforms").

Type hint using stub file instead of inline comments

Move to using a *.pyi stub file for type hints instead of the current inline comments. There are a number of reasons I'd like to do this:

  • Ability to use complex types without adding more imports, as well as recent language features like forward references and TypedDict on a codebase that presently still supports 2.7
  • Readability: type comments don't look great and clutter the already ugly (in my opinion) code base
  • Performance: avoid attempting to import the typing module

RPM package

Build a RPM package for Linux distributions that use yum (RHEL, CentOS, Fedora). Need to make something that can be distributed on EPEL and meets the guidelines for CentOS and Fedora.

Usage videos

Add a few videos or animations to the README demonstrating usage of get-mac command and the Python module getmac. These can be recorded using asciinema or other such tools.

Change method selection strategy

The current strategy of finding a mac is to try as many methods as possible and hope for the best. This strategy severely punishes the case of a non-existent interface or host, not to mention being poor coding.

The first part of the solution is to have specific methods attached to platforms, and potentially platform sub-versions/releases. For platforms that we know well, this will likely be a single or handful of methods.

Next, we should try to look for "trapdoors" to exit early where ever possible. For instance, if we are able to read the arp table, but the host isn't present, then we should exit early instead of trying the rest of the host methods.

Path traversal mitigation

Presently, system binary paths are determined by reading the PATH environment variable. This is problematic from a security perspective, as it could potentially lead to code execution (symlinks, manipulation of PATH, etc.). One potential mitigation is to determine the locations of the executables required at startup without relying on PATH. This could be accomplished through system fingerprinting, e.g. "we know the current system is Ubuntu < 14 therefore check these paths".

This can be completed after the refactor is completed, as that'll make fingerprinting more reliable and provide a concise and deterministic (mostly) list of the executables (and paths) required.

WSL2 support

Add support for Windows Subsystem for Linux (WSL) version 2. It's basically just a slimmed down Linux VM so in theory everything will work as it should, just need to test and verify that's actually the case.

Raspberry pi, calling get_mac_address("localhost") or with your local hosts ip doesn't return correct value

On a Raspberry Pi, I'm looping around my subnet and when it gets to its own ip address, it doesn't return anything. You get 'none'.

using get_mac_address with either 'ip="your ip address"' or 'hostname="localhost"' doesn't return expected mac address. You get 'none' or '00:00:00:00:00:00'. Working fine for other ip addresses on the network.

pi@pi4-4:/home/linux $ ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.1.209 netmask 255.255.255.0 broadcast 192.168.1.255
inet6 fe80::6718:7e65:ae4f:c8a5 prefixlen 64 scopeid 0x20
inet6 2a00:23c5:af97:1f01:2f15:c490:9da1:ddbd prefixlen 64 scopeid 0x0
ether dc:a6:32:09:bf:45 txqueuelen 1000 (Ethernet)
RX packets 13787675 bytes 2276070684 (2.1 GiB)
RX errors 13658 dropped 13659 overruns 0 frame 0
TX packets 15114542 bytes 890824493 (849.5 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

pi@pi4-4:/home/linux $ python
Python 2.7.16 (default, Apr 6 2019, 01:42:57)
[GCC 8.2.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.

from getmac import get_mac_address
print get_mac_address(ip="192.168.1.209")
None
print get_mac_address(hostname="localhost")
00:00:00:00:00:00
print get_mac_address(hostname="pi4-4")
None
print get_mac_address()
dc:a6:32:09:bf:45
print get_mac_address(ip="192.168.1.131")
b8:27:eb:05:eb:64

I'd expect it to return the same thing as calling get_mac_address().

Raspberry Pi 4B, running Duster.

I also tried it on python3, but it just doesn't seem to work for me at all :-

pi@pi4-4:/home/linux $ python3
Python 3.7.3 (default, Apr 3 2019, 05:39:12)
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.

from getmac import get_mac_address
print get_mac_address(ip="192.168.1.209")
File "", line 1
print get_mac_address(ip="192.168.1.209")
^
SyntaxError: invalid syntax
print get_mac_address()
File "", line 1
print get_mac_address()
^
SyntaxError: invalid syntax

and just importing the whole module :-

pi@pi4-4:/home/linux $ python3
Python 3.7.3 (default, Apr 3 2019, 05:39:12)
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.

from getmac import getmac
print get_mac_address(ip="192.168.1.209")
File "", line 1
print get_mac_address(ip="192.168.1.209")
^
SyntaxError: invalid syntax

Sample collection script

Presently, the collection of samples (saved output of commands or scripts) is a highly manual and inconsistent process. Having a standardized script to automate this collection on variants of a platform would help greatly with development efforts, as well as troubleshooting.

This script needs to be able to run on multiple platforms, including Windows, OSX, Linux, BSDs, and various obscure and inane platforms.

To test, use Vagrant to spin up test machines. See the Vagrantfile in the repo root for usage: https://github.com/GhostofGoes/getmac/blob/master/Vagrantfile

Tasks:

  • Create a script to collect samples for all relevant commands on a platform and save output into the appropriately named sub-directory in samples/
  • Make a standardized set of commands for the script to run for each platform (e.g. Windows)

Stretch goal:

  • Add option to upload the results to Pastebin (or any other site that fits the purpose)

OpenSUSE support

  • Hosts
  • Interfaces
  • Default interface detection
  • Samples of command output (e.g. the output of ip link list)
  • Test cases (these will use the samples)

Debian package

Build a .deb package for Debian-based Linux distributions (Ubuntu, Kali, etc.)

FreeBSD support

  • Hosts
  • Interfaces
  • Default interface detection
  • Samples of command output (e.g. the output of ip link list)
  • Test cases (these will use the samples)

Solaris support

  • Hosts
  • Interfaces
  • Default interface detection
  • Samples of command output (e.g. the output of ip link list)
  • Test cases (these will use the samples)

Android support

  • Hosts
  • Interfaces
  • Default interface detection
  • Samples of command output (e.g. the output of ip link list)
  • Test cases (these will use the samples)

Unclosed Socket

Describe the bug
A socket is opened in line 143 of the getmac.py file. However, this socket is not closed again.

This leads to a "ResourceWarning".

/Users/xxxx/projects/netmap/netmap/network.py:18: ResourceWarning: unclosed <socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0, laddr=('0.0.0.0', 50240)>
  self.mac_address = get_mac_address(ip=str(self.ip_address.ip))
Object allocated at (most recent call last):
  File "/Users/xxxx/projects/netmap/venv/lib/python3.7/site-packages/getmac/getmac.py", lineno 143
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

To Reproduce
Run a unittest that contains a get_mac_address call that requests a mac through a ip.

Expected behavior
No Warning

System info
(please complete the following information):

  • OS name: macOS
  • OS Version: 10.14.4
  • Python version: 3.7.3 x64
  • getmac version: 0.8.0

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.