From 3eaba33cb0de944b587b405659690d087785e32d Mon Sep 17 00:00:00 2001 From: saifullah-N Date: Sat, 24 Dec 2022 16:40:57 +0530 Subject: [PATCH] iw_parse pkg --- setup.py | 2 +- src/py/iw_parse/README.md | 80 +++++++++ src/py/iw_parse/__init__.py | 1 + src/py/iw_parse/iw_parse | 25 +++ src/py/iw_parse/iw_parse.py | 337 ++++++++++++++++++++++++++++++++++++ src/py/iw_parse/license.txt | 10 ++ 6 files changed, 454 insertions(+), 1 deletion(-) create mode 100644 src/py/iw_parse/README.md create mode 100644 src/py/iw_parse/__init__.py create mode 100644 src/py/iw_parse/iw_parse create mode 100644 src/py/iw_parse/iw_parse.py create mode 100644 src/py/iw_parse/license.txt diff --git a/setup.py b/setup.py index e3ff885..4e061fb 100755 --- a/setup.py +++ b/setup.py @@ -17,7 +17,7 @@ setup( license = pkg['license'], url = pkg['homepage'], package_dir = {'': 'src/py'}, - packages = ['bbctrl', 'inevent', 'lcd', 'camotics'], + packages = ['bbctrl', 'inevent', 'lcd', 'camotics','iw_parse'], include_package_data = True, entry_points = { 'console_scripts': [ diff --git a/src/py/iw_parse/README.md b/src/py/iw_parse/README.md new file mode 100644 index 0000000..4f33936 --- /dev/null +++ b/src/py/iw_parse/README.md @@ -0,0 +1,80 @@ +iw_parse +======== + +Parse the output of iwlist scan to get the name, address, quality, channel, and encryption type of all networks broadcasting within your Wireless NIC's reach. + +Dependencies +------------ + +* [pip](http://www.pip-installer.org/en/latest/installing.html "pip installation guide") - If you don't have pip installed, followed the link. + + +Installation +------------ + +```bash +pip install iw_parse +``` + +Usage +----- + +```bash +iwlist scan | iw_parse +``` + +Replace `` with the system name for your wireless NIC. It is usually something like `wlan0`. The command `iwconfig` will list all of your network interfaces. + +Example: + +```bash +iwlist wlan0 scan | iw_parse +``` + +The result should look something like: + +``` +Name Address Quality Channel Encryption +wireless1 20:AA:4B:34:2C:F5 100 % 11 WEP +wireless2 00:26:F2:1E:FC:03 84 % 1 WPA v.1 +wireless3 00:1D:D3:6A:3C:60 66 % 6 WEP +wireless4 20:10:7A:E5:02:98 64 % 1 WEP +wireless5 CC:A4:62:B7:D2:B0 54 % 8 WPA v.1 +wireless6 30:46:9A:53:3C:76 47 % 11 WPA v.1 +wireless7 A0:21:B7:5F:84:B0 44 % 11 WEP +wireless8 04:A1:51:18:E8:E0 41 % 6 WPA v.1 +``` + +Example from Python shell: + +```python +>>> import iw_parse +>>> networks = iw_parse.get_interfaces(interface='wlan0') +>>> print networks +[{'Address': 'F8:1E:DF:F9:B0:0B', + 'Channel': '3', + 'Encryption': 'WEP', + 'Name': 'Francis', + 'Bit Rates': '144 Mb/s', + 'Signal Level': '42', + 'Name': 'Francis', + 'Quality': '100'}, + {'Address': '86:1B:5E:33:17:D4', + 'Channel': '6', + 'Encryption': 'Open', + 'Bit Rates': '54 Mb/s', + 'Signal Level': '72', + 'Name': 'optimumwifi', + 'Quality': '100'}, + ... +``` + +Acknowledgements +---------------- + +* The vast majority of iw_parse was written by Hugo Chargois. + +License +------- + +iw_parse is free--as in BSD. Hack your heart out, hackers. diff --git a/src/py/iw_parse/__init__.py b/src/py/iw_parse/__init__.py new file mode 100644 index 0000000..df26230 --- /dev/null +++ b/src/py/iw_parse/__init__.py @@ -0,0 +1 @@ +from .iw_parse import * diff --git a/src/py/iw_parse/iw_parse b/src/py/iw_parse/iw_parse new file mode 100644 index 0000000..022997e --- /dev/null +++ b/src/py/iw_parse/iw_parse @@ -0,0 +1,25 @@ +#! /usr/bin/env python + +import sys + +from iw_parse import get_parsed_cells, print_cells + +def main(): + """ Pretty prints the output of iwlist scan into a table. """ + parsed_cells = get_parsed_cells(sys.stdin) + + # You can choose which columns to display here, and most importantly + # in what order. Of course, they must exist as keys in the dict rules. + columns = [ + "Name", + "Address", + "Quality", + "Channel", + "Signal Level", + "Encryption" + ] + + print_cells(parsed_cells, columns) + +if __name__ == "__main__": + main() diff --git a/src/py/iw_parse/iw_parse.py b/src/py/iw_parse/iw_parse.py new file mode 100644 index 0000000..9be9007 --- /dev/null +++ b/src/py/iw_parse/iw_parse.py @@ -0,0 +1,337 @@ +#! /usr/bin/env python + +# Hugo Chargois - 17 jan. 2010 - v.0.1 +# Parses the output of iwlist scan into a table + +# You can add or change the functions to parse the properties +# of each AP (cell) below. They take one argument, the bunch of text +# describing one cell in iwlist scan and return a property of that cell. + +import re +import subprocess + +VERSION_RGX = re.compile("version\s+\d+", re.IGNORECASE) + +def get_name(cell): + """ Gets the name / essid of a network / cell. + @param string cell + A network / cell from iwlist scan. + + @return string + The name / essid of the network. + """ + + essid = matching_line(cell, "ESSID:") + if not essid: + return "" + return essid[1:-1] + +def get_quality(cell): + """ Gets the quality of a network / cell. + @param string cell + A network / cell from iwlist scan. + + @return string + The quality of the network. + """ + + quality = matching_line(cell, "Quality=") + if quality is None: + return "" + quality = quality.split()[0].split("/") + quality = matching_line(cell, "Quality=").split()[0].split("/") + return str(int(round(float(quality[0]) / float(quality[1]) * 100))) + +def get_signal_level(cell): + """ Gets the signal level of a network / cell. + @param string cell + A network / cell from iwlist scan. + + @return string + The signal level of the network. + """ + + signal = matching_line(cell, "Signal level=") + if signal is None: + return "" + signal = signal.split("=")[1].split("/") + if len(signal) == 2: + return str(int(round(float(signal[0]) / float(signal[1]) * 100))) + elif len(signal) == 1: + return signal[0].split(' ')[0] + else: + return "" + +def get_noise_level(cell): + """ Gets the noise level of a network / cell. + @param string cell + A network / cell from iwlist scan. + + @return string + The noise level of the network. + """ + + noise = matching_line(cell, "Noise level=") + if noise is None: + return "" + noise = noise.split("=")[1] + return noise.split(' ')[0] + +def get_channel(cell): + """ Gets the channel of a network / cell. + @param string cell + A network / cell from iwlist scan. + + @return string + The channel of the network. + """ + + channel = matching_line(cell, "Channel:") + if channel: + return channel + frequency = matching_line(cell, "Frequency:") + channel = re.sub(r".*\(Channel\s(\d{1,3})\).*", r"\1", frequency) + return channel + +def get_frequency(cell): + """ Gets the frequency of a network / cell. + @param string cell + A network / cell from iwlist scan. + + @return string + The frequency of the network. + """ + + frequency = matching_line(cell, "Frequency:") + if frequency is None: + return "" + return frequency.split()[0] + +def get_encryption(cell, emit_version=False): + """ Gets the encryption type of a network / cell. + @param string cell + A network / cell from iwlist scan. + + @return string + The encryption type of the network. + """ + + enc = "" + if matching_line(cell, "Encryption key:") == "off": + enc = "Open" + else: + for line in cell: + matching = match(line,"IE:") + if matching == None: + continue + + wpa = match(matching,"WPA") + if wpa == None: + continue + + version_matches = VERSION_RGX.search(wpa) + if len(version_matches.regs) == 1: + version = version_matches \ + .group(0) \ + .lower() \ + .replace("version", "") \ + .strip() + wpa = wpa.replace(version_matches.group(0), "").strip() + if wpa == "": + wpa = "WPA" + if emit_version: + enc = "{0} v.{1}".format(wpa, version) + else: + enc = wpa + if wpa == "WPA2": + return enc + else: + enc = wpa + if enc == "": + enc = "WEP" + return enc + +def get_mode(cell): + """ Gets the mode of a network / cell. + @param string cell + A network / cell from iwlist scan. + + @return string + The IEEE 802.11 mode of the network. + """ + + mode = matching_line(cell, "Extra:ieee_mode=") + if mode is None: + return "" + return mode + +def get_address(cell): + """ Gets the address of a network / cell. + @param string cell + A network / cell from iwlist scan. + + @return string + The address of the network. + """ + + return matching_line(cell, "Address: ") + +def get_bit_rates(cell): + """ Gets the bit rate of a network / cell. + @param string cell + A network / cell from iwlist scan. + + @return string + The bit rate of the network. + """ + + return matching_line(cell, "Bit Rates:") + +# Here you can choose the way of sorting the table. sortby should be a key of +# the dictionary rules. + +def sort_cells(cells): + sortby = "Quality" + reverse = True + cells.sort(key=lambda el:el[sortby], reverse=reverse) + + +# Below here goes the boring stuff. You shouldn't have to edit anything below +# this point + +def matching_line(lines, keyword): + """ Returns the first matching line in a list of lines. + @see match() + """ + for line in lines: + matching = match(line,keyword) + if matching != None: + return matching + return None + +def match(line, keyword): + """ If the first part of line (modulo blanks) matches keyword, + returns the end of that line. Otherwise checks if keyword is + anywhere in the line and returns that section, else returns None""" + + line = line.lstrip() + length = len(keyword) + if line[:length] == keyword: + return line[length:] + else: + if keyword in line: + return line[line.index(keyword):] + else: + return None + +def parse_cell(cell, rules): + """ Applies the rules to the bunch of text describing a cell. + @param string cell + A network / cell from iwlist scan. + @param dictionary rules + A dictionary of parse rules. + + @return dictionary + parsed networks. """ + + parsed_cell = {} + for key in rules: + rule = rules[key] + parsed_cell.update({key: rule(cell)}) + return parsed_cell + +def print_table(table): + # Functional black magic. + widths = list(map(max, map(lambda l: map(len, l), zip(*table)))) + + justified_table = [] + for line in table: + justified_line = [] + for i, el in enumerate(line): + justified_line.append(el.ljust(widths[i] + 2)) + justified_table.append(justified_line) + + for line in justified_table: + print("\t".join(line)) + +def print_cells(cells, columns): + table = [columns] + for cell in cells: + cell_properties = [] + for column in columns: + if column == 'Quality': + # make print nicer + cell[column] = cell[column].rjust(3) + " %" + cell_properties.append(cell[column]) + table.append(cell_properties) + print_table(table) + +def get_parsed_cells(iw_data, rules=None): + """ Parses iwlist output into a list of networks. + @param list iw_data + Output from iwlist scan. + A list of strings. + + @return list + properties: Name, Address, Quality, Channel, Frequency, Encryption, Signal Level, Noise Level, Bit Rates, Mode. + """ + + # Here's a dictionary of rules that will be applied to the description + # of each cell. The key will be the name of the column in the table. + # The value is a function defined above. + rules = rules or { + "Name": get_name, + "Quality": get_quality, + "Channel": get_channel, + "Frequency": get_frequency, + "Encryption": get_encryption, + "Address": get_address, + "Signal Level": get_signal_level, + "Noise Level": get_noise_level, + "Bit Rates": get_bit_rates, + "Mode": get_mode, + } + + cells = [[]] + parsed_cells = [] + + for line in iw_data: + cell_line = match(line, "Cell ") + if cell_line != None: + cells.append([]) + line = cell_line[-27:] + cells[-1].append(line.rstrip()) + + cells = cells[1:] + + for cell in cells: + parsed_cells.append(parse_cell(cell, rules)) + + sort_cells(parsed_cells) + return parsed_cells + +def call_iwlist(interface='wlan0'): + """ Get iwlist output via subprocess + @param string interface + interface to scan + default is wlan0 + + @return string + properties: iwlist output + """ + return subprocess.check_output(['iwlist', interface, 'scanning']) + +def get_interfaces(interface="wlan0"): + """ Get parsed iwlist output + @param string interface + interface to scan + default is wlan0 + + @param list columns + default data attributes to return + + @return dict + properties: dictionary of iwlist attributes + """ + return get_parsed_cells(call_iwlist(interface).split('\n')) + diff --git a/src/py/iw_parse/license.txt b/src/py/iw_parse/license.txt new file mode 100644 index 0000000..2784568 --- /dev/null +++ b/src/py/iw_parse/license.txt @@ -0,0 +1,10 @@ +Copyright (c) 2013, Cuzzo Yahn +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +