| #!/usr/bin/python |
| # Copyright 2015 Google Inc. All Rights Reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # pylint: disable=line-too-long |
| |
| """Routines for handling DHCP signature information. |
| """ |
| |
| # Unit tests can override these. |
| DHCP_LEASES_FILE = '/config/dhcp.leases' |
| DHCP_SIGNATURE_FILE = '/config/dhcp.fingerprints' |
| |
| |
| # There is an enormous database of DHCP fingerprints at fingerbank.org. |
| # It is a 15 MByte SQLite DB. Thus far we're only using the DHCP |
| # signature as an additive when wifi signatures are indistinct, so |
| # we're only including the DHCP signatures which we will actually use. |
| database = { |
| '1,33,3,6,15,26,28,51,58,59': ['android'], |
| '1,33,3,6,15,28,51,58,59': ['android'], |
| '1,3,6,28,33,51,58,59,121': ['android'], |
| '1,121,33,3,6,15,28,51,58,59,119': ['android'], |
| |
| '1,3,6,15,112,113,78,79,95,252': ['appletv1'], |
| |
| '6,3,1,15,66,67,13,44,2,42,12': ['brotherprinter'], |
| |
| '1,3,6,15,44,47': ['canonprinter'], |
| |
| '1,121,33,3,6,12,15,26,28,51,54,58,59,119,252': ['chromeos'], |
| '1,121,33,3,6,12,15,26,28,51,54,58,59,119': ['chromeos'], |
| |
| '1,3,6': ['dashbutton', 'canonprinter'], |
| |
| '1,3,6,28': ['ecobee', 'canonprinter'], |
| |
| '1,3,6,12,15,17,28,40,41,42': ['epsonprinter'], |
| |
| '6,3,1,15,66,67,13,44': ['hpprinter'], |
| '6,3,1,15,66,67,13,44,12': ['hpprinter'], |
| '6,3,1,15,66,67,13,44,12,81': ['hpprinter'], |
| '6,3,1,15,66,67,13,44,119,12,81,252': ['hpprinter'], |
| '6,3,1,15,66,67,13,44,12,81,252': ['hpprinter'], |
| |
| '1,3,6,15,119,252': ['ios'], |
| '1,121,3,6,15,119,252': ['ios'], |
| |
| '1,3,6,15,119,95,252,44,46,47': ['ipodtouch1'], |
| |
| '252,3,42,15,6,1,12': ['lgtv', 'tizen'], |
| |
| '1,3,6,15,119,95,252,44,46,101': ['macos'], |
| '1,3,6,15,119,95,252,44,46': ['macos'], |
| '1,121,3,6,15,119,252,95,44,46': ['macos'], |
| |
| '58,59,6,15,51,54,1,3': ['panasonictv'], |
| |
| '1,3,15,6': ['playstation'], |
| |
| '1,3,6,15,12': ['roku'], |
| |
| '1,3,6,12,15,28,42,125': ['samsungtv'], |
| |
| '1,28,2,3,15,6,12': ['tivo'], |
| |
| '1,3,6,12,15,28,42': ['visiotv', 'wemo'], |
| '1,3,6,12,15,28,40,41,42': ['visiotv', 'kindle'], |
| |
| '1,3,6,15,28,33': ['wii'], |
| '1,3,6,15': ['wii', 'xbox'], |
| |
| '1,15,3,6,44,46,47,31,33,121,249,252,43': ['windows-phone', 'windows'], |
| '1,3,6,15,31,33,43,44,46,47,121,249,252': ['windows'], |
| } |
| |
| |
| def LookupOperatingSystem(mac): |
| """Lookup the operating system using a DHCP signature.""" |
| mac = mac.lower() |
| try: |
| with open(DHCP_SIGNATURE_FILE) as f: |
| for line in f: |
| try: |
| (physaddr, signature) = line.split() |
| except ValueError: |
| continue |
| if physaddr.lower() == mac: |
| return database.get(signature, []) |
| except IOError: |
| pass |
| return [] |
| |
| |
| def LookupHostname(mac): |
| """Lookup the hostname for a MAC address.""" |
| mac = mac.lower() |
| try: |
| with open(DHCP_LEASES_FILE) as f: |
| for line in f: |
| try: |
| (_, physaddr, _, name, _) = line.split() |
| except ValueError: |
| # There are other formats of lines in dhcp.leases, like DUID. |
| continue |
| if physaddr.lower() == mac: |
| return None if name == '*' else name |
| except IOError: |
| pass |
| return None |