├── .github └── workflows │ └── pythonpackage.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── aztarna ├── __init__.py ├── __main__.py ├── cmd.py ├── commons.py ├── helpers.py ├── industrialrouters │ ├── __init__.py │ └── scanner.py └── ros │ ├── __init__.py │ ├── commons.py │ ├── helpers.py │ ├── industrial │ ├── __init__.py │ └── scanner.py │ ├── ros │ ├── __init__.py │ ├── helpers.py │ └── scanner.py │ ├── ros2 │ ├── __init__.py │ ├── helpers.py │ ├── helpers2.py │ └── scanner.py │ └── sros │ ├── __init__.py │ ├── helpers.py │ └── scanner.py ├── docs ├── Makefile ├── _build │ ├── doctrees │ │ ├── aztarna.doctree │ │ ├── aztarna.ros.doctree │ │ ├── aztarna.sros.doctree │ │ ├── environment.pickle │ │ ├── index.doctree │ │ ├── install.doctree │ │ ├── modules.doctree │ │ └── usage.doctree │ └── html │ │ ├── .buildinfo │ │ ├── .doctrees │ │ ├── aztarna.doctree │ │ ├── aztarna.industrialrouters.doctree │ │ ├── aztarna.ros.doctree │ │ ├── aztarna.sros.doctree │ │ ├── environment.pickle │ │ ├── index.doctree │ │ ├── install.doctree │ │ ├── modules.doctree │ │ └── usage.doctree │ │ ├── _modules │ │ ├── aztarna │ │ │ ├── cmd.html │ │ │ ├── commons.html │ │ │ ├── helpers.html │ │ │ ├── industrialrouters │ │ │ │ └── scanner.html │ │ │ └── ros │ │ │ │ ├── ros │ │ │ │ ├── helpers.html │ │ │ │ └── scanner.html │ │ │ │ └── sros │ │ │ │ ├── helpers.html │ │ │ │ └── scanner.html │ │ └── index.html │ │ ├── _sources │ │ ├── aztarna.industrialrouters.rst.txt │ │ ├── aztarna.ros.rst.txt │ │ ├── aztarna.rst.txt │ │ ├── aztarna.sros.rst.txt │ │ ├── index.rst.txt │ │ ├── install.rst.txt │ │ ├── modules.rst.txt │ │ └── usage.rst.txt │ │ ├── _static │ │ ├── ajax-loader.gif │ │ ├── basic.css │ │ ├── comment-bright.png │ │ ├── comment-close.png │ │ ├── comment.png │ │ ├── css │ │ │ ├── badge_only.css │ │ │ └── theme.css │ │ ├── doctools.js │ │ ├── documentation_options.js │ │ ├── down-pressed.png │ │ ├── down.png │ │ ├── file.png │ │ ├── fonts │ │ │ ├── Inconsolata-Bold.ttf │ │ │ ├── Inconsolata-Regular.ttf │ │ │ ├── Inconsolata.ttf │ │ │ ├── Lato-Bold.ttf │ │ │ ├── Lato-Regular.ttf │ │ │ ├── Lato │ │ │ │ ├── lato-bold.eot │ │ │ │ ├── lato-bold.ttf │ │ │ │ ├── lato-bold.woff │ │ │ │ ├── lato-bold.woff2 │ │ │ │ ├── lato-bolditalic.eot │ │ │ │ ├── lato-bolditalic.ttf │ │ │ │ ├── lato-bolditalic.woff │ │ │ │ ├── lato-bolditalic.woff2 │ │ │ │ ├── lato-italic.eot │ │ │ │ ├── lato-italic.ttf │ │ │ │ ├── lato-italic.woff │ │ │ │ ├── lato-italic.woff2 │ │ │ │ ├── lato-regular.eot │ │ │ │ ├── lato-regular.ttf │ │ │ │ ├── lato-regular.woff │ │ │ │ └── lato-regular.woff2 │ │ │ ├── RobotoSlab-Bold.ttf │ │ │ ├── RobotoSlab-Regular.ttf │ │ │ ├── RobotoSlab │ │ │ │ ├── roboto-slab-v7-bold.eot │ │ │ │ ├── roboto-slab-v7-bold.ttf │ │ │ │ ├── roboto-slab-v7-bold.woff │ │ │ │ ├── roboto-slab-v7-bold.woff2 │ │ │ │ ├── roboto-slab-v7-regular.eot │ │ │ │ ├── roboto-slab-v7-regular.ttf │ │ │ │ ├── roboto-slab-v7-regular.woff │ │ │ │ └── roboto-slab-v7-regular.woff2 │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.svg │ │ │ ├── fontawesome-webfont.ttf │ │ │ ├── fontawesome-webfont.woff │ │ │ └── fontawesome-webfont.woff2 │ │ ├── jquery-3.2.1.js │ │ ├── jquery.js │ │ ├── js │ │ │ ├── modernizr.min.js │ │ │ └── theme.js │ │ ├── logo.png │ │ ├── minus.png │ │ ├── plus.png │ │ ├── pygments.css │ │ ├── searchtools.js │ │ ├── underscore-1.3.1.js │ │ ├── underscore.js │ │ ├── up-pressed.png │ │ ├── up.png │ │ └── websupport.js │ │ ├── aztarna.html │ │ ├── aztarna.industrialrouters.html │ │ ├── aztarna.ros.html │ │ ├── aztarna.sros.html │ │ ├── genindex.html │ │ ├── index.html │ │ ├── install.html │ │ ├── modules.html │ │ ├── objects.inv │ │ ├── py-modindex.html │ │ ├── search.html │ │ ├── searchindex.js │ │ └── usage.html ├── _static │ └── logo.png ├── aztarna.industrialrouters.rst ├── aztarna.ros.rst ├── aztarna.ros2.rst ├── aztarna.rst ├── aztarna.sros.rst ├── conf.py ├── index.rst ├── install.rst ├── modules.rst └── usage.rst ├── readthedocs.yml ├── requirements.txt ├── ros2_entrypoint.sh └── setup.py /.github/workflows/pythonpackage.yml: -------------------------------------------------------------------------------- 1 | name: Python package 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | strategy: 10 | max-parallel: 4 11 | matrix: 12 | python-version: [3.5, 3.6] 13 | 14 | steps: 15 | - uses: actions/checkout@v1 16 | - name: Set up Python ${{ matrix.python-version }} 17 | uses: actions/setup-python@v1 18 | with: 19 | python-version: ${{ matrix.python-version }} 20 | - name: Install dependencies 21 | run: | 22 | python -m pip install --upgrade pip 23 | pip3 install -r requirements.txt 24 | - name: Lint with flake8 25 | run: | 26 | pip install flake8 27 | # stop the build if there are Python syntax errors or undefined names 28 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 29 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 30 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 31 | - name: Test with pytest 32 | run: | 33 | pip install pytest 34 | pytest 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/python,pycharm 3 | 4 | ### PyCharm ### 5 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 6 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 7 | 8 | # User-specific stuff 9 | .idea/**/workspace.xml 10 | .idea/**/tasks.xml 11 | .idea/**/usage.statistics.xml 12 | .idea/**/dictionaries 13 | .idea/**/shelf 14 | 15 | # Sensitive or high-churn files 16 | .idea/**/dataSources/ 17 | .idea/**/dataSources.ids 18 | .idea/**/dataSources.local.xml 19 | .idea/**/sqlDataSources.xml 20 | .idea/**/dynamic.xml 21 | .idea/**/uiDesigner.xml 22 | .idea/**/dbnavigator.xml 23 | 24 | # Gradle 25 | .idea/**/gradle.xml 26 | .idea/**/libraries 27 | 28 | # Gradle and Maven with auto-import 29 | # When using Gradle or Maven with auto-import, you should exclude module files, 30 | # since they will be recreated, and may cause churn. Uncomment if using 31 | # auto-import. 32 | # .idea/modules.xml 33 | # .idea/*.iml 34 | # .idea/modules 35 | 36 | # CMake 37 | cmake-build-*/ 38 | 39 | # Mongo Explorer plugin 40 | .idea/**/mongoSettings.xml 41 | 42 | # File-based project format 43 | *.iws 44 | 45 | # IntelliJ 46 | out/ 47 | 48 | # mpeltonen/sbt-idea plugin 49 | .idea_modules/ 50 | 51 | # JIRA plugin 52 | atlassian-ide-plugin.xml 53 | 54 | # Cursive Clojure plugin 55 | .idea/replstate.xml 56 | 57 | # Crashlytics plugin (for Android Studio and IntelliJ) 58 | com_crashlytics_export_strings.xml 59 | crashlytics.properties 60 | crashlytics-build.properties 61 | fabric.properties 62 | 63 | # Editor-based Rest Client 64 | .idea/httpRequests 65 | 66 | ### PyCharm Patch ### 67 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 68 | 69 | # *.iml 70 | # modules.xml 71 | # .idea/misc.xml 72 | # *.ipr 73 | 74 | # Sonarlint plugin 75 | .idea/sonarlint 76 | 77 | ### Python ### 78 | # Byte-compiled / optimized / DLL files 79 | __pycache__/ 80 | *.py[cod] 81 | *$py.class 82 | 83 | # C extensions 84 | *.so 85 | 86 | # Distribution / packaging 87 | .Python 88 | build/ 89 | develop-eggs/ 90 | dist/ 91 | downloads/ 92 | eggs/ 93 | .eggs/ 94 | lib/ 95 | lib64/ 96 | parts/ 97 | sdist/ 98 | var/ 99 | wheels/ 100 | *.egg-info/ 101 | .installed.cfg 102 | *.egg 103 | MANIFEST 104 | 105 | # PyInstaller 106 | # Usually these files are written by a python script from a template 107 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 108 | *.manifest 109 | *.spec 110 | 111 | # Installer logs 112 | pip-log.txt 113 | pip-delete-this-directory.txt 114 | 115 | # Unit test / coverage reports 116 | htmlcov/ 117 | .tox/ 118 | .coverage 119 | .coverage.* 120 | .cache 121 | nosetests.xml 122 | coverage.xml 123 | *.cover 124 | .hypothesis/ 125 | .pytest_cache/ 126 | 127 | # Translations 128 | *.mo 129 | *.pot 130 | 131 | # Django stuff: 132 | *.log 133 | local_settings.py 134 | db.sqlite3 135 | 136 | # Flask stuff: 137 | instance/ 138 | .webassets-cache 139 | 140 | # Scrapy stuff: 141 | .scrapy 142 | 143 | # Sphinx documentation 144 | docs/_build/ 145 | 146 | # PyBuilder 147 | target/ 148 | 149 | # Jupyter Notebook 150 | .ipynb_checkpoints 151 | 152 | # pyenv 153 | .python-version 154 | 155 | # celery beat schedule file 156 | celerybeat-schedule 157 | 158 | # SageMath parsed files 159 | *.sage.py 160 | 161 | # Environments 162 | .env 163 | .venv 164 | env/ 165 | venv/ 166 | ENV/ 167 | env.bak/ 168 | venv.bak/ 169 | 170 | # Spyder project settings 171 | .spyderproject 172 | .spyproject 173 | 174 | # Rope project settings 175 | .ropeproject 176 | 177 | # mkdocs documentation 178 | /site 179 | 180 | # mypy 181 | .mypy_cache/ 182 | 183 | ### Python Patch ### 184 | .venv/ 185 | 186 | ### Python.VirtualEnv Stack ### 187 | # Virtualenv 188 | # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ 189 | [Bb]in 190 | [Ii]nclude 191 | [Ll]ib 192 | [Ll]ib64 193 | [Ll]ocal 194 | [Ss]cripts 195 | pyvenv.cfg 196 | pip-selfcheck.json 197 | 198 | ### macOS ### 199 | # General 200 | .DS_Store 201 | .AppleDouble 202 | .LSOverride 203 | 204 | # Icon must end with two \r 205 | Icon 206 | 207 | # Thumbnails 208 | ._* 209 | 210 | # Files that might appear in the root of a volume 211 | .DocumentRevisions-V100 212 | .fseventsd 213 | .Spotlight-V100 214 | .TemporaryItems 215 | .Trashes 216 | .VolumeIcon.icns 217 | .com.apple.timemachine.donotpresent 218 | 219 | # Directories potentially created on remote AFP share 220 | .AppleDB 221 | .AppleDesktop 222 | Network Trash Folder 223 | Temporary Items 224 | .apdisk 225 | 226 | # End of https://www.gitignore.io/api/python,pycharm 227 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | #### 2 | # A Docker container for running aztarna, 3 | # a tool for robot footprinting 4 | # 5 | # To build, beware of caching and: 6 | # 7 | # * If you wish to build current master 8 | # 9 | # docker build -t aztarna_docker . 10 | # 11 | # * If you wish to build a specific commit, use the AZTARNA_COMMIT build argument. 12 | # 13 | # docker build -t aztarna_docker --build-arg AZTARNA_COMMIT= . 14 | # 15 | # To run: 16 | # 17 | # docker run -it --rm --net=host aztarna_docker 18 | #### 19 | 20 | FROM ros:dashing 21 | # ARG AZTARNA_COMMIT=master 22 | # ENV AZTARNA_COMMIT ${AZTARNA_COMMIT} 23 | 24 | RUN \ 25 | echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections \ 26 | && apt-get -qq update && apt-get -qqy upgrade \ 27 | && apt-get -qqy install \ 28 | libgmp3-dev gengetopt \ 29 | libpcap-dev flex byacc \ 30 | libjson-c-dev unzip \ 31 | libunistring-dev wget \ 32 | libxml2-dev libxslt1-dev \ 33 | libffi-dev libssl-dev \ 34 | tshark \ 35 | && rm -rf /var/lib/apt/lists/* 36 | 37 | # copy the aztarna files the FS and install it 38 | COPY . /root/aztarna 39 | RUN cd /root/aztarna && python3 setup.py install 40 | 41 | 42 | ENTRYPOINT ["/root/aztarna/ros2_entrypoint.sh"] 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > This repository has been archived and is not maintained any further. Refer to [alurity](https://aliasrobotics.com/alurity.php) for future progress on robot footprinting and fingerprinting. 2 | 3 | # aztarna 4 | 5 | 6 | This repository contains Alias Robotics' aztarna, a footprinting tool for robots. 7 | 8 | **Alias Robotics supports original robot manufacturers assessing their security and improving their quality of software. By no means we encourage or promote the unauthorized tampering with running robotic systems. This can cause serious human harm and material damages.** 9 | 10 | [![PyPI version](https://badge.fury.io/py/aztarna.svg)](https://badge.fury.io/py/aztarna) [![Documentation Status](https://readthedocs.org/projects/aztarna/badge/?version=latest)](https://aztarna.readthedocs.io/en/latest/?badge=latest) [![Article](https://img.shields.io/badge/article-arxiv%3A1812.09490-red.svg)](https://arxiv.org/pdf/1812.09490.pdf) 11 | 12 | 13 | 14 | ### For ROS 15 | * A list of the ROS nodes present in the system (Publishers and Subscribers) 16 | * For each node, the published and subscribed topis including the topic type 17 | * For each node, the ROS services each of the nodes offer 18 | * A list of all ROS parameters present in the Parameter Server 19 | * A list of the active communications running in the system. A single communication includes the involved publiser/subscriber nodes and the topics 20 | 21 | ### For SROS 22 | * Determining if the system is a SROS master. 23 | * Detecting if demo configuration is in use. 24 | * A list of the nodes found in the system. (Extended mode) 25 | * A list of allow/deny policies for each node. 26 | * Publishable topics. 27 | * Subscriptable topics. 28 | * Executable services. 29 | * Readable parameters. 30 | 31 | ### For ROS2 **(Funded under the [ROSIN project](http://rosin-project.eu/))** 32 | * A list of ROS2 nodes present in each communication domains. 33 | * A list of discovered topics on each communication domain. 34 | * A list of discovered services on each communication domain. 35 | * For each node, the relationship of published and subscribed topics. 36 | * For each node, the services provided by that node. 37 | 38 | ### For Industrial routers 39 | * Detecting eWON, Moxa, Sierra Wireless and Westermo industrial routers. 40 | * Default credential checking for found routers. 41 | 42 | ### For ROS Industrial packages **(Funded under the [ROSIN project](http://rosin-project.eu/))** 43 | * Detection of ROS Industrial Hosts. 44 | * Manufacturers: 45 | * ABB 46 | * Fanuc 47 | * Kuka 48 | 49 | 50 | ## Installing 51 | ### For production 52 | Direcly from PyPi 53 | ``` 54 | pip3 install aztarna 55 | ``` 56 | or from the repository: 57 | ``` 58 | pip3 install . 59 | ``` 60 | 61 | ### For development 62 | ``` 63 | pip3 install -e . 64 | ``` 65 | or 66 | ``` 67 | python3 setup.py develop 68 | ``` 69 | **Python 3.6** and the [setuptools](https://pypi.org/project/setuptools/) package is required for installation. 70 | Python 3.7 is recommended. 71 | 72 | 73 | ### ROS2 Module 74 | 75 | For usage of the ROS2 footprinting module a ROS2 installation is required. Source the setup.bash script prior to launch. 76 | 77 | ### Install with docker 78 | ```bash 79 | docker build -t aztarna_docker . 80 | ``` 81 | 82 | ### Code usage: 83 | 84 | ```bash 85 | usage: aztarna [-h] -t TYPE [-a ADDRESS] [-p PORTS] [-i INPUT_FILE] 86 | [-o OUT_FILE] [-e] [-r RATE] [-d DOMAIN] [--daemon] [--hidden] 87 | [--shodan] [--api-key API_KEY] [--passive PASSIVE] 88 | 89 | Aztarna 90 | 91 | optional arguments: 92 | -h, --help show this help message and exit 93 | -t TYPE, --type TYPE Scan 94 | ROS, SROS, ROS2 hosts or Industrial routers 95 | -a ADDRESS, --address ADDRESS 96 | Single address or network range to scan. 97 | -p PORTS, --ports PORTS 98 | Ports to scan (format: 13311 or 11111-11155 or 99 | 1,2,3,4) 100 | -i INPUT_FILE, --input_file INPUT_FILE 101 | Input file of addresses to use for scanning 102 | -o OUT_FILE, --out_file OUT_FILE 103 | Output file for the results 104 | -e, --extended Extended scan of the hosts 105 | -r RATE, --rate RATE Maximum simultaneous network connections 106 | -d DOMAIN, --domain DOMAIN 107 | ROS 2 DOMAIN ID (ROS_DOMAIN_ID environmental 108 | variable). Only applies to ROS 2. 109 | --daemon Use rclpy daemon (coming from ros2cli). 110 | --hidden Show hidden ROS 2 nodes. By default filtering 111 | _ros2cli* 112 | --shodan Use shodan for the scan types that support it. 113 | --api-key API_KEY Shodan API Key 114 | --passive PASSIVE Passive search for ROS2 115 | ``` 116 | 117 | ### Run the code (example input file): 118 | 119 | ```bash 120 | aztarna -t ROS -p 11311 -i ros_scan_s20.csv 121 | ``` 122 | 123 | ### Run the code with Docker (example input file): 124 | ```bash 125 | docker run -v :/root -it aztarna_docker -t ROS -p 11311 -i 126 | ``` 127 | 128 | ### Run the code (example single ip address): 129 | 130 | ```bash 131 | aztarna -t ROS -p 11311 -a 115.129.241.241 132 | ``` 133 | 134 | ### Run the code (example subnet): 135 | 136 | ```bash 137 | aztarna -t ROS -p 11311 -a 115.129.241.0/24 138 | ``` 139 | 140 | ### Run the code (example single ip address, port range): 141 | 142 | ```bash 143 | aztarna -t ROS -p 11311-11500 -a 115.129.241.241 144 | ``` 145 | 146 | ### Run the code (example single ip address, port list): 147 | 148 | ```bash 149 | aztarna -t ROS -p 11311,11312,11313 -a 115.129.241.241 150 | ``` 151 | 152 | ### Run the code with ROS 2 (example exploring all ranges, 0-231) 153 | 154 | ```bash 155 | aztarna -t ROS2 156 | ``` 157 | 158 | ### Run the code with ROS 2 with `ROS_DOMAIN_ID=15` 159 | 160 | ```bash 161 | aztarna -t ROS2 -d 15 162 | ``` 163 | 164 | ### Run the code with ROS 2 using rclpy ros2cli daemon and with `ROS_DOMAIN_ID=0` while showing hidden nodes 165 | 166 | ```bash 167 | aztarna -t ros2 -d 0 --daemon --hidden 168 | ``` 169 | 170 | ### Run de code with ROS 2 using passive mode to search the hosts. if you set 'any' as argument, is going to search on all interfaces in your system: 171 | 172 | ```bash 173 | aztarna -t ros2 --passive any 174 | ``` 175 | 176 | ### Run the code (example piping directly from zmap): 177 | 178 | ```bash 179 | zmap -p 11311 0.0.0.0/0 -q | aztarna -t SROS -p 11311 180 | ``` 181 | 182 | ### Run the code (example search for industrial routers in shodan) 183 | ```bash 184 | aztarna -t IROUTERS --shodan --api-key 185 | ``` 186 | 187 | ### Run the code (example search for industrial routers in shodan, piping to file) 188 | ```bash 189 | aztarna -t IROUTERS --shodan --api-key -o routers.csv 190 | ``` 191 | ## Cite our work 192 | If you're using our work for your research, please cite us as: 193 | ``` 194 | @ARTICLE{2018arXiv181209490V, 195 | author = {{Vilches}, V{\'\i}ctor Mayoral and {Mendia}, Gorka Olalde and 196 | {Baskaran}, Xabier Perez and {Cordero}, Alejandro Hern{\'a}ndez 197 | and {Juan}, Lander Usategui San and {Gil-Uriarte}, Endika and 198 | {de Urabain}, Odei Olalde Saez and {Kirschgens}, Laura Alzola}, 199 | title = "{Aztarna, a footprinting tool for robots}", 200 | journal = {arXiv e-prints}, 201 | keywords = {Computer Science - Cryptography and Security, Computer Science - Robotics}, 202 | year = 2018, 203 | month = Dec, 204 | eid = {arXiv:1812.09490}, 205 | pages = {arXiv:1812.09490}, 206 | archivePrefix = {arXiv}, 207 | eprint = {1812.09490}, 208 | primaryClass = {cs.CR}, 209 | adsurl = {https://ui.adsabs.harvard.edu/\#abs/2018arXiv181209490V}, 210 | adsnote = {Provided by the SAO/NASA Astrophysics Data System} 211 | } 212 | ``` 213 | 214 | *** 215 | 219 | 220 | 221 | rosin_logo 223 |
224 | 225 | Supported by ROSIN - ROS-Industrial Quality-Assured Robot Software Components. 226 | More information: rosin-project.eu 227 | 228 | eu_flag 230 | 231 | This repository was partly funded by ROSIN RedROS2-I FTP which received funding from the European Union’s Horizon 2020 232 | research and innovation programme under the project ROSIN with the grant agreement No 732287. 233 | 234 | -------------------------------------------------------------------------------- /aztarna/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | -------------------------------------------------------------------------------- /aztarna/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from aztarna.cmd import main 5 | 6 | if __name__ == '__main__': 7 | main() -------------------------------------------------------------------------------- /aztarna/cmd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import asyncio 4 | import logging 5 | import re 6 | from argparse import ArgumentParser 7 | import argcomplete 8 | import uvloop 9 | 10 | from aztarna.ros.industrial.scanner import ROSIndustrialScanner 11 | from aztarna.industrialrouters.scanner import IndustrialRouterAdapter 12 | 13 | # asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) 14 | 15 | logging.getLogger(__name__).setLevel(logging.DEBUG) 16 | 17 | def main(): 18 | """ 19 | Main method 20 | """ 21 | logging.basicConfig(level=logging.INFO, format="%(name)s - %(message)s") 22 | logger = logging.getLogger(__name__) 23 | parser = ArgumentParser(description='Aztarna, a reconnaissance tool for robots and robot components.') 24 | parser.add_argument('-t', '--type', help=' Scan ROS, SROS, ROS2 hosts or Industrial routers', required=True) 25 | parser.add_argument('-a', '--address', help='Single address or network range to scan.') 26 | parser.add_argument('-p', '--ports', help='Ports to scan (format: 13311 or 11111-11155 or 1,2,3,4)', default='11311') 27 | parser.add_argument('-i', '--input_file', help='Input file of addresses to use for scanning') 28 | parser.add_argument('-o', '--out_file', help='Output file for the results') 29 | parser.add_argument('-e', '--extended', help='Extended scan of the hosts', action='store_true') 30 | parser.add_argument('-r', '--rate', help='Maximum simultaneous network connections', default=100, type=int) 31 | parser.add_argument('-d', '--domain', help='ROS 2 DOMAIN ID (ROS_DOMAIN_ID environmental variable). Only applies to ROS 2.', type=int) 32 | parser.add_argument('--daemon', help='Use rclpy daemon (coming from ros2cli).', action='store_true') 33 | parser.add_argument('--hidden', help='Show hidden ROS 2 nodes. By default filtering _ros2cli*', action='store_true') 34 | parser.add_argument('--shodan', help='Use shodan for the scan types that support it.', action='store_true') 35 | parser.add_argument('--api-key', help='Shodan API Key') 36 | parser.add_argument('--verbose', help='Verbose output') 37 | parser.add_argument('--passive', help='Passive search for ROS2', action='store_true') 38 | argcomplete.autocomplete(parser) 39 | args = parser.parse_args() 40 | try: 41 | if args.type == 'ROS' or args.type == 'ros': 42 | from aztarna.ros.ros import ROSScanner 43 | scanner = ROSScanner() 44 | elif args.type == 'SROS' or args.type == 'sros': 45 | from aztarna.ros.sros import SROSScanner 46 | scanner = SROSScanner() 47 | elif args.type == 'IROUTERS' or args.type == 'irouters': 48 | scanner = IndustrialRouterAdapter() 49 | if args.shodan is True: 50 | scanner.use_shodan = True 51 | scanner.shodan_api_key = args.api_key 52 | scanner.initialize_shodan() 53 | elif args.type.upper() == 'ROSIN': 54 | scanner = ROSIndustrialScanner() 55 | elif args.type.upper() == 'ROS2': 56 | from aztarna.ros.ros2.scanner import ROS2Scanner 57 | scanner = ROS2Scanner() 58 | else: 59 | logger.critical('Invalid type selected') 60 | return 61 | if args.input_file: 62 | try: 63 | scanner.load_from_file(args.input_file) 64 | except FileNotFoundError: 65 | logger.critical('Input file not found') 66 | elif args.address: 67 | scanner.load_range(args.address) 68 | else: 69 | if args.type.upper() not in ['ROS2']: 70 | scanner.scan_pipe_main() 71 | return 72 | 73 | 74 | # TODO Implement a regex for port argument 75 | try: 76 | scanner.ports = range(int(args.ports.split('-')[0]), int(args.ports.split('-')[1])) 77 | except: 78 | try: 79 | scanner.ports = [int(port) for port in args.ports.split(',')] 80 | except: 81 | try: 82 | scanner.ports.append(int(args.ports)) 83 | except Exception as e: 84 | logger.error('[-] Error: ' + str(e)) 85 | 86 | scanner.extended = args.extended 87 | scanner.rate = args.rate 88 | scanner.domain = args.domain 89 | if args.daemon is True: 90 | scanner.use_daemon = True 91 | if args.hidden is True: 92 | scanner.hidden = True 93 | if args.passive is True: 94 | scanner.passive = True 95 | scanner.scan() 96 | 97 | if args.out_file: 98 | scanner.write_to_file(args.out_file) 99 | else: 100 | if args.extended is True: 101 | scanner.print_results() 102 | except Exception as e: 103 | logger.critical('Exception occurred during execution') 104 | raise e 105 | 106 | 107 | if __name__ == '__main__': 108 | main() 109 | -------------------------------------------------------------------------------- /aztarna/commons.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import asyncio 4 | import ipaddress 5 | import logging 6 | from ipaddress import IPv4Address, ip_network 7 | 8 | logger = logging.getLogger(__name__) 9 | 10 | 11 | class RobotAdapter: 12 | """ 13 | BaseScanner class, an abstraction for different type scans 14 | """ 15 | def __init__(self, ports=[80], extended=False): 16 | self.host_list = [] 17 | self.ports = ports 18 | self.extended = extended 19 | self.input = False 20 | self._rate = 1000 21 | self.semaphore = asyncio.Semaphore(self._rate) 22 | self.domain = None 23 | self.use_daemon = False 24 | self.hidden = False 25 | self.passive = False 26 | 27 | @property 28 | def rate(self): 29 | return self._rate 30 | 31 | @rate.setter 32 | def rate(self, rate): 33 | self._rate = rate 34 | self.semaphore = asyncio.Semaphore(rate) 35 | 36 | def load_from_file(self, filename): 37 | """ 38 | Load a range of ipv4 addresses to scan and add them The :class:BaseScanner host_list attribute 39 | :param filename: name of the input file 40 | """ 41 | with open(filename, 'r') as file: 42 | for line in file.readlines(): 43 | try: 44 | address = ipaddress.ip_address(line.rstrip('\n')) 45 | self.host_list.append(address) 46 | except ValueError: 47 | logger.warning('Invalid IP address in input file') 48 | 49 | def load_range(self, net_range): 50 | """ 51 | Transform ipv4 address strings to pythons ipaddress library type objects for scanning purposes 52 | :param net_range: A range of string type IPv4 addresses 53 | """ 54 | network = ip_network(net_range) 55 | if network.netmask == IPv4Address('255.255.255.255'): 56 | self.host_list = [IPv4Address(net_range)] 57 | else: 58 | self.host_list = list(network.hosts()) 59 | 60 | @staticmethod 61 | async def stream_as_generator(loop, stream): 62 | reader = asyncio.StreamReader(loop=loop) 63 | reader_protocol = asyncio.StreamReaderProtocol(reader) 64 | await loop.connect_read_pipe(lambda: reader_protocol, stream) 65 | 66 | while True: 67 | line = await reader.readline() 68 | if not line: # EOF. 69 | break 70 | yield line 71 | 72 | def scan(self): 73 | raise NotImplementedError 74 | 75 | def scan_pipe_main(self): 76 | raise NotImplementedError 77 | 78 | def print_results(self): 79 | raise NotImplementedError 80 | 81 | def write_to_file(self, out_file): 82 | raise NotImplementedError 83 | 84 | 85 | class BaseRobotHost: 86 | """ 87 | A base class for different type of Robot hosts 88 | """ 89 | def __init__(self): 90 | self.address = '' 91 | self.port = '' 92 | -------------------------------------------------------------------------------- /aztarna/helpers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import asyncio 5 | from platform import system as system_name # Returns the system/OS name 6 | from subprocess import call as system_call # Execute a shell command 7 | 8 | class HelpersLINQ: 9 | """ 10 | A helper class for emulating .NET useful methods. 11 | """ 12 | @staticmethod 13 | def distinct(sequence): 14 | seen = set() 15 | for s in sequence: 16 | if not s in seen: 17 | seen.add(s) 18 | yield s 19 | 20 | class HelpersNetWorking: 21 | """ 22 | A helper class that checks networking related data 23 | """ 24 | @staticmethod 25 | def ping(host): 26 | """ 27 | A method that replicates the command line ping utility. 28 | 29 | :param host: Host to ping to 30 | :return: A boolean type that means if the ping reaches the destination or not 31 | """ 32 | ret = None 33 | # Ping command count option as function of OS 34 | param = '-n' if system_name().lower() == 'windows' else '-c' 35 | # Building the command. Ex: "ping -c 1 google.com" 36 | command = ['ping', param, '1', host] 37 | with open("/dev/null", "w+") as f: 38 | ret = system_call(command, stdout=f) == 0 39 | 40 | return ret 41 | 42 | class PortScanner: 43 | """ 44 | A base class that provides methods to check correct por scans. 45 | """ 46 | @staticmethod 47 | async def check_port(ip, port): 48 | """ 49 | Checks if a certain port is open 50 | :param ip: The host's IP address 51 | :param port: The host's port 52 | :return: The scanned port if open, else None 53 | """ 54 | conn = asyncio.open_connection(ip, port) 55 | try: 56 | reader, writer = await asyncio.wait_for(conn, timeout=3) 57 | writer.close() 58 | return port 59 | except: 60 | return None 61 | 62 | @staticmethod 63 | async def check_port_sem(sem, ip, port): 64 | """ 65 | Calls to :method:check_port with a Semaphore to avoid too many open connections. 66 | 67 | :param sem: 68 | :param ip: 69 | :param port: 70 | :return: 71 | """ 72 | async with sem: 73 | return await PortScanner.check_port(ip, port) 74 | 75 | @staticmethod 76 | async def scan_host(address, start_port, end_port, max_conns=400): 77 | """ 78 | 79 | :param address: IPv4 address to scan 80 | :param start_port: First port value to scan 81 | :param end_port: Last port value to scan 82 | :param max_conns: Maximum simultaneous number of connections 83 | :return: 84 | """ 85 | sem = asyncio.Semaphore(max_conns) 86 | ports = range(start_port, end_port) 87 | tasks = [asyncio.ensure_future(PortScanner.check_port_sem(sem, address, port)) for port in ports] 88 | responses = await asyncio.gather(*tasks) 89 | open_ports = list(filter(lambda x: x is not None, responses)) 90 | 91 | return open_ports 92 | -------------------------------------------------------------------------------- /aztarna/industrialrouters/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | -------------------------------------------------------------------------------- /aztarna/ros/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/aztarna/ros/__init__.py -------------------------------------------------------------------------------- /aztarna/ros/commons.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import asyncio 4 | import ipaddress 5 | import logging 6 | from ipaddress import IPv4Address, ip_network 7 | 8 | from aztarna.commons import * 9 | 10 | logger = logging.getLogger(__name__) 11 | 12 | 13 | class BaseHostROS(BaseRobotHost): 14 | """ 15 | A base class for ROS hosts 16 | """ 17 | def __init__(self): 18 | super().__init__() 19 | self.nodes = [] 20 | 21 | 22 | class BaseNodeROS: 23 | """ 24 | A base class for ROS nodes 25 | """ 26 | def __init__(self): 27 | self.name = '' 28 | self.address = '' 29 | self.port = '' 30 | 31 | 32 | class BaseTopicROS: 33 | """ 34 | A base class for ROS topics 35 | """ 36 | def __init__(self): 37 | self.name = '' 38 | self.type = '' 39 | 40 | 41 | class BaseServiceROS: 42 | """ 43 | A base class for ROS services 44 | """ 45 | def __init__(self): 46 | self.name = '' 47 | 48 | 49 | class ParameterROS: 50 | """ 51 | A class representing a ROS parameter 52 | """ 53 | def __init__(self): 54 | self.name = '' 55 | self.type = '' 56 | self.value = '' 57 | 58 | 59 | class CommunicationROS: 60 | """ 61 | A class representing a ROS communication, including the publishers, subscribers and the topics involveds 62 | """ 63 | def __init__(self, topic): 64 | self.publishers = [] # Node type 65 | self.subscribers = [] # Node type 66 | self.topic = topic # Topic() object 67 | -------------------------------------------------------------------------------- /aztarna/ros/helpers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import asyncio 5 | from aztarna.helpers import HelpersLINQ 6 | 7 | class HelpersROS: 8 | """ 9 | A helper class from ROS system state response manipulation. 10 | Process response is of type array = [ [a, [a1, a2] ] , [b, [b1, b2] ] ] 11 | """ 12 | @staticmethod 13 | def process_line(array_object): 14 | if len(array_object) == 0: 15 | return None 16 | 17 | topic_name = array_object[0] 18 | node_names = (HelpersLINQ.distinct(array_object[1])) 19 | 20 | return [topic_name, node_names] 21 | -------------------------------------------------------------------------------- /aztarna/ros/industrial/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/aztarna/ros/industrial/__init__.py -------------------------------------------------------------------------------- /aztarna/ros/industrial/scanner.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import logging 3 | import sys 4 | import traceback 5 | from functools import reduce 6 | 7 | import aiohttp 8 | from aiohttp import ClientConnectorError 9 | from aiohttp_xmlrpc.client import ServerProxy 10 | 11 | from aztarna.commons import RobotAdapter 12 | from aztarna.ros.ros.helpers import ROSHost 13 | 14 | 15 | class ROSIndustrialScanner(RobotAdapter): 16 | 17 | def __init__(self, ports=[80], extended=False): 18 | RobotAdapter.__init__(self, ports, extended) 19 | self.rosin_nodes = ['/streaming_client', # ABB 20 | '/motion_download_interface', # ABB 21 | '/robot_state', # ABB 22 | '/joint_trajectory_action', # ABB 23 | '/kuka_eki_hw_interface', # KUKA 24 | '/controller_spawner', # KUKA 25 | '/motion_streaming_interface', # FANUC 26 | '/industrial_robot_client', # FANUC 27 | '/joint_state', # FANUC 28 | '/kuka_rsi_simulator' # KUKA 29 | ] 30 | self.timeout = aiohttp.ClientTimeout(total=3) 31 | self.logger = logging.getLogger(__name__) 32 | self.hosts = [] 33 | self.rate = 1000 34 | 35 | def load_from_file(self, filename): 36 | RobotAdapter.load_from_file(self, filename) 37 | 38 | def load_range(self, net_range): 39 | RobotAdapter.load_range(self, net_range) 40 | 41 | @staticmethod 42 | def stream_as_generator(loop, stream): 43 | return RobotAdapter.stream_as_generator(loop, stream) 44 | 45 | def scan(self): 46 | asyncio.get_event_loop().run_until_complete(self.scan_network()) 47 | 48 | def scan_pipe_main(self): 49 | asyncio.get_event_loop().run_until_complete(self.scan_pipe()) 50 | 51 | def print_results(self): 52 | for host in self.hosts: 53 | self.logger.warning(f'[+] ROSIN Host Found at {host.address}:{host.port}!!!') # noqa: E999 54 | 55 | def write_to_file(self, out_file): 56 | with open(out_file, 'w') as f: 57 | f.write('Address;Port;Node\n') 58 | for host in self.host_list: 59 | for node in host.nodes: 60 | f.write(f'{host.address};{host.port};{node}\n') 61 | 62 | async def analyze_nodes(self, address, port): 63 | found_nodes = [] 64 | async with self.semaphore: 65 | full_host = f'http://{address}:{port}' 66 | self.logger.info(f'[+] Scanning host at {full_host}') 67 | try: 68 | async with aiohttp.ClientSession(loop=asyncio.get_event_loop(), timeout=self.timeout) as client: 69 | ros_master_client = ServerProxy(full_host, client=client) 70 | code, msg, val = await ros_master_client.getSystemState('') 71 | if code == 1: 72 | nodes = list(map(lambda x: x[0], map(lambda x: x[1], reduce(lambda x, y: x + y, val)))) 73 | for node in nodes: 74 | if node in self.rosin_nodes: 75 | found_nodes.append(node) 76 | if len(found_nodes) > 0: 77 | ros_host = ROSHost(address, port) 78 | ros_host.nodes = found_nodes 79 | self.hosts.append(ros_host) 80 | except ClientConnectorError: 81 | self.logger.debug(f'[-] Unable to connect to host {address}') 82 | except Exception as e: 83 | ex, msg, tb = sys.exc_info() 84 | traceback.print_tb(tb) 85 | self.logger.debug(f'[-] Connection error on host {address}') 86 | 87 | 88 | async def scan_network(self): 89 | """ 90 | Scan the provided network (from args) searching for ROS nodes. 91 | """ 92 | try: 93 | results = [] 94 | for port in self.ports: 95 | for address in self.host_list: 96 | results.append(self.analyze_nodes(address, port)) 97 | 98 | for result in await asyncio.gather(*results): 99 | pass 100 | 101 | except ValueError as e: 102 | self.logger.error('Invalid address entered') 103 | raise e 104 | 105 | async def scan_pipe(self): 106 | async for line in RobotAdapter.stream_as_generator(asyncio.get_event_loop(), sys.stdin): 107 | str_line = (line.decode()).rstrip('\n') 108 | for port in self.ports: 109 | await self.analyze_nodes(str_line, port) 110 | -------------------------------------------------------------------------------- /aztarna/ros/ros/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from .scanner import ROSScanner 5 | -------------------------------------------------------------------------------- /aztarna/ros/ros/helpers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | ROS Scanner helper module. 5 | 6 | :author Alias Robotics SL (https://aliasrobotics.com) 7 | """ 8 | from aztarna.ros.commons import BaseNodeROS, BaseNodeROS, BaseServiceROS, BaseHostROS 9 | 10 | class ROSHost(BaseHostROS): 11 | """ 12 | Class for keeping all the attributes of a ROS Node.Extends:class:`aztarna.commons.BaseHostROS` 13 | """ 14 | def __init__(self, address, port): 15 | super().__init__() 16 | self.address = address 17 | self.port = port 18 | self.communications = [] 19 | self.services = [] 20 | 21 | def __repr__(self): 22 | if len(self.nodes) == 0: 23 | return "Address: {}".format(self.address) 24 | return "Address: {}, Nodes: {}".format(self.address, self.nodes) 25 | 26 | class Node(BaseNodeROS): 27 | """ 28 | Node class, an extension of the BaseNodeROS 29 | """ 30 | def __init__(self, name): 31 | super().__init__() 32 | self.name = name 33 | self.published_topics = [] 34 | self.subscribed_topics = [] 35 | self.services = [] 36 | 37 | def __str__(self): 38 | return '{} XMLRPCUri: http://{}:{}'.format(self.name, self.address, self.port) 39 | 40 | class Topic(BaseNodeROS): 41 | """ 42 | Topic class, an extension of BaseNodeROS 43 | """ 44 | def __init__(self, name, topic_type): 45 | super().__init__() 46 | self.name = name 47 | self.type = topic_type 48 | 49 | def __str__(self): 50 | return self.name + '(Type: ' + self.type + ')' 51 | 52 | class Service(BaseServiceROS): 53 | """ 54 | Service class, an extension of BaseServiceROS 55 | """ 56 | def __init__(self, name): 57 | super().__init__() 58 | self.name = name 59 | 60 | def __str__(self): 61 | return '{}'.format(self.name) 62 | -------------------------------------------------------------------------------- /aztarna/ros/ros/scanner.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | ROS Scanner module. 5 | 6 | :author Alias Robotics SL (https://aliasrobotics.com) 7 | """ 8 | import asyncio 9 | import aiohttp 10 | import logging 11 | import re 12 | from aiohttp_xmlrpc.client import ServerProxy 13 | from aztarna.ros.commons import CommunicationROS 14 | from aztarna.commons import RobotAdapter 15 | from aztarna.ros.helpers import HelpersROS 16 | from aztarna.ros.ros.helpers import Node, Topic, Service 17 | from aztarna.ros.ros.helpers import ROSHost 18 | import sys 19 | from ipaddress import IPv4Address 20 | 21 | class ROSScanner(RobotAdapter): 22 | """ 23 | ROSScanner class, an extension of BaseScanner for ROS. 24 | """ 25 | def __init__(self): 26 | super().__init__() 27 | 28 | self.timeout = aiohttp.ClientTimeout(total=3) 29 | self.hosts = [] 30 | 31 | self.logger = logging.getLogger(__name__) 32 | 33 | async def analyze_nodes(self, address, port): 34 | """ 35 | Scan a node and gather all its data including topics, services and Communications. 36 | 37 | :param address: address of the ROS master 38 | :param port: port of the ROS master 39 | """ 40 | async with aiohttp.ClientSession(loop=asyncio.get_event_loop(), timeout=self.timeout) as client: 41 | full_host = 'http://' + str(address) + ':' + str(port) 42 | ros_master_client = ServerProxy(full_host, loop=asyncio.get_event_loop(), client=client) 43 | ros_host = ROSHost(address, port) 44 | async with self.semaphore: 45 | try: 46 | code, msg, val = await ros_master_client.getSystemState('') 47 | if code == 1: 48 | self.hosts.append(ros_host) 49 | if self.extended: 50 | publishers_array = val[0] 51 | subscribers_array = val[1] 52 | services_array = val[2] 53 | found_topics = await self.analyze_topic_types(ros_master_client) # In order to analyze the nodes topics are needed 54 | 55 | self.extract_nodes(publishers_array, found_topics, 'pub', ros_host) 56 | self.extract_nodes(subscribers_array, found_topics, 'sub', ros_host) 57 | self.extract_services(services_array, ros_host) 58 | 59 | for topic_name, topic_type in found_topics.items(): # key, value 60 | current_topic = Topic(topic_name, topic_type) 61 | comm = CommunicationROS(current_topic) 62 | for node in ros_host.nodes: 63 | if next((x for x in node.published_topics if x.name == current_topic.name), None) is not None: 64 | comm.publishers.append(node) 65 | if next((x for x in node.subscribed_topics if x.name == current_topic.name), None) is not None: 66 | comm.subscribers.append(node) 67 | ros_host.communications.append(comm) 68 | await self.set_xmlrpcuri_node(ros_master_client, ros_host) 69 | await client.close() 70 | self.logger.warning('[+] ROS Host found at {}:{}'.format(ros_host.address, ros_host.port)) 71 | else: 72 | self.logger.critical('[-] Error getting system state. Probably not a ROS Master') 73 | 74 | except Exception as e: 75 | # self.logger.error('[-] Error connecting to host ' + str(ros_host.address) + ':' + str(ros_host.port) + ' -> '+str(e) + '\n\tNot a ROS host') 76 | pass # do not log anything to ensure a clean output 77 | 78 | def extract_nodes(self, source_array, topics, pub_or_sub, host): 79 | """ 80 | From all the data ROS Master returns, extract just the node info. 81 | 82 | :param source_array: A multiple level array containing data from the the ROS system state 83 | :param topics: A list of all topics found in the ROS system 84 | :param pub_or_sub: A boolean to separate publisher and subscriber nodes 85 | :param host: Current ROS host 86 | """ 87 | source_lines = list(map(HelpersROS.process_line, list(filter(lambda x: (list(x)) is not None, source_array)))) 88 | for source_line in source_lines: 89 | for node_name in source_line[1]: # source_line[1] == nodes from a topic, is a list 90 | node = self.get_create_node(node_name, host) 91 | topic_name = source_line[0] 92 | topic_type = topics[topic_name] 93 | topic = Topic(topic_name, topic_type) 94 | if topic not in node.published_topics and pub_or_sub == 'pub': 95 | node.published_topics.append(topic) 96 | if topic not in node.subscribed_topics and pub_or_sub == 'sub': 97 | node.subscribed_topics.append(topic) 98 | 99 | @staticmethod 100 | def get_create_node(node_name, host): 101 | """ 102 | Generate new :class:`aztarna.ros.helpers.Node` objects, and if they exist just return them. 103 | 104 | :param node_name: The name of the node to create or return 105 | :param host: Current ROS host 106 | :return: The newly created node or an existing that matches :attr:`node_name` 107 | """ 108 | node_name_attrs = [o.name for o in host.nodes] 109 | if node_name not in node_name_attrs: 110 | ret_node = Node(node_name) 111 | host.nodes.append(ret_node) 112 | else: 113 | ret_node = next((x for x in host.nodes if x.name == node_name), None) 114 | 115 | return ret_node 116 | 117 | async def set_xmlrpcuri_node(self, ros_master_client, host): 118 | """ 119 | Once all node data is collected, set the xml. 120 | 121 | :param ros_master_client: xml-rpc object for the ROS Master Client 122 | """ 123 | for node in host.nodes: 124 | uri = await ros_master_client.lookupNode('', node.name) 125 | if uri[2] != '': 126 | regexp = re.compile(r'http://(?P\S+):(?P[0-9]{1,5})') 127 | uri_groups = regexp.search(uri[2]) 128 | node.address = uri_groups.group('host') 129 | node.port = uri_groups.group('port') 130 | 131 | @staticmethod 132 | async def analyze_topic_types(ros_master_client): 133 | """ 134 | Extract topic from ROS Master and disassemble them into topic name and topic type. 135 | 136 | :param ros_master_client: xml-rpc object for the ROS Master Client 137 | :return: A dictionary of topics. Key is the topic name and value the topic type 138 | """ 139 | topic_types = await ros_master_client.getTopicTypes('') 140 | topics = {} 141 | for topic_type_element in topic_types[2]: 142 | topic_name = topic_type_element[0] 143 | topic_type = topic_type_element[1] 144 | topics[topic_name] = topic_type 145 | return topics 146 | 147 | def extract_services(self, source_array, host): 148 | """ 149 | Extract the services from the ROS system state. 150 | 151 | :param source_array: A multiple level array containing data from the the ROS system state 152 | :param host: Current ROS host 153 | """ 154 | service_lines = list(map(HelpersROS.process_line, list(filter(lambda x: (list(x)) is not None, source_array)))) 155 | for service_line in service_lines: 156 | for node_name in service_line[1]: # source_line[1] == nodes from a topic, is a list 157 | node = self.get_create_node(node_name, host) 158 | node.services.append(Service(service_line[0])) 159 | 160 | async def scan_network(self): 161 | """ 162 | Scan the provided network (from args) searching for ROS nodes. 163 | """ 164 | try: 165 | results = [] 166 | for port in self.ports: 167 | for address in self.host_list: 168 | results.append(self.analyze_nodes(address, port)) 169 | 170 | for result in await asyncio.gather(*results): 171 | pass 172 | 173 | except ValueError as e: 174 | self.logger.error('Invalid address entered') 175 | raise e 176 | 177 | def scan(self): 178 | """ 179 | Call to :meth:`aztarna.ros.scanner.scan_network` asynchronously 180 | """ 181 | asyncio.get_event_loop().run_until_complete(self.scan_network()) 182 | 183 | async def scan_pipe(self): 184 | async for line in RobotAdapter.stream_as_generator(asyncio.get_event_loop(), sys.stdin): 185 | str_line = (line.decode()).rstrip('\n') 186 | for port in self.ports: 187 | await self.analyze_nodes(str_line, port) 188 | 189 | def scan_pipe_main(self): 190 | asyncio.get_event_loop().run_until_complete(self.scan_pipe()) 191 | 192 | def print_results(self): 193 | """ 194 | Print the information of a ROS system. 195 | """ 196 | for host in self.hosts: 197 | for node in host.nodes: 198 | print('\nNode: ' + str(node)) 199 | print('\n\t Published topics:') 200 | for topic in node.published_topics: 201 | print('\t\t * ' + str(topic)) 202 | print('\n\t Subscribed topics:') 203 | for topic in node.subscribed_topics: 204 | print('\t\t * ' + str(topic)) 205 | print('\n\t Services:') 206 | for service in node.services: 207 | print('\t\t * ' + str(service)) 208 | 209 | print('\nCommunications: ') 210 | for i in range(len(host.communications)): 211 | comm = host.communications[i] 212 | print('\n\t Communication ' + str(i) + ':') 213 | print('\t\t - Publishers:') 214 | for node in comm.publishers: 215 | print('\t\t\t' + str(node)) 216 | print('\t\t - Topic: ' + str(comm.topic)) 217 | print('\t\t - Subscribers:') 218 | for node in comm.subscribers: 219 | print('\t\t\t' + str(node)) 220 | print('\n\n') 221 | 222 | def write_to_file(self, out_file): 223 | """ 224 | Write the information of a ROS system into the provided file. 225 | 226 | :param out_file: The file where to write the results 227 | """ 228 | lines = [] 229 | header = 'Master Address;Master port;Node Name;Node Address;Port;Published Topics;Subscribed Topics;Services\n' 230 | lines.append(header) 231 | for host in self.hosts: 232 | line = '{};{};;;;;;\n'.format(host.address, host.port) 233 | if len(host.nodes) > 0: 234 | for node in host.nodes: 235 | for ptopic in node.published_topics: 236 | for stopic in node.subscribed_topics: 237 | for service in node.services: 238 | line = '{};{};{};{};{};{};{};{}\n'.format(host.address, host.port, node.name, node.address, node.port, ptopic, 239 | stopic, service) 240 | lines.append(line) 241 | 242 | with open(out_file, 'w') as file: 243 | file.writelines(lines) 244 | -------------------------------------------------------------------------------- /aztarna/ros/ros2/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/aztarna/ros/ros2/__init__.py -------------------------------------------------------------------------------- /aztarna/ros/ros2/helpers.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | default_topics = ['/rosout', '/parameter_events'] 4 | 5 | 6 | class ROS2Node: 7 | 8 | def __init__(self): 9 | self.name = '' 10 | self.domain_id = 0 11 | self.namespace = '' 12 | self.subscribed_topics = [] 13 | self.published_topics = [] 14 | self.services = [] 15 | 16 | 17 | class ROS2Topic: 18 | 19 | def __init__(self): 20 | self.name = '' 21 | self.topic_type = '' 22 | self.domain_id = 0 23 | 24 | 25 | class ROS2Host: 26 | 27 | def __init__(self): 28 | self.nodes = [] 29 | self.topics = [] 30 | self.domain_id = 0 31 | self.passive = '' 32 | 33 | 34 | class ROS2Service: 35 | 36 | def __init__(self): 37 | self.name = '' 38 | self.service_type = '' 39 | 40 | 41 | def raw_topics_to_pyobj_list(topics, include_default=False) -> List[ROS2Topic]: 42 | """ 43 | Utility function for converting the raw data returned by the ROS2 API into a list of python topic objects. 44 | 45 | :param topics: Raw topics list. 46 | :param include_default: If to include the default topic names on the returned list or not. 47 | :return: A list containing all parsed :class:`aztarna.ros.ros2.helpers.ROS2Topic` objects. 48 | """ 49 | topics_list = [] 50 | for topic_name, topic_type in topics: 51 | if not (not include_default and topic_name in default_topics): 52 | topic = ROS2Topic() 53 | topic.name = topic_name 54 | topic.topic_type = topic_type 55 | topics_list.append(topic) 56 | return topics_list 57 | 58 | 59 | def raw_services_to_pyobj_list(services) -> List[ROS2Service]: 60 | """ 61 | Utility function for converting the raw data returned by the ROS2 API into a list of python service objects. 62 | 63 | :param services: Raw services list. 64 | :return: A list containing all parsed :class:`aztarna.ros.ros2.helpers.ROS2Service` objects. 65 | """ 66 | services_list = [] 67 | for service_name, service_type in services: 68 | service = ROS2Service() 69 | service.name = service_name 70 | service.service_type = service_type 71 | services_list.append(service) 72 | return services_list 73 | -------------------------------------------------------------------------------- /aztarna/ros/ros2/helpers2.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | default_topics = ['/rosout', '/parameter_events'] 4 | 5 | 6 | class ROS2host: 7 | """ 8 | A class abstracting a host running ROS 2 9 | 10 | NOTE: this class isn't likely to be used since the ROS 2 api does 11 | not expose the hosts by default. 12 | """ 13 | def __init__(self): 14 | self.nodes = [] 15 | self.topics = [] 16 | self.domain_id = 0 17 | 18 | 19 | class ROS2node: 20 | """ 21 | A class abstracting a ROS 2 node 22 | """ 23 | def __init__(self): 24 | self.name = '' 25 | self.domain_id = 0 26 | self.namespace = '' 27 | self.subscribers = [] # list of nodes 28 | self.publishers = [] # list of nodes 29 | self.topics = [] 30 | self.services = [] 31 | self.action_servers = [] 32 | self.action_clients = [] 33 | 34 | def __str__(self): 35 | return_str = "" 36 | return_str += "Node:\n" 37 | return_str += "\t Name: "+str(self.name)+"\n" 38 | return_str += "\t ROS2_DOMAIN_ID: "+str(self.domain_id)+"\n" 39 | return_str += "\t Namespace: "+str(self.namespace)+"\n" 40 | return return_str 41 | 42 | 43 | class ROS2topic: 44 | """ 45 | A class abstracting a ROS 2 topic 46 | """ 47 | def __init__(self): 48 | self.name = '' 49 | self.topic_type = '' 50 | self.domain_id = 0 51 | self.subscribers = 0 52 | self.publishers = 0 53 | 54 | def __str__(self): 55 | return_str = '' 56 | return_str += 'Topic:\n' 57 | return_str += '\t Name: ' + str(self.name) + '\n' 58 | return_str += '\t ROS2_DOMAIN_ID: ' + str(self.domain_id) + '\n' 59 | return_str += '\t Number of publishers: ' + str(self.publishers) + '\n' 60 | return_str += '\t Number of subscribers: ' + str(self.publishers) + '\n' 61 | return_str += "\t Namespace: " + str(self.namespace) + "\n" 62 | return return_str 63 | 64 | 65 | class ROS2service: 66 | """ 67 | A class abstracting a ROS 2 service 68 | """ 69 | def __init__(self): 70 | self.name = '' 71 | self.domain_id = 0 72 | self.service_type = '' 73 | 74 | def __str__(self): 75 | return_str = '' 76 | return_str += 'Services:\n' 77 | return_str += '\t Name: ' + str(self.name) + '\n' 78 | return_str += '\t ROS2_DOMAIN_ID: ' + str(self.domain_id) + '\n' 79 | return_str += '\t Service type: ' + self.service_type() + '\n' 80 | return return_str 81 | 82 | 83 | class ROS2actionServer: 84 | """ 85 | A class abstracting a ROS 2 ActionServer 86 | """ 87 | def __init__(self): 88 | # TODO: fillme 89 | raise NotImplementedError 90 | 91 | 92 | class ROS2actionClient: 93 | """ 94 | A class abstracting a ROS 2 ActionClient 95 | """ 96 | def __init__(self): 97 | # TODO: fillme 98 | raise NotImplementedError 99 | -------------------------------------------------------------------------------- /aztarna/ros/sros/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from .scanner import SROSScanner 5 | -------------------------------------------------------------------------------- /aztarna/ros/sros/helpers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | SROS Scanner helpers classes module. 6 | :author Alias Robotics SL (https://aliasrobotics.com) 7 | """ 8 | 9 | import asyncio 10 | from scapy.all import * 11 | from scapy.layers.tls.extensions import TLS_Ext_SupportedGroups, TLS_Ext_SupportedPointFormat, \ 12 | TLS_Ext_SignatureAlgorithms, TLS_Ext_Heartbeat, TLS_Ext_Padding 13 | from scapy.layers.tls.handshake import TLSClientHello 14 | from scapy.layers.tls.record import TLS 15 | 16 | from aztarna.ros.commons import BaseHostROS, BaseNodeROS 17 | 18 | load_layer('tls') 19 | 20 | logger = logging.getLogger(__name__) 21 | 22 | class SROSNode(BaseNodeROS): 23 | """ 24 | Class for keeping all the attributes of a SROS Node. Extends :class:`aztarna.commons.BaseNodeROS` 25 | """ 26 | def __init__(self): 27 | super().__init__() 28 | self.is_demo = False 29 | self.policies = [] 30 | 31 | def __repr__(self): 32 | return "Name: {}, Address: {}, Port: {}, Demo: {}, Policies: {}".format(self.name, self.address, 33 | self.port, self.is_demo, self.policies) 34 | 35 | 36 | class SROSHost(BaseHostROS): 37 | """ 38 | Class for keeping all the attributes of a SROS Node.Extends:class:`aztarna.commons.BaseHostROS` 39 | """ 40 | def __init__(self): 41 | super().__init__() 42 | 43 | def __repr__(self): 44 | return "Address: {}, Nodes: {}".format(self.address, self.nodes) 45 | 46 | class SROSPolicy: 47 | """ 48 | Class for representing a SROS Policy, containing the possible types for it, the value and its permissions. 49 | """ 50 | TYPE_SUBSCRIPTABLE_TOPICS = 'Subscriptable topics' 51 | TYPE_PUBLISHABLE_TOPICS = 'Publishable topics' 52 | TYPE_EXECUTABLE_SVCS = 'Executable services' 53 | TYPE_READABLE_PARAMS = 'Readable parameters' 54 | TYPE_UNKNOWN = 'Unknown' 55 | POLICY_ALLOWED = True 56 | POLICY_DENIED = False 57 | 58 | def __init__(self): 59 | self.type = SROSPolicy.TYPE_UNKNOWN 60 | self.values = [] 61 | self.permissions = SROSPolicy.POLICY_DENIED 62 | 63 | def __repr__(self): 64 | return 'Type: {}, Values: {}, Permission: {}'.format(self.type, self.values, self.permissions) 65 | 66 | def get_node_info(cert): 67 | """ 68 | Extract all the information for a node, based on it's certificate. 69 | :param cert: The input certificate in X509 format. 70 | :return: :class:`aztarna.sros.helpers.SROSNode` The extracted node info from the certificate. 71 | """ 72 | node = SROSNode() 73 | ros_demo_fields = {'stateOrProvinceName': 'Sate', 74 | 'organizationName': 'Organization', 75 | 'countryName': 'ZZ', 76 | 'organizationUnitName': 'Organizational Unit', 77 | 'commonName': 'master', 78 | 'localityName': 'Locality' 79 | } 80 | try: 81 | node.name = cert.subject['commonName'] 82 | if cert.issuer == ros_demo_fields: 83 | node.is_demo = True 84 | except Exception: 85 | logger.warning('\t\tException when obtaining node info') 86 | return None 87 | else: 88 | return node 89 | 90 | def get_policies(cert): 91 | """ 92 | Get the related policies from an input SROS Node certificate. 93 | :param cert: Certificate in X509 format. 94 | :return: List containing all policies as instances of :class:`aztarna.sros.helpers.SROSPolicy` 95 | """ 96 | policies = [] 97 | try: 98 | extensions = cert.tbsCertificate.extensions 99 | policies_extension = list(filter(lambda ext: ext.extnID.val == '2.5.29.32', extensions))[0] 100 | for cert_policy in policies_extension.extnValue.certificatePolicies: 101 | id = cert_policy.policyIdentifier.val 102 | policy = SROSPolicy() 103 | if id[-3] == '1': 104 | policy.type = SROSPolicy.TYPE_SUBSCRIPTABLE_TOPICS 105 | elif id[-3] == '2': 106 | policy.type = SROSPolicy.TYPE_PUBLISHABLE_TOPICS 107 | elif id[-3] in ['3', '6']: 108 | policy.type = SROSPolicy.TYPE_UNKNOWN 109 | elif id[-3] == '4': 110 | policy.type = SROSPolicy.TYPE_EXECUTABLE_SVCS 111 | elif id[-3] == '5': 112 | policy.type = SROSPolicy.TYPE_READABLE_PARAMS 113 | 114 | if id[-1] == '1': 115 | policy.permission = SROSPolicy.POLICY_ALLOWED 116 | else: 117 | policy.permission = SROSPolicy.POLICY_DENIED 118 | 119 | for qualifier in cert_policy.policyQualifiers: 120 | policy.values.append(qualifier.qualifier.val) 121 | policies.append(policy) 122 | except Exception: 123 | logger.warning('\t\tException when capturing the certificate policies') 124 | return policies 125 | 126 | async def get_sros_certificate(address, port, timeout=3): 127 | """ 128 | Function to connect to a SROS Node, simulate the TLS handshake, and get it's server certificate on the process. 129 | :param address: Address of the node. 130 | :param port: Port of the node. 131 | :param timeout: Timeout for the connection. 132 | :return: A tuple containing the address, port and certificate if found, otherwise, 133 | a tuple containing address, port and None. 134 | """ 135 | client_hello = TLS(version='TLS 1.0', msg=TLSClientHello( 136 | ciphers=[49200, 49196, 49202, 49198, 49199, 49195, 49201, 49197, 165, 163, 161, 159, 164, 162, 160, 158, 49192, 137 | 49188, 49172, 49162, 49194, 49190, 49167, 49157, 107, 106, 105, 104, 57, 56, 55, 54, 49191, 49187, 138 | 49171, 49161, 49193, 49189, 49166, 49156, 103, 64, 63, 62, 51, 50, 49, 48, 136, 135, 134, 133, 69, 68, 139 | 67, 66, 49170, 49160, 49165, 49155, 22, 19, 16, 13, 157, 156, 61, 53, 60, 47, 132, 65, 10, 255], 140 | comp=[0], 141 | gmt_unix_time=12345566, 142 | ext=[TLS_Ext_SupportedGroups(groups=[23, 25, 28, 27, 24, 26, 22, 14, 13, 11, 12, 9, 10]), 143 | TLS_Ext_SupportedPointFormat(ecpl=[0, 1, 2]), 144 | TLS_Ext_SignatureAlgorithms( 145 | sig_algs=[1537, 1538, 1539, 1281, 1282, 1283, 1025, 1026, 1027, 769, 770, 771, 513, 514, 515]), 146 | TLS_Ext_Heartbeat(heartbeat_mode=1), 147 | TLS_Ext_Padding(padding=212 * b'\x00')])) 148 | 149 | tls_header = b'\x16\x03\x03' 150 | server_hello_done = b'\x0e\x00\x00\x00' 151 | received_data = b'' 152 | is_sros = True 153 | writer = None 154 | try: 155 | conn = asyncio.open_connection(str(address), port, loop=asyncio.get_event_loop()) 156 | reader, writer = await asyncio.wait_for(conn, timeout=timeout) 157 | writer.write(bytes(client_hello)) 158 | await writer.drain() 159 | while received_data[-4:] != server_hello_done: 160 | received_data += await asyncio.wait_for(reader.read(1024), timeout=3) 161 | if received_data[:3] != tls_header: 162 | is_sros = False 163 | break 164 | except Exception as e: 165 | logger.error('[-] Error connecting to host ' + str(address) + ': ' + str(e) + '\n\tNot a SROS host') 166 | return address, port, None 167 | else: 168 | if is_sros: 169 | server_hello = TLS(received_data) 170 | cert = server_hello.payload.msg[0].certs[0][1] 171 | logger.warning('[+] SROS host found!!!') 172 | return address, port, cert 173 | finally: 174 | if writer: 175 | writer.close() 176 | if sys.version_info >= (3, 7, 0): 177 | await writer.wait_closed() 178 | return address, port, None 179 | 180 | async def check_port(ip, port): 181 | """ 182 | Check if a port is open. 183 | :param ip: Address of the host 184 | :param port: Port to check 185 | :return: Returns the port if it is open. None otherwise. 186 | """ 187 | try: 188 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 189 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 190 | sock.settimeout(0.1) 191 | await asyncio.wait_for(asyncio.get_event_loop().sock_connect(sock, (str(ip), port,)), timeout=0.1) 192 | logger.warning('Scanning host {}:{}'.format(ip, port)) 193 | return port 194 | except Exception as e: 195 | pass 196 | finally: 197 | try: 198 | sock.close() 199 | except: 200 | pass 201 | 202 | async def check_port_sem(sem, ip, port): 203 | """ 204 | Check ports from a host, limiting concurrency with a semaphore. 205 | 206 | :param sem: Asyncio semaphore. 207 | :param ip: Address of the host. 208 | :param port: Port to be checked. 209 | :return: The result of :meth:`aztarna.sros.helpers.check_port`. 210 | """ 211 | async with sem: 212 | return await check_port(ip, port) 213 | 214 | async def find_node_ports(address, ports): 215 | """ 216 | Find all the open ports for a host. 217 | 218 | :param address: IP address of the host. 219 | :param ports: Port to check. 220 | :return: A list of the found open ports. 221 | """ 222 | sem = asyncio.Semaphore(400) # Change this value for concurrency limitation 223 | tasks = [asyncio.ensure_future(check_port_sem(sem, address, p)) for p in ports] 224 | found_ports = [] 225 | for response in await asyncio.gather(*tasks): 226 | if response: 227 | found_ports.append(response) 228 | 229 | return found_ports 230 | -------------------------------------------------------------------------------- /aztarna/ros/sros/scanner.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | SROS Scanner module. 5 | 6 | :author Alias Robotics SL (https://aliasrobotics.com) 7 | """ 8 | 9 | import asyncio 10 | import logging 11 | import random 12 | import sys 13 | import traceback 14 | from ipaddress import AddressValueError 15 | 16 | from aztarna.commons import RobotAdapter 17 | from .helpers import SROSHost, get_node_info, get_policies, get_sros_certificate, find_node_ports 18 | 19 | logger = logging.getLogger(__name__) 20 | 21 | class SROSScanner(RobotAdapter): 22 | """ 23 | SROS Scanner class, extending :class:`aztarna.commons.BaseScanner`. 24 | """ 25 | 26 | def __init__(self): 27 | super().__init__() 28 | self.hosts = [] 29 | self.addresses = [] 30 | 31 | async def scan_host(self, address: str, master_port: int, timeout=1): 32 | """ 33 | Scan a single SROS host and return a :class:`aztarna.sros.helpers.SROSHost` instance with all the data if found. 34 | 35 | :param address: Host IP address. 36 | :param master_port: Master node port. 37 | :param timeout: Timeout for the connection. 38 | :return: :class:`aztarna.sros.helpers.SROSHost` instance. 39 | """ 40 | async with self.semaphore: 41 | sros_host = None 42 | logger.warning('Connecting to {}:{}'.format(address, master_port)) 43 | try: 44 | master_address, port, master_cert = await get_sros_certificate(address, master_port, timeout) 45 | if master_cert: 46 | if master_cert.subject['commonName'] == 'master': 47 | sros_host = SROSHost() 48 | sros_host.address = address 49 | sros_host.port = master_port 50 | master_node = get_node_info(master_cert) 51 | master_node.port = master_port 52 | master_node.policies = get_policies(master_cert) 53 | sros_host.nodes.append(master_node) 54 | results = [] 55 | if self.extended: 56 | port_range = list(range(1024, 49151)) 57 | random.shuffle(port_range) 58 | node_ports = await find_node_ports(address, port_range) 59 | for port in node_ports: 60 | results.append(get_sros_certificate(address, port)) 61 | for result in await asyncio.gather(*results): 62 | try: 63 | if result: 64 | print(result) 65 | if result[2]: 66 | node_info = get_node_info(result[2]) 67 | node_info.policies = get_policies(result[2]) 68 | sros_host.nodes.append(node_info) 69 | except Exception as e: 70 | logger.exception('Exception at host scan', e) 71 | except Exception: 72 | logger.exception('Exception at host scan') 73 | return None 74 | return sros_host 75 | 76 | async def scan_network(self): 77 | """ 78 | Scan all the hosts specified in the internal hosts list :attr:`self.hosts`. 79 | 80 | :return: A list of :class:`aztarna.sros.helpers.SROSHost` containing all the found hosts. 81 | """ 82 | try: 83 | results = [] 84 | for port in self.ports: 85 | for host_address in self.host_list: 86 | results.append(self.scan_host(host_address, port)) 87 | for result in await asyncio.gather(*results): 88 | if result: 89 | self.hosts.append(result) 90 | 91 | except AddressValueError: 92 | print('Invalid network entered') 93 | except Exception as e: 94 | print(e) 95 | 96 | def scan(self): 97 | """ 98 | Run the scan for SROS hosts. Extended from :class:`aztarna.commons.BaseScanner`. 99 | This function is the one to be called externally in order to run the scans. Internally those scans are run 100 | with the help of asyncio. 101 | """ 102 | asyncio.get_event_loop().run_until_complete(self.scan_network()) 103 | 104 | async def scan_pipe(self): 105 | async for line in RobotAdapter.stream_as_generator(asyncio.get_event_loop(), sys.stdin): 106 | str_line = (line.decode()).rstrip('\n') 107 | for port in self.ports: 108 | await self.scan_host(str_line, port) 109 | 110 | def scan_pipe_main(self): 111 | asyncio.get_event_loop().run_until_complete(self.scan_pipe()) 112 | 113 | def print_results(self): 114 | """ 115 | Print the results of the scan into console. Extended from :class:`aztarna.commons.BaseScanner`. 116 | 117 | """ 118 | for host in self.hosts: 119 | print('{}:{}'.format(host.address, host.port)) 120 | for node in host.nodes: 121 | print('\tNode name: {}'.format(node.name)) 122 | print('\tPort: {}'.format(node.port)) 123 | print('\tDemo CA Used: {}'.format(node.is_demo)) 124 | if self.extended: 125 | print('\tPolicies:') 126 | for policy in node.policies: 127 | print('\t\tType: {}'.format(policy.type)) 128 | print('\t\tPermission: {}'.format(policy.permissions)) 129 | print('\t\tValues: ') 130 | for value in policy.values: 131 | print('\t\t\t{}'.format(value)) 132 | print('') 133 | print('') 134 | 135 | def write_to_file(self, out_file: str): 136 | """ 137 | Write the results to the specified output file. Extended from :class:`aztarna.commons.BaseScanner`. 138 | 139 | :param out_file: Output file name in which the results will be writen. 140 | """ 141 | lines = [] 142 | header = 'Address;Node;Port;Demo;Policy_Type;Value;Permission\n' 143 | lines.append(header) 144 | for host in self.hosts: 145 | for node in host.nodes: 146 | for policy in node.policies: 147 | for value in policy.values: 148 | line = '{};{};{};{};{};{};{}\n'.format(host.address, node.name, node.port, node.is_demo, 149 | policy.type, value, policy.permissions) 150 | lines.append(line) 151 | with open(out_file, 'w') as file: 152 | file.writelines(lines) 153 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = Aztarna 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/_build/doctrees/aztarna.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/doctrees/aztarna.doctree -------------------------------------------------------------------------------- /docs/_build/doctrees/aztarna.ros.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/doctrees/aztarna.ros.doctree -------------------------------------------------------------------------------- /docs/_build/doctrees/aztarna.sros.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/doctrees/aztarna.sros.doctree -------------------------------------------------------------------------------- /docs/_build/doctrees/environment.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/doctrees/environment.pickle -------------------------------------------------------------------------------- /docs/_build/doctrees/index.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/doctrees/index.doctree -------------------------------------------------------------------------------- /docs/_build/doctrees/install.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/doctrees/install.doctree -------------------------------------------------------------------------------- /docs/_build/doctrees/modules.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/doctrees/modules.doctree -------------------------------------------------------------------------------- /docs/_build/doctrees/usage.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/doctrees/usage.doctree -------------------------------------------------------------------------------- /docs/_build/html/.buildinfo: -------------------------------------------------------------------------------- 1 | # Sphinx build info version 1 2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. 3 | config: 0d4e70f2655e6417e29765359ea589cf 4 | tags: 645f666f9bcd5a90fca523b33c5a78b7 5 | -------------------------------------------------------------------------------- /docs/_build/html/.doctrees/aztarna.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/.doctrees/aztarna.doctree -------------------------------------------------------------------------------- /docs/_build/html/.doctrees/aztarna.industrialrouters.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/.doctrees/aztarna.industrialrouters.doctree -------------------------------------------------------------------------------- /docs/_build/html/.doctrees/aztarna.ros.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/.doctrees/aztarna.ros.doctree -------------------------------------------------------------------------------- /docs/_build/html/.doctrees/aztarna.sros.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/.doctrees/aztarna.sros.doctree -------------------------------------------------------------------------------- /docs/_build/html/.doctrees/environment.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/.doctrees/environment.pickle -------------------------------------------------------------------------------- /docs/_build/html/.doctrees/index.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/.doctrees/index.doctree -------------------------------------------------------------------------------- /docs/_build/html/.doctrees/install.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/.doctrees/install.doctree -------------------------------------------------------------------------------- /docs/_build/html/.doctrees/modules.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/.doctrees/modules.doctree -------------------------------------------------------------------------------- /docs/_build/html/.doctrees/usage.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/.doctrees/usage.doctree -------------------------------------------------------------------------------- /docs/_build/html/_modules/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Overview: module code — Aztarna 1.0 documentation 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 |
44 | 45 | 96 | 97 |
98 | 99 | 100 | 106 | 107 | 108 |
109 | 110 |
111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 |
129 | 130 |
    131 | 132 |
  • Docs »
  • 133 | 134 |
  • Overview: module code
  • 135 | 136 | 137 |
  • 138 | 139 |
  • 140 | 141 |
142 | 143 | 144 |
145 |
146 |
147 | 163 | 164 |
165 |
166 | 167 | 168 |
169 | 170 |
171 |

172 | © Copyright 2018, Alias Robotics 173 | 174 |

175 |
176 | Built with Sphinx using a theme provided by Read the Docs. 177 | 178 |
179 | 180 |
181 |
182 | 183 |
184 | 185 |
186 | 187 | 188 | 189 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | -------------------------------------------------------------------------------- /docs/_build/html/_sources/aztarna.industrialrouters.rst.txt: -------------------------------------------------------------------------------- 1 | aztarna.industrialrouters package 2 | ================================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | aztarna.industrialrouters.scanner module 8 | ---------------------------------------- 9 | 10 | .. automodule:: aztarna.industrialrouters.scanner 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | 16 | Module contents 17 | --------------- 18 | 19 | .. automodule:: aztarna.industrialrouters 20 | :members: 21 | :undoc-members: 22 | :show-inheritance: -------------------------------------------------------------------------------- /docs/_build/html/_sources/aztarna.ros.rst.txt: -------------------------------------------------------------------------------- 1 | aztarna.ros.ros package 2 | ======================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | aztarna.ros.ros.helpers module 8 | ------------------------------ 9 | 10 | .. automodule:: aztarna.ros.ros.helpers 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | aztarna.ros.ros.scanner module 16 | ------------------------------ 17 | 18 | .. automodule:: aztarna.ros.ros.scanner 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | 24 | Module contents 25 | --------------- 26 | 27 | .. automodule:: aztarna.ros.ros 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | -------------------------------------------------------------------------------- /docs/_build/html/_sources/aztarna.rst.txt: -------------------------------------------------------------------------------- 1 | aztarna package 2 | =============== 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | 9 | aztarna.ros 10 | aztarna.ros2 11 | aztarna.sros 12 | aztarna.industrialrouters 13 | 14 | Submodules 15 | ---------- 16 | 17 | aztarna.cmd module 18 | ------------------ 19 | 20 | .. automodule:: aztarna.cmd 21 | :members: 22 | :undoc-members: 23 | :show-inheritance: 24 | 25 | aztarna.commons module 26 | ---------------------- 27 | 28 | .. automodule:: aztarna.commons 29 | :members: 30 | :undoc-members: 31 | :show-inheritance: 32 | 33 | aztarna.helpers module 34 | ---------------------- 35 | 36 | .. automodule:: aztarna.helpers 37 | :members: 38 | :undoc-members: 39 | :show-inheritance: 40 | 41 | 42 | Module contents 43 | --------------- 44 | 45 | .. automodule:: aztarna 46 | :members: 47 | :undoc-members: 48 | :show-inheritance: 49 | -------------------------------------------------------------------------------- /docs/_build/html/_sources/aztarna.sros.rst.txt: -------------------------------------------------------------------------------- 1 | aztarna.ros.sros package 2 | ======================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | aztarna.ros.sros.helpers module 8 | ------------------------------- 9 | 10 | .. automodule:: aztarna.ros.sros.helpers 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | aztarna.ros.sros.scanner module 16 | ------------------------------- 17 | 18 | .. automodule:: aztarna.ros.sros.scanner 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | 24 | Module contents 25 | --------------- 26 | 27 | .. automodule:: aztarna.ros.sros 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | -------------------------------------------------------------------------------- /docs/_build/html/_sources/index.rst.txt: -------------------------------------------------------------------------------- 1 | .. Aztarna documentation master file, created by 2 | sphinx-quickstart on Wed Sep 5 17:23:49 2018. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | **Alias Robotics supports original robot manufacturers assessing their 7 | security and improving their quality of software. By no means we 8 | encourage or promote the unauthorized tampering with running robotic 9 | systems. This can cause serious human harm and material damages.** 10 | 11 | For ROS 12 | ------- 13 | 14 | - A list of the ROS nodes present in the system (Publishers and 15 | Subscribers) 16 | - For each node, the published and subscribed topis including the topic 17 | type 18 | - For each node, the ROS services each of the nodes offer 19 | - A list of all ROS parameters present in the Parameter Server 20 | - A list of the active communications running in the system. A single 21 | communication includes the involved publiser/subscriber nodes and the 22 | topics 23 | 24 | For SROS 25 | -------- 26 | 27 | - Determining if the system is a SROS master. 28 | - Detecting if demo configuration is in use. 29 | - A list of the nodes found in the system. (Extended mode) 30 | - A list of allow/deny policies for each node. 31 | 32 | - Publishable topics. 33 | - Subscriptable topics. 34 | - Executable services. 35 | - Readable parameters. 36 | 37 | For ROS2 38 | -------- 39 | - Detection of ROS2 nodes in all possible ROS2 domain IDs. Local network. 40 | - Listing of all available topics and their relationship to nodes. 41 | - Listing of all available services and their relationship to nodes. 42 | 43 | 44 | For Industrial routers 45 | ---------------------- 46 | 47 | - Detecting eWON, Moxa, Sierra Wireless and Westermo industrial routers. 48 | - Default credential checking for found routers. 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | .. toctree:: 57 | :maxdepth: 4 58 | :caption: Contents: 59 | 60 | install 61 | usage 62 | modules 63 | 64 | .. automodule:: aztarna 65 | :members: 66 | :undoc-members: 67 | :show-inheritance: 68 | 69 | 70 | 71 | Indices and tables 72 | ================== 73 | * :ref:`genindex` 74 | * :ref:`modindex` 75 | * :ref:`search` 76 | 77 | -------------------------------------------------------------------------------- /docs/_build/html/_sources/install.rst.txt: -------------------------------------------------------------------------------- 1 | .. _install: 2 | 3 | Installing 4 | ---------- 5 | 6 | For production 7 | ~~~~~~~~~~~~~~ 8 | 9 | :: 10 | 11 | pip3 install . 12 | 13 | or 14 | 15 | :: 16 | 17 | python3 setup.py install 18 | 19 | For development 20 | ~~~~~~~~~~~~~~~ 21 | 22 | :: 23 | 24 | pip3 install -e . 25 | 26 | or 27 | 28 | :: 29 | 30 | python3 setup.py develop 31 | 32 | The only requirement is 33 | `setuptools `__ package, which is 34 | usually a defacto standard in a python3 installation. 35 | 36 | Install with docker 37 | ~~~~~~~~~~~~~~~~~~~ 38 | 39 | .. code:: bash 40 | 41 | docker build -t aztarna_docker . -------------------------------------------------------------------------------- /docs/_build/html/_sources/modules.rst.txt: -------------------------------------------------------------------------------- 1 | .. _modules: 2 | 3 | Aztarna modules 4 | =============== 5 | 6 | .. toctree:: 7 | :maxdepth: 4 8 | 9 | aztarna 10 | -------------------------------------------------------------------------------- /docs/_build/html/_sources/usage.rst.txt: -------------------------------------------------------------------------------- 1 | .. _usage: 2 | 3 | Code usage 4 | ~~~~~~~~~~ 5 | 6 | .. code:: bash 7 | 8 | usage: aztarna [-h] -t TYPE [-a ADDRESS] [-p PORTS] [-i INPUT_FILE] 9 | [-o OUT_FILE] [-e] [-r RATE] [--shodan] [--api-key API_KEY] 10 | 11 | Aztarna 12 | 13 | optional arguments: 14 | -h, --help show this help message and exit 15 | -t TYPE, --type TYPE Scan ROS, SROS, ROS2 16 | hosts or Industrial routers 17 | -a ADDRESS, --address ADDRESS 18 | Single address or network range to scan. 19 | -p PORTS, --ports PORTS 20 | Ports to scan (format: 13311 or 11111-11155 or 21 | 1,2,3,4) 22 | -i INPUT_FILE, --input_file INPUT_FILE 23 | Input file of addresses to use for scanning 24 | -o OUT_FILE, --out_file OUT_FILE 25 | Output file for the results 26 | -e, --extended Extended scan of the hosts 27 | -r RATE, --rate RATE Maximum simultaneous network connections 28 | --shodan Use shodan for the scan types that support it. 29 | --api-key API_KEY Shodan API Key 30 | 31 | - Run the code (example input file): 32 | 33 | .. code:: bash 34 | 35 | aztarna -t ROS -p 11311 -i ros_scan_s20.csv 36 | 37 | - Run the code with Docker (example input file): 38 | 39 | .. code:: bash 40 | 41 | docker run -v :/root -it aztarna_docker -t ROS -p 11311 -i 42 | 43 | - Run the code (example single ip address): 44 | 45 | .. code:: bash 46 | 47 | aztarna -t ROS -p 11311 -a 115.129.241.241 48 | 49 | - Run the code (example subnet): 50 | 51 | .. code:: bash 52 | 53 | aztarna -t ROS -p 11311 -a 115.129.241.0/24 54 | 55 | - Run the code (example single ip address, port range): 56 | 57 | .. code:: bash 58 | 59 | aztarna -t ROS -p 11311-11500 -a 115.129.241.241 60 | 61 | - Run the code (example single ip address, port list): 62 | 63 | .. code:: bash 64 | 65 | aztarna -t ROS -p 11311,11312,11313 -a 115.129.241.241 66 | 67 | - Run the code (example piping directly from zmap): 68 | 69 | .. code:: bash 70 | 71 | zmap -p 11311 0.0.0.0/0 -q | aztarna -t SROS -p 11311 72 | 73 | - Run the code (example search for industrial routers in shodan) 74 | 75 | .. code:: bash 76 | 77 | aztarna -t IROUTERS --shodan --api-key 78 | 79 | - Run the code (example search for industrial routers in shodan, piping to file) 80 | 81 | .. code:: bash 82 | 83 | aztarna -t IROUTERS --shodan --api-key -o routers.csv 84 | 85 | - Run the code (example search against ROS2 nodes) 86 | 87 | .. code:: bash 88 | 89 | aztarna -t ROS2 90 | 91 | - Run the code (example search against ROS2 nodes in extended mode) 92 | 93 | .. code:: bash 94 | 95 | aztarna -t ROS2 -e 96 | 97 | - Run the code (example search against ROS2 nodes in extended mode with file output) 98 | 99 | .. code:: bash 100 | 101 | aztarna -t ROS2 -e -o output.csv 102 | -------------------------------------------------------------------------------- /docs/_build/html/_static/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/ajax-loader.gif -------------------------------------------------------------------------------- /docs/_build/html/_static/comment-bright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/comment-bright.png -------------------------------------------------------------------------------- /docs/_build/html/_static/comment-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/comment-close.png -------------------------------------------------------------------------------- /docs/_build/html/_static/comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/comment.png -------------------------------------------------------------------------------- /docs/_build/html/_static/css/badge_only.css: -------------------------------------------------------------------------------- 1 | .fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../fonts/fontawesome-webfont.eot");src:url("../fonts/fontawesome-webfont.eot?#iefix") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff") format("woff"),url("../fonts/fontawesome-webfont.ttf") format("truetype"),url("../fonts/fontawesome-webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:""}.icon-book:before{content:""}.fa-caret-down:before{content:""}.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.icon-caret-up:before{content:""}.fa-caret-left:before{content:""}.icon-caret-left:before{content:""}.fa-caret-right:before{content:""}.icon-caret-right:before{content:""}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} 2 | -------------------------------------------------------------------------------- /docs/_build/html/_static/doctools.js: -------------------------------------------------------------------------------- 1 | /* 2 | * doctools.js 3 | * ~~~~~~~~~~~ 4 | * 5 | * Sphinx JavaScript utilities for all documentation. 6 | * 7 | * :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | /** 13 | * select a different prefix for underscore 14 | */ 15 | $u = _.noConflict(); 16 | 17 | /** 18 | * make the code below compatible with browsers without 19 | * an installed firebug like debugger 20 | if (!window.console || !console.firebug) { 21 | var names = ["log", "debug", "info", "warn", "error", "assert", "dir", 22 | "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", 23 | "profile", "profileEnd"]; 24 | window.console = {}; 25 | for (var i = 0; i < names.length; ++i) 26 | window.console[names[i]] = function() {}; 27 | } 28 | */ 29 | 30 | /** 31 | * small helper function to urldecode strings 32 | */ 33 | jQuery.urldecode = function(x) { 34 | return decodeURIComponent(x).replace(/\+/g, ' '); 35 | }; 36 | 37 | /** 38 | * small helper function to urlencode strings 39 | */ 40 | jQuery.urlencode = encodeURIComponent; 41 | 42 | /** 43 | * This function returns the parsed url parameters of the 44 | * current request. Multiple values per key are supported, 45 | * it will always return arrays of strings for the value parts. 46 | */ 47 | jQuery.getQueryParameters = function(s) { 48 | if (typeof s === 'undefined') 49 | s = document.location.search; 50 | var parts = s.substr(s.indexOf('?') + 1).split('&'); 51 | var result = {}; 52 | for (var i = 0; i < parts.length; i++) { 53 | var tmp = parts[i].split('=', 2); 54 | var key = jQuery.urldecode(tmp[0]); 55 | var value = jQuery.urldecode(tmp[1]); 56 | if (key in result) 57 | result[key].push(value); 58 | else 59 | result[key] = [value]; 60 | } 61 | return result; 62 | }; 63 | 64 | /** 65 | * highlight a given string on a jquery object by wrapping it in 66 | * span elements with the given class name. 67 | */ 68 | jQuery.fn.highlightText = function(text, className) { 69 | function highlight(node, addItems) { 70 | if (node.nodeType === 3) { 71 | var val = node.nodeValue; 72 | var pos = val.toLowerCase().indexOf(text); 73 | if (pos >= 0 && 74 | !jQuery(node.parentNode).hasClass(className) && 75 | !jQuery(node.parentNode).hasClass("nohighlight")) { 76 | var span; 77 | var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); 78 | if (isInSVG) { 79 | span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); 80 | } else { 81 | span = document.createElement("span"); 82 | span.className = className; 83 | } 84 | span.appendChild(document.createTextNode(val.substr(pos, text.length))); 85 | node.parentNode.insertBefore(span, node.parentNode.insertBefore( 86 | document.createTextNode(val.substr(pos + text.length)), 87 | node.nextSibling)); 88 | node.nodeValue = val.substr(0, pos); 89 | if (isInSVG) { 90 | var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); 91 | var bbox = node.parentElement.getBBox(); 92 | rect.x.baseVal.value = bbox.x; 93 | rect.y.baseVal.value = bbox.y; 94 | rect.width.baseVal.value = bbox.width; 95 | rect.height.baseVal.value = bbox.height; 96 | rect.setAttribute('class', className); 97 | addItems.push({ 98 | "parent": node.parentNode, 99 | "target": rect}); 100 | } 101 | } 102 | } 103 | else if (!jQuery(node).is("button, select, textarea")) { 104 | jQuery.each(node.childNodes, function() { 105 | highlight(this, addItems); 106 | }); 107 | } 108 | } 109 | var addItems = []; 110 | var result = this.each(function() { 111 | highlight(this, addItems); 112 | }); 113 | for (var i = 0; i < addItems.length; ++i) { 114 | jQuery(addItems[i].parent).before(addItems[i].target); 115 | } 116 | return result; 117 | }; 118 | 119 | /* 120 | * backward compatibility for jQuery.browser 121 | * This will be supported until firefox bug is fixed. 122 | */ 123 | if (!jQuery.browser) { 124 | jQuery.uaMatch = function(ua) { 125 | ua = ua.toLowerCase(); 126 | 127 | var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || 128 | /(webkit)[ \/]([\w.]+)/.exec(ua) || 129 | /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || 130 | /(msie) ([\w.]+)/.exec(ua) || 131 | ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || 132 | []; 133 | 134 | return { 135 | browser: match[ 1 ] || "", 136 | version: match[ 2 ] || "0" 137 | }; 138 | }; 139 | jQuery.browser = {}; 140 | jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; 141 | } 142 | 143 | /** 144 | * Small JavaScript module for the documentation. 145 | */ 146 | var Documentation = { 147 | 148 | init : function() { 149 | this.fixFirefoxAnchorBug(); 150 | this.highlightSearchWords(); 151 | this.initIndexTable(); 152 | if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) { 153 | this.initOnKeyListeners(); 154 | } 155 | }, 156 | 157 | /** 158 | * i18n support 159 | */ 160 | TRANSLATIONS : {}, 161 | PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; }, 162 | LOCALE : 'unknown', 163 | 164 | // gettext and ngettext don't access this so that the functions 165 | // can safely bound to a different name (_ = Documentation.gettext) 166 | gettext : function(string) { 167 | var translated = Documentation.TRANSLATIONS[string]; 168 | if (typeof translated === 'undefined') 169 | return string; 170 | return (typeof translated === 'string') ? translated : translated[0]; 171 | }, 172 | 173 | ngettext : function(singular, plural, n) { 174 | var translated = Documentation.TRANSLATIONS[singular]; 175 | if (typeof translated === 'undefined') 176 | return (n == 1) ? singular : plural; 177 | return translated[Documentation.PLURALEXPR(n)]; 178 | }, 179 | 180 | addTranslations : function(catalog) { 181 | for (var key in catalog.messages) 182 | this.TRANSLATIONS[key] = catalog.messages[key]; 183 | this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); 184 | this.LOCALE = catalog.locale; 185 | }, 186 | 187 | /** 188 | * add context elements like header anchor links 189 | */ 190 | addContextElements : function() { 191 | $('div[id] > :header:first').each(function() { 192 | $('\u00B6'). 193 | attr('href', '#' + this.id). 194 | attr('title', _('Permalink to this headline')). 195 | appendTo(this); 196 | }); 197 | $('dt[id]').each(function() { 198 | $('\u00B6'). 199 | attr('href', '#' + this.id). 200 | attr('title', _('Permalink to this definition')). 201 | appendTo(this); 202 | }); 203 | }, 204 | 205 | /** 206 | * workaround a firefox stupidity 207 | * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 208 | */ 209 | fixFirefoxAnchorBug : function() { 210 | if (document.location.hash && $.browser.mozilla) 211 | window.setTimeout(function() { 212 | document.location.href += ''; 213 | }, 10); 214 | }, 215 | 216 | /** 217 | * highlight the search words provided in the url in the text 218 | */ 219 | highlightSearchWords : function() { 220 | var params = $.getQueryParameters(); 221 | var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; 222 | if (terms.length) { 223 | var body = $('div.body'); 224 | if (!body.length) { 225 | body = $('body'); 226 | } 227 | window.setTimeout(function() { 228 | $.each(terms, function() { 229 | body.highlightText(this.toLowerCase(), 'highlighted'); 230 | }); 231 | }, 10); 232 | $('') 234 | .appendTo($('#searchbox')); 235 | } 236 | }, 237 | 238 | /** 239 | * init the domain index toggle buttons 240 | */ 241 | initIndexTable : function() { 242 | var togglers = $('img.toggler').click(function() { 243 | var src = $(this).attr('src'); 244 | var idnum = $(this).attr('id').substr(7); 245 | $('tr.cg-' + idnum).toggle(); 246 | if (src.substr(-9) === 'minus.png') 247 | $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); 248 | else 249 | $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); 250 | }).css('display', ''); 251 | if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { 252 | togglers.click(); 253 | } 254 | }, 255 | 256 | /** 257 | * helper function to hide the search marks again 258 | */ 259 | hideSearchWords : function() { 260 | $('#searchbox .highlight-link').fadeOut(300); 261 | $('span.highlighted').removeClass('highlighted'); 262 | }, 263 | 264 | /** 265 | * make the url absolute 266 | */ 267 | makeURL : function(relativeURL) { 268 | return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; 269 | }, 270 | 271 | /** 272 | * get the current relative url 273 | */ 274 | getCurrentURL : function() { 275 | var path = document.location.pathname; 276 | var parts = path.split(/\//); 277 | $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { 278 | if (this === '..') 279 | parts.pop(); 280 | }); 281 | var url = parts.join('/'); 282 | return path.substring(url.lastIndexOf('/') + 1, path.length - 1); 283 | }, 284 | 285 | initOnKeyListeners: function() { 286 | $(document).keyup(function(event) { 287 | var activeElementType = document.activeElement.tagName; 288 | // don't navigate when in search box or textarea 289 | if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT') { 290 | switch (event.keyCode) { 291 | case 37: // left 292 | var prevHref = $('link[rel="prev"]').prop('href'); 293 | if (prevHref) { 294 | window.location.href = prevHref; 295 | return false; 296 | } 297 | case 39: // right 298 | var nextHref = $('link[rel="next"]').prop('href'); 299 | if (nextHref) { 300 | window.location.href = nextHref; 301 | return false; 302 | } 303 | } 304 | } 305 | }); 306 | } 307 | }; 308 | 309 | // quick alias for translations 310 | _ = Documentation.gettext; 311 | 312 | $(document).ready(function() { 313 | Documentation.init(); 314 | }); 315 | -------------------------------------------------------------------------------- /docs/_build/html/_static/documentation_options.js: -------------------------------------------------------------------------------- 1 | var DOCUMENTATION_OPTIONS = { 2 | URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), 3 | VERSION: '1.0', 4 | LANGUAGE: 'None', 5 | COLLAPSE_INDEX: false, 6 | FILE_SUFFIX: '.html', 7 | HAS_SOURCE: true, 8 | SOURCELINK_SUFFIX: '.txt', 9 | NAVIGATION_WITH_KEYS: false 10 | }; -------------------------------------------------------------------------------- /docs/_build/html/_static/down-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/down-pressed.png -------------------------------------------------------------------------------- /docs/_build/html/_static/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/down.png -------------------------------------------------------------------------------- /docs/_build/html/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/file.png -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/Inconsolata-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/Inconsolata-Bold.ttf -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/Inconsolata-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/Inconsolata-Regular.ttf -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/Inconsolata.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/Inconsolata.ttf -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/Lato-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/Lato-Bold.ttf -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/Lato-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/Lato-Regular.ttf -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/Lato/lato-bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/Lato/lato-bold.eot -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/Lato/lato-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/Lato/lato-bold.ttf -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/Lato/lato-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/Lato/lato-bold.woff -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/Lato/lato-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/Lato/lato-bold.woff2 -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/Lato/lato-bolditalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/Lato/lato-bolditalic.eot -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/Lato/lato-bolditalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/Lato/lato-bolditalic.ttf -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/Lato/lato-bolditalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/Lato/lato-bolditalic.woff -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/Lato/lato-bolditalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/Lato/lato-bolditalic.woff2 -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/Lato/lato-italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/Lato/lato-italic.eot -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/Lato/lato-italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/Lato/lato-italic.ttf -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/Lato/lato-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/Lato/lato-italic.woff -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/Lato/lato-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/Lato/lato-italic.woff2 -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/Lato/lato-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/Lato/lato-regular.eot -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/Lato/lato-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/Lato/lato-regular.ttf -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/Lato/lato-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/Lato/lato-regular.woff -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/Lato/lato-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/Lato/lato-regular.woff2 -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/RobotoSlab-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/RobotoSlab-Bold.ttf -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/RobotoSlab-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/RobotoSlab-Regular.ttf -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2 -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/_build/html/_static/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /docs/_build/html/_static/js/theme.js: -------------------------------------------------------------------------------- 1 | /* sphinx_rtd_theme version 0.4.3 | MIT license */ 2 | /* Built 20190212 16:02 */ 3 | require=function r(s,a,l){function c(e,n){if(!a[e]){if(!s[e]){var i="function"==typeof require&&require;if(!n&&i)return i(e,!0);if(u)return u(e,!0);var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}var o=a[e]={exports:{}};s[e][0].call(o.exports,function(n){return c(s[e][1][n]||n)},o,o.exports,r,s,a,l)}return a[e].exports}for(var u="function"==typeof require&&require,n=0;n"),i("table.docutils.footnote").wrap("
"),i("table.docutils.citation").wrap("
"),i(".wy-menu-vertical ul").not(".simple").siblings("a").each(function(){var e=i(this);expand=i(''),expand.on("click",function(n){return t.toggleCurrent(e),n.stopPropagation(),!1}),e.prepend(expand)})},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),i=e.find('[href="'+n+'"]');if(0===i.length){var t=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(i=e.find('[href="#'+t.attr("id")+'"]')).length&&(i=e.find('[href="#"]'))}0this.docHeight||(this.navBar.scrollTop(i),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",function(){this.linkScroll=!1})},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current"),e.siblings().find("li.current").removeClass("current"),e.find("> ul li.current").removeClass("current"),e.toggleClass("current")}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:e.exports.ThemeNav,StickyNav:e.exports.ThemeNav}),function(){for(var r=0,n=["ms","moz","webkit","o"],e=0;e2;a== 12 | null&&(a=[]);if(y&&a.reduce===y)return e&&(c=b.bind(c,e)),f?a.reduce(c,d):a.reduce(c);j(a,function(a,b,i){f?d=c.call(e,d,a,b,i):(d=a,f=true)});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(z&&a.reduceRight===z)return e&&(c=b.bind(c,e)),f?a.reduceRight(c,d):a.reduceRight(c);var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect= 13 | function(a,c,b){var e;E(a,function(a,g,h){if(c.call(b,a,g,h))return e=a,true});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(A&&a.filter===A)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(B&&a.every===B)return a.every(c,b);j(a,function(a,g,h){if(!(e= 14 | e&&c.call(b,a,g,h)))return n});return e};var E=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(C&&a.some===C)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return n});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;return p&&a.indexOf===p?a.indexOf(c)!=-1:b=E(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck= 15 | function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;bd?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a, 17 | c,d){d||(d=b.identity);for(var e=0,f=a.length;e>1;d(a[g])=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1));return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e=0;d--)b=[a[d].apply(this,b)];return b[0]}}; 24 | b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=J||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.defaults=function(a){j(i.call(arguments, 25 | 1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return q(a,b,[])};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=o||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)}; 26 | b.isArguments=function(a){return l.call(a)=="[object Arguments]"};if(!b.isArguments(arguments))b.isArguments=function(a){return!(!a||!b.has(a,"callee"))};b.isFunction=function(a){return l.call(a)=="[object Function]"};b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"}; 27 | b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,b){return I.call(a,b)};b.noConflict=function(){r._=G;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};b.mixin=function(a){j(b.functions(a), 28 | function(c){K(c,b[c]=a[c])})};var L=0;b.uniqueId=function(a){var b=L++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var t=/.^/,u=function(a){return a.replace(/\\\\/g,"\\").replace(/\\'/g,"'")};b.template=function(a,c){var d=b.templateSettings,d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.escape||t,function(a,b){return"',_.escape("+ 29 | u(b)+"),'"}).replace(d.interpolate||t,function(a,b){return"',"+u(b)+",'"}).replace(d.evaluate||t,function(a,b){return"');"+u(b).replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",e=new Function("obj","_",d);return c?e(c,b):function(a){return e.call(this,a,b)}};b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var v=function(a,c){return c?b(a).chain():a},K=function(a,c){m.prototype[a]= 30 | function(){var a=i.call(arguments);H.call(a,this._wrapped);return v(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return v(d,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return v(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain= 31 | true;return this};m.prototype.value=function(){return this._wrapped}}).call(this); 32 | -------------------------------------------------------------------------------- /docs/_build/html/_static/up-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/up-pressed.png -------------------------------------------------------------------------------- /docs/_build/html/_static/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/_static/up.png -------------------------------------------------------------------------------- /docs/_build/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | For ROS — Aztarna 1.0 documentation 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 |
45 | 46 | 97 | 98 |
99 | 100 | 101 | 107 | 108 | 109 |
110 | 111 |
112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 |
130 | 131 |
    132 | 133 |
  • Docs »
  • 134 | 135 |
  • For ROS
  • 136 | 137 | 138 |
  • 139 | 140 | 141 | View page source 142 | 143 | 144 |
  • 145 | 146 |
147 | 148 | 149 |
150 |
151 |
152 |
153 | 154 |

Alias Robotics supports original robot manufacturers assessing their 155 | security and improving their quality of software. By no means we 156 | encourage or promote the unauthorized tampering with running robotic 157 | systems. This can cause serious human harm and material damages.

158 |
159 |

For ROS

160 |
    161 |
  • A list of the ROS nodes present in the system (Publishers and 162 | Subscribers)

  • 163 |
  • For each node, the published and subscribed topis including the topic 164 | type

  • 165 |
  • For each node, the ROS services each of the nodes offer

  • 166 |
  • A list of all ROS parameters present in the Parameter Server

  • 167 |
  • A list of the active communications running in the system. A single 168 | communication includes the involved publiser/subscriber nodes and the 169 | topics

  • 170 |
171 |
172 |
173 |

For SROS

174 |
    175 |
  • Determining if the system is a SROS master.

  • 176 |
  • Detecting if demo configuration is in use.

  • 177 |
  • A list of the nodes found in the system. (Extended mode)

  • 178 |
  • A list of allow/deny policies for each node.

    179 |
      180 |
    • Publishable topics.

    • 181 |
    • Subscriptable topics.

    • 182 |
    • Executable services.

    • 183 |
    • Readable parameters.

    • 184 |
    185 |
  • 186 |
187 |
188 |
189 |

For ROS2

190 |
    191 |
  • Detection of ROS2 nodes in all possible ROS2 domain IDs. Local network.

  • 192 |
  • Listing of all available topics and their relationship to nodes.

  • 193 |
  • Listing of all available services and their relationship to nodes.

  • 194 |
195 |
196 |
197 |

For Industrial routers

198 |
    199 |
  • Detecting eWON, Moxa, Sierra Wireless and Westermo industrial routers.

  • 200 |
  • Default credential checking for found routers.

  • 201 |
202 |
203 |

Contents:

204 | 231 |
232 |
233 |

Indices and tables

234 | 239 |
240 |
241 | 242 | 243 |
244 | 245 |
246 |
247 | 248 | 254 | 255 | 256 |
257 | 258 |
259 |

260 | © Copyright 2018, Alias Robotics 261 | 262 |

263 |
264 | Built with Sphinx using a theme provided by Read the Docs. 265 | 266 |
267 | 268 |
269 |
270 | 271 |
272 | 273 |
274 | 275 | 276 | 277 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | -------------------------------------------------------------------------------- /docs/_build/html/install.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Installing — Aztarna 1.0 documentation 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 | 47 | 103 | 104 |
105 | 106 | 107 | 113 | 114 | 115 |
116 | 117 |
118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 |
136 | 137 |
    138 | 139 |
  • Docs »
  • 140 | 141 |
  • Installing
  • 142 | 143 | 144 |
  • 145 | 146 | 147 | View page source 148 | 149 | 150 |
  • 151 | 152 |
153 | 154 | 155 |
156 |
157 |
158 |
159 | 160 |
161 |

Installing

162 |
163 |

For production

164 |
pip3 install .
165 | 
166 |
167 |

or

168 |
python3 setup.py install
169 | 
170 |
171 |
172 |
173 |

For development

174 |
pip3 install -e .
175 | 
176 |
177 |

or

178 |
python3 setup.py develop
179 | 
180 |
181 |

The only requirement is 182 | setuptools package, which is 183 | usually a defacto standard in a python3 installation.

184 |
185 |
186 |

Install with docker

187 |
docker build -t aztarna_docker .
188 | 
189 |
190 |
191 |
192 | 193 | 194 |
195 | 196 |
197 |
198 | 199 | 207 | 208 | 209 |
210 | 211 |
212 |

213 | © Copyright 2018, Alias Robotics 214 | 215 |

216 |
217 | Built with Sphinx using a theme provided by Read the Docs. 218 | 219 |
220 | 221 |
222 |
223 | 224 |
225 | 226 |
227 | 228 | 229 | 230 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | -------------------------------------------------------------------------------- /docs/_build/html/modules.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Aztarna modules — Aztarna 1.0 documentation 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 | 47 | 101 | 102 |
103 | 104 | 105 | 111 | 112 | 113 |
114 | 115 |
116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 |
134 | 135 |
    136 | 137 |
  • Docs »
  • 138 | 139 |
  • Aztarna modules
  • 140 | 141 | 142 |
  • 143 | 144 | 145 | View page source 146 | 147 | 148 |
  • 149 | 150 |
151 | 152 | 153 |
154 |
155 | 208 |
209 | 210 | 218 | 219 | 220 |
221 | 222 |
223 |

224 | © Copyright 2018, Alias Robotics 225 | 226 |

227 |
228 | Built with Sphinx using a theme provided by Read the Docs. 229 | 230 |
231 | 232 |
233 |
234 | 235 |
236 | 237 |
238 | 239 | 240 | 241 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | -------------------------------------------------------------------------------- /docs/_build/html/objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_build/html/objects.inv -------------------------------------------------------------------------------- /docs/_build/html/py-modindex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Python Module Index — Aztarna 1.0 documentation 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
47 | 48 | 99 | 100 |
101 | 102 | 103 | 109 | 110 | 111 |
112 | 113 |
114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 |
132 | 133 |
    134 | 135 |
  • Docs »
  • 136 | 137 |
  • Python Module Index
  • 138 | 139 | 140 |
  • 141 | 142 |
  • 143 | 144 |
145 | 146 | 147 |
148 |
149 |
150 |
151 | 152 | 153 |

Python Module Index

154 | 155 |
156 | a 157 |
158 | 159 | 160 | 161 | 163 | 164 | 166 | 169 | 170 | 172 | 175 | 176 | 177 | 180 | 181 | 182 | 185 | 186 | 187 | 190 | 191 | 192 | 195 | 196 | 197 | 200 | 201 | 202 | 205 | 206 | 207 | 210 | 211 | 212 | 215 | 216 | 217 | 220 | 221 | 222 | 225 | 226 | 227 | 230 | 231 | 232 | 235 | 236 | 237 | 240 | 241 | 242 | 245 |
 
162 | a
167 | aztarna 168 |
173 | aztarna 174 |
    178 | aztarna.cmd 179 |
    183 | aztarna.commons 184 |
    188 | aztarna.helpers 189 |
    193 | aztarna.industrialrouters 194 |
    198 | aztarna.industrialrouters.scanner 199 |
    203 | aztarna.ros.ros 204 |
    208 | aztarna.ros.ros.helpers 209 |
    213 | aztarna.ros.ros.scanner 214 |
    218 | aztarna.ros.ros2 219 |
    223 | aztarna.ros.ros2.helpers 224 |
    228 | aztarna.ros.ros2.scanner 229 |
    233 | aztarna.ros.sros 234 |
    238 | aztarna.ros.sros.helpers 239 |
    243 | aztarna.ros.sros.scanner 244 |
246 | 247 | 248 |
249 | 250 |
251 |
252 | 253 | 254 |
255 | 256 |
257 |

258 | © Copyright 2018, Alias Robotics 259 | 260 |

261 |
262 | Built with Sphinx using a theme provided by Read the Docs. 263 | 264 |
265 | 266 |
267 |
268 | 269 |
270 | 271 |
272 | 273 | 274 | 275 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | -------------------------------------------------------------------------------- /docs/_build/html/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Search — Aztarna 1.0 documentation 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 |
45 | 46 | 97 | 98 |
99 | 100 | 101 | 107 | 108 | 109 |
110 | 111 |
112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 |
130 | 131 |
    132 | 133 |
  • Docs »
  • 134 | 135 |
  • Search
  • 136 | 137 | 138 |
  • 139 | 140 | 141 | 142 |
  • 143 | 144 |
145 | 146 | 147 |
148 |
149 |
150 |
151 | 152 | 160 | 161 | 162 |
163 | 164 |
165 | 166 |
167 | 168 |
169 |
170 | 171 | 172 |
173 | 174 |
175 |

176 | © Copyright 2018, Alias Robotics 177 | 178 |

179 |
180 | Built with Sphinx using a theme provided by Read the Docs. 181 | 182 |
183 | 184 |
185 |
186 | 187 |
188 | 189 |
190 | 191 | 192 | 193 | 198 | 199 | 200 | 201 | 202 | 203 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | -------------------------------------------------------------------------------- /docs/_build/html/usage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Code usage — Aztarna 1.0 documentation 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 | 47 | 98 | 99 |
100 | 101 | 102 | 108 | 109 | 110 |
111 | 112 |
113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 |
131 | 132 |
    133 | 134 |
  • Docs »
  • 135 | 136 |
  • Code usage
  • 137 | 138 | 139 |
  • 140 | 141 | 142 | View page source 143 | 144 | 145 |
  • 146 | 147 |
148 | 149 | 150 |
151 |
152 |
153 |
154 | 155 |
156 |

Code usage

157 |
usage: aztarna [-h] -t TYPE [-a ADDRESS] [-p PORTS] [-i INPUT_FILE]
158 |                [-o OUT_FILE] [-e] [-r RATE] [--shodan] [--api-key API_KEY]
159 | 
160 | Aztarna
161 | 
162 | optional arguments:
163 |   -h, --help            show this help message and exit
164 |   -t TYPE, --type TYPE  <ROS/ros/SROS/sros/ROS2/ros2/IROUTERS/irouters> Scan ROS, SROS, ROS2
165 |                         hosts or Industrial routers
166 |   -a ADDRESS, --address ADDRESS
167 |                         Single address or network range to scan.
168 |   -p PORTS, --ports PORTS
169 |                         Ports to scan (format: 13311 or 11111-11155 or
170 |                         1,2,3,4)
171 |   -i INPUT_FILE, --input_file INPUT_FILE
172 |                         Input file of addresses to use for scanning
173 |   -o OUT_FILE, --out_file OUT_FILE
174 |                         Output file for the results
175 |   -e, --extended        Extended scan of the hosts
176 |   -r RATE, --rate RATE  Maximum simultaneous network connections
177 |   --shodan              Use shodan for the scan types that support it.
178 |   --api-key API_KEY     Shodan API Key
179 | 
180 |
181 |
    182 |
  • Run the code (example input file):

  • 183 |
184 |
aztarna -t ROS -p 11311 -i ros_scan_s20.csv
185 | 
186 |
187 |
    188 |
  • Run the code with Docker (example input file):

  • 189 |
190 |
docker run -v <host_path>:/root -it aztarna_docker -t ROS -p 11311 -i <input_file>
191 | 
192 |
193 |
    194 |
  • Run the code (example single ip address):

  • 195 |
196 |
aztarna -t ROS -p 11311 -a 115.129.241.241
197 | 
198 |
199 |
    200 |
  • Run the code (example subnet):

  • 201 |
202 |
aztarna -t ROS -p 11311 -a 115.129.241.0/24
203 | 
204 |
205 |
    206 |
  • Run the code (example single ip address, port range):

  • 207 |
208 |
aztarna -t ROS -p 11311-11500 -a 115.129.241.241
209 | 
210 |
211 |
    212 |
  • Run the code (example single ip address, port list):

  • 213 |
214 |
aztarna -t ROS -p 11311,11312,11313 -a 115.129.241.241
215 | 
216 |
217 |
    218 |
  • Run the code (example piping directly from zmap):

  • 219 |
220 |
zmap -p 11311 0.0.0.0/0 -q | aztarna -t SROS -p 11311
221 | 
222 |
223 |
    224 |
  • Run the code (example search for industrial routers in shodan)

  • 225 |
226 |
aztarna -t IROUTERS --shodan --api-key <yourshodanapikey>
227 | 
228 |
229 |
    230 |
  • Run the code (example search for industrial routers in shodan, piping to file)

  • 231 |
232 |
aztarna -t IROUTERS --shodan --api-key <yourshodanapikey> -o routers.csv
233 | 
234 |
235 |
    236 |
  • Run the code (example search against ROS2 nodes)

  • 237 |
238 |
aztarna -t ROS2
239 | 
240 |
241 |
    242 |
  • Run the code (example search against ROS2 nodes in extended mode)

  • 243 |
244 |
aztarna -t ROS2 -e
245 | 
246 |
247 |
    248 |
  • Run the code (example search against ROS2 nodes in extended mode with file output)

  • 249 |
250 |
aztarna -t ROS2 -e -o output.csv
251 | 
252 |
253 |
254 | 255 | 256 |
257 | 258 |
259 |
260 | 261 | 269 | 270 | 271 |
272 | 273 |
274 |

275 | © Copyright 2018, Alias Robotics 276 | 277 |

278 |
279 | Built with Sphinx using a theme provided by Read the Docs. 280 | 281 |
282 | 283 |
284 |
285 | 286 |
287 | 288 |
289 | 290 | 291 | 292 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | -------------------------------------------------------------------------------- /docs/_static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aliasrobotics/aztarna/952ff658234f614ea239661f6b2bb7e3f8824a7a/docs/_static/logo.png -------------------------------------------------------------------------------- /docs/aztarna.industrialrouters.rst: -------------------------------------------------------------------------------- 1 | aztarna.industrialrouters package 2 | ================================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | aztarna.industrialrouters.scanner module 8 | ---------------------------------------- 9 | 10 | .. automodule:: aztarna.industrialrouters.scanner 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | 16 | Module contents 17 | --------------- 18 | 19 | .. automodule:: aztarna.industrialrouters 20 | :members: 21 | :undoc-members: 22 | :show-inheritance: -------------------------------------------------------------------------------- /docs/aztarna.ros.rst: -------------------------------------------------------------------------------- 1 | aztarna.ros.ros package 2 | ======================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | aztarna.ros.ros.helpers module 8 | ------------------------------ 9 | 10 | .. automodule:: aztarna.ros.ros.helpers 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | aztarna.ros.ros.scanner module 16 | ------------------------------ 17 | 18 | .. automodule:: aztarna.ros.ros.scanner 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | 24 | Module contents 25 | --------------- 26 | 27 | .. automodule:: aztarna.ros.ros 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | -------------------------------------------------------------------------------- /docs/aztarna.ros2.rst: -------------------------------------------------------------------------------- 1 | aztarna.ros.ros2 package 2 | ======================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | aztarna.ros.ros2.helpers module 8 | ------------------------------- 9 | 10 | .. automodule:: aztarna.ros.ros2.helpers 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | aztarna.ros.ros2.scanner module 16 | ------------------------------- 17 | 18 | .. automodule:: aztarna.ros.ros2.scanner 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | 24 | Module contents 25 | --------------- 26 | 27 | .. automodule:: aztarna.ros.ros2 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | -------------------------------------------------------------------------------- /docs/aztarna.rst: -------------------------------------------------------------------------------- 1 | aztarna package 2 | =============== 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | 9 | aztarna.ros 10 | aztarna.ros2 11 | aztarna.sros 12 | aztarna.industrialrouters 13 | 14 | Submodules 15 | ---------- 16 | 17 | aztarna.cmd module 18 | ------------------ 19 | 20 | .. automodule:: aztarna.cmd 21 | :members: 22 | :undoc-members: 23 | :show-inheritance: 24 | 25 | aztarna.commons module 26 | ---------------------- 27 | 28 | .. automodule:: aztarna.commons 29 | :members: 30 | :undoc-members: 31 | :show-inheritance: 32 | 33 | aztarna.helpers module 34 | ---------------------- 35 | 36 | .. automodule:: aztarna.helpers 37 | :members: 38 | :undoc-members: 39 | :show-inheritance: 40 | 41 | 42 | Module contents 43 | --------------- 44 | 45 | .. automodule:: aztarna 46 | :members: 47 | :undoc-members: 48 | :show-inheritance: 49 | -------------------------------------------------------------------------------- /docs/aztarna.sros.rst: -------------------------------------------------------------------------------- 1 | aztarna.ros.sros package 2 | ======================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | aztarna.ros.sros.helpers module 8 | ------------------------------- 9 | 10 | .. automodule:: aztarna.ros.sros.helpers 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | aztarna.ros.sros.scanner module 16 | ------------------------------- 17 | 18 | .. automodule:: aztarna.ros.sros.scanner 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | 24 | Module contents 25 | --------------- 26 | 27 | .. automodule:: aztarna.ros.sros 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Configuration file for the Sphinx documentation builder. 4 | # 5 | # This file does only contain a selection of the most common options. For a 6 | # full list see the documentation: 7 | # http://www.sphinx-doc.org/en/master/config 8 | 9 | # -- Path setup -------------------------------------------------------------- 10 | 11 | # If extensions (or modules to document with autodoc) are in another directory, 12 | # add these directories to sys.path here. If the directory is relative to the 13 | # documentation root, use os.path.abspath to make it absolute, like shown here. 14 | # 15 | # import os 16 | # import sys 17 | # sys.path.insert(0, os.path.abspath('.')) 18 | 19 | import os 20 | import sys 21 | sys.path.append(os.path.join(os.path.dirname(__name__), '..')) 22 | 23 | # -- Project information ----------------------------------------------------- 24 | 25 | project = 'Aztarna' 26 | copyright = '2018, Alias Robotics' 27 | author = 'Alias Robotics' 28 | 29 | # The short X.Y version 30 | version = '' 31 | # The full version, including alpha/beta/rc tags 32 | release = '1.0' 33 | 34 | 35 | # -- General configuration --------------------------------------------------- 36 | 37 | # If your documentation needs a minimal Sphinx version, state it here. 38 | # 39 | # needs_sphinx = '1.0' 40 | 41 | # Add any Sphinx extension module names here, as strings. They can be 42 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 43 | # ones. 44 | extensions = [ 45 | 'sphinx.ext.autodoc', 46 | 'sphinx.ext.intersphinx', 47 | 'sphinx.ext.todo', 48 | 'sphinx.ext.coverage', 49 | 'sphinx.ext.viewcode', 50 | 'sphinx.ext.napoleon', 51 | ] 52 | 53 | # Add any paths that contain templates here, relative to this directory. 54 | templates_path = ['_templates'] 55 | 56 | # The suffix(es) of source filenames. 57 | # You can specify multiple suffix as a list of string: 58 | # 59 | # source_suffix = ['.rst', '.md'] 60 | source_suffix = '.rst' 61 | 62 | # The master toctree document. 63 | master_doc = 'index' 64 | 65 | # The language for content autogenerated by Sphinx. Refer to documentation 66 | # for a list of supported languages. 67 | # 68 | # This is also used if you do content translation via gettext catalogs. 69 | # Usually you set "language" from the command line for these cases. 70 | language = None 71 | 72 | # List of patterns, relative to source directory, that match files and 73 | # directories to ignore when looking for source files. 74 | # This pattern also affects html_static_path and html_extra_path . 75 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 76 | 77 | # The name of the Pygments (syntax highlighting) style to use. 78 | pygments_style = 'sphinx' 79 | 80 | 81 | # -- Options for HTML output ------------------------------------------------- 82 | 83 | # The theme to use for HTML and HTML Help pages. See the documentation for 84 | # a list of builtin themes. 85 | # 86 | html_theme = 'sphinx_rtd_theme' 87 | 88 | # Theme options are theme-specific and customize the look and feel of a theme 89 | # further. For a list of options available for each theme, see the 90 | # documentation. 91 | # 92 | # html_theme_options = {} 93 | 94 | html_logo = '_static/logo.png' 95 | 96 | # Add any paths that contain custom static files (such as style sheets) here, 97 | # relative to this directory. They are copied after the builtin static files, 98 | # so a file named "default.css" will overwrite the builtin "default.css". 99 | html_static_path = ['_static'] 100 | 101 | # Custom sidebar templates, must be a dictionary that maps document names 102 | # to template names. 103 | # 104 | # The default sidebars (for documents that don't match any pattern) are 105 | # defined by theme itself. Builtin themes are using these templates by 106 | # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', 107 | # 'searchbox.html']``. 108 | # 109 | # html_sidebars = {} 110 | 111 | 112 | # -- Options for HTMLHelp output --------------------------------------------- 113 | 114 | # Output file base name for HTML help builder. 115 | htmlhelp_basename = 'Aztarnadoc' 116 | 117 | 118 | # -- Options for LaTeX output ------------------------------------------------ 119 | 120 | latex_elements = { 121 | # The paper size ('letterpaper' or 'a4paper'). 122 | # 123 | # 'papersize': 'letterpaper', 124 | 125 | # The font size ('10pt', '11pt' or '12pt'). 126 | # 127 | # 'pointsize': '10pt', 128 | 129 | # Additional stuff for the LaTeX preamble. 130 | # 131 | # 'preamble': '', 132 | 133 | # Latex figure (float) alignment 134 | # 135 | # 'figure_align': 'htbp', 136 | } 137 | 138 | # Grouping the document tree into LaTeX files. List of tuples 139 | # (source start file, target name, title, 140 | # author, documentclass [howto, manual, or own class]). 141 | latex_documents = [ 142 | (master_doc, 'Aztarna.tex', 'Aztarna Documentation', 143 | 'Alias Robotics', 'manual'), 144 | ] 145 | 146 | 147 | # -- Options for manual page output ------------------------------------------ 148 | 149 | # One entry per manual page. List of tuples 150 | # (source start file, name, description, authors, manual section). 151 | man_pages = [ 152 | (master_doc, 'aztarna', 'Aztarna Documentation', 153 | [author], 1) 154 | ] 155 | 156 | 157 | # -- Options for Texinfo output ---------------------------------------------- 158 | 159 | # Grouping the document tree into Texinfo files. List of tuples 160 | # (source start file, target name, title, author, 161 | # dir menu entry, description, category) 162 | texinfo_documents = [ 163 | (master_doc, 'Aztarna', 'Aztarna Documentation', 164 | author, 'Aztarna', 'One line description of project.', 165 | 'Miscellaneous'), 166 | ] 167 | 168 | 169 | # -- Extension configuration ------------------------------------------------- 170 | 171 | # -- Options for intersphinx extension --------------------------------------- 172 | 173 | # Example configuration for intersphinx: refer to the Python standard library. 174 | intersphinx_mapping = {'https://docs.python.org/': None} 175 | 176 | # -- Options for todo extension ---------------------------------------------- 177 | 178 | # If true, `todo` and `todoList` produce output, else they produce nothing. 179 | todo_include_todos = True -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. Aztarna documentation master file, created by 2 | sphinx-quickstart on Wed Sep 5 17:23:49 2018. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | **Alias Robotics supports original robot manufacturers assessing their 7 | security and improving their quality of software. By no means we 8 | encourage or promote the unauthorized tampering with running robotic 9 | systems. This can cause serious human harm and material damages.** 10 | 11 | For ROS 12 | ------- 13 | 14 | - A list of the ROS nodes present in the system (Publishers and 15 | Subscribers) 16 | - For each node, the published and subscribed topis including the topic 17 | type 18 | - For each node, the ROS services each of the nodes offer 19 | - A list of all ROS parameters present in the Parameter Server 20 | - A list of the active communications running in the system. A single 21 | communication includes the involved publiser/subscriber nodes and the 22 | topics 23 | 24 | For SROS 25 | -------- 26 | 27 | - Determining if the system is a SROS master. 28 | - Detecting if demo configuration is in use. 29 | - A list of the nodes found in the system. (Extended mode) 30 | - A list of allow/deny policies for each node. 31 | 32 | - Publishable topics. 33 | - Subscriptable topics. 34 | - Executable services. 35 | - Readable parameters. 36 | 37 | For ROS2 38 | -------- 39 | - Detection of ROS2 nodes in all possible ROS2 domain IDs. Local network. 40 | - Listing of all available topics and their relationship to nodes. 41 | - Listing of all available services and their relationship to nodes. 42 | 43 | 44 | For Industrial routers 45 | ---------------------- 46 | 47 | - Detecting eWON, Moxa, Sierra Wireless and Westermo industrial routers. 48 | - Default credential checking for found routers. 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | .. toctree:: 57 | :maxdepth: 4 58 | :caption: Contents: 59 | 60 | install 61 | usage 62 | modules 63 | 64 | .. automodule:: aztarna 65 | :members: 66 | :undoc-members: 67 | :show-inheritance: 68 | 69 | 70 | 71 | Indices and tables 72 | ================== 73 | * :ref:`genindex` 74 | * :ref:`modindex` 75 | * :ref:`search` 76 | 77 | -------------------------------------------------------------------------------- /docs/install.rst: -------------------------------------------------------------------------------- 1 | .. _install: 2 | 3 | Installing 4 | ---------- 5 | 6 | For production 7 | ~~~~~~~~~~~~~~ 8 | 9 | :: 10 | 11 | pip3 install . 12 | 13 | or 14 | 15 | :: 16 | 17 | python3 setup.py install 18 | 19 | For development 20 | ~~~~~~~~~~~~~~~ 21 | 22 | :: 23 | 24 | pip3 install -e . 25 | 26 | or 27 | 28 | :: 29 | 30 | python3 setup.py develop 31 | 32 | The only requirement is 33 | `setuptools `__ package, which is 34 | usually a defacto standard in a python3 installation. 35 | 36 | Install with docker 37 | ~~~~~~~~~~~~~~~~~~~ 38 | 39 | .. code:: bash 40 | 41 | docker build -t aztarna_docker . -------------------------------------------------------------------------------- /docs/modules.rst: -------------------------------------------------------------------------------- 1 | .. _modules: 2 | 3 | Aztarna modules 4 | =============== 5 | 6 | .. toctree:: 7 | :maxdepth: 4 8 | 9 | aztarna 10 | -------------------------------------------------------------------------------- /docs/usage.rst: -------------------------------------------------------------------------------- 1 | .. _usage: 2 | 3 | Code usage 4 | ~~~~~~~~~~ 5 | 6 | .. code:: bash 7 | 8 | usage: aztarna [-h] -t TYPE [-a ADDRESS] [-p PORTS] [-i INPUT_FILE] 9 | [-o OUT_FILE] [-e] [-r RATE] [--shodan] [--api-key API_KEY] 10 | 11 | Aztarna 12 | 13 | optional arguments: 14 | -h, --help show this help message and exit 15 | -t TYPE, --type TYPE Scan ROS, SROS, ROS2 16 | hosts or Industrial routers 17 | -a ADDRESS, --address ADDRESS 18 | Single address or network range to scan. 19 | -p PORTS, --ports PORTS 20 | Ports to scan (format: 13311 or 11111-11155 or 21 | 1,2,3,4) 22 | -i INPUT_FILE, --input_file INPUT_FILE 23 | Input file of addresses to use for scanning 24 | -o OUT_FILE, --out_file OUT_FILE 25 | Output file for the results 26 | -e, --extended Extended scan of the hosts 27 | -r RATE, --rate RATE Maximum simultaneous network connections 28 | --shodan Use shodan for the scan types that support it. 29 | --api-key API_KEY Shodan API Key 30 | 31 | - Run the code (example input file): 32 | 33 | .. code:: bash 34 | 35 | aztarna -t ROS -p 11311 -i ros_scan_s20.csv 36 | 37 | - Run the code with Docker (example input file): 38 | 39 | .. code:: bash 40 | 41 | docker run -v :/root -it aztarna_docker -t ROS -p 11311 -i 42 | 43 | - Run the code (example single ip address): 44 | 45 | .. code:: bash 46 | 47 | aztarna -t ROS -p 11311 -a 115.129.241.241 48 | 49 | - Run the code (example subnet): 50 | 51 | .. code:: bash 52 | 53 | aztarna -t ROS -p 11311 -a 115.129.241.0/24 54 | 55 | - Run the code (example single ip address, port range): 56 | 57 | .. code:: bash 58 | 59 | aztarna -t ROS -p 11311-11500 -a 115.129.241.241 60 | 61 | - Run the code (example single ip address, port list): 62 | 63 | .. code:: bash 64 | 65 | aztarna -t ROS -p 11311,11312,11313 -a 115.129.241.241 66 | 67 | - Run the code (example piping directly from zmap): 68 | 69 | .. code:: bash 70 | 71 | zmap -p 11311 0.0.0.0/0 -q | aztarna -t SROS -p 11311 72 | 73 | - Run the code (example search for industrial routers in shodan) 74 | 75 | .. code:: bash 76 | 77 | aztarna -t IROUTERS --shodan --api-key 78 | 79 | - Run the code (example search for industrial routers in shodan, piping to file) 80 | 81 | .. code:: bash 82 | 83 | aztarna -t IROUTERS --shodan --api-key -o routers.csv 84 | 85 | - Run the code (example search against ROS2 nodes) 86 | 87 | .. code:: bash 88 | 89 | aztarna -t ROS2 90 | 91 | - Run the code (example search against ROS2 nodes in extended mode) 92 | 93 | .. code:: bash 94 | 95 | aztarna -t ROS2 -e 96 | 97 | - Run the code (example search against ROS2 nodes in extended mode with file output) 98 | 99 | .. code:: bash 100 | 101 | aztarna -t ROS2 -e -o output.csv 102 | -------------------------------------------------------------------------------- /readthedocs.yml: -------------------------------------------------------------------------------- 1 | build: 2 | image: latest 3 | 4 | python: 5 | version: 3.6 6 | 7 | requirements_file: requirements.txt 8 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp==3.5.4 2 | aiohttp-xmlrpc==0.7.4 3 | alabaster==0.7.12 4 | argcomplete==1.9.5 5 | asn1crypto==0.24.0 6 | async-timeout==3.0.1 7 | attrs==19.1.0 8 | Babel==2.6.0 9 | certifi==2019.3.9 10 | cffi==1.12.2 11 | chardet==3.0.4 12 | Click==7.0 13 | click-plugins==1.1.1 14 | colorama==0.4.1 15 | cryptography==2.6.1 16 | dnspython==1.16.0 17 | docutils==0.14 18 | idna==2.8 19 | idna-ssl==1.1.0 20 | imagesize==1.1.0 21 | ipwhois==1.1.0 22 | Jinja2==2.10.1 23 | lxml==4.3.3 24 | MarkupSafe==1.1.1 25 | multidict==4.5.2 26 | packaging==19.0 27 | property==2.2 28 | pycparser==2.19 29 | Pygments==2.3.1 30 | pyparsing==2.4.0 31 | pytz==2019.1 32 | requests==2.22.0 33 | scapy==2.4.2 34 | shodan==1.12.1 35 | six==1.12.0 36 | snowballstemmer==1.2.1 37 | Sphinx==2.0.1 38 | sphinxcontrib-applehelp==1.0.1 39 | sphinxcontrib-devhelp==1.0.1 40 | sphinxcontrib-htmlhelp==1.0.2 41 | sphinxcontrib-jsmath==1.0.1 42 | sphinxcontrib-qthelp==1.0.2 43 | sphinxcontrib-serializinghtml==1.1.3 44 | sphinxcontrib-websupport==1.1.0 45 | urllib3>=1.24.2 46 | uvloop==0.12.2 47 | XlsxWriter==1.1.6 48 | yarl==1.3.0 49 | -------------------------------------------------------------------------------- /ros2_entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source /opt/ros/dashing/setup.bash 3 | /usr/local/bin/aztarna $@ 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import sys 5 | 6 | from setuptools import setup 7 | 8 | setup( 9 | name='aztarna', 10 | version='1.2.2', 11 | packages=[ 12 | 'aztarna', 13 | 'aztarna.ros', 14 | 'aztarna.ros.ros', 15 | 'aztarna.ros.sros', 16 | 'aztarna.ros.industrial', 17 | 'aztarna.ros.ros2', 18 | 'aztarna.industrialrouters', 19 | ], 20 | url='https://www.aliasrobotics.com/research/aztarna.htm', 21 | project_urls={ 22 | 'Source Code': 'https://github.com/aliasrobotics/aztarna' 23 | }, 24 | license='GPLv3', 25 | author='Alias Robotics', 26 | author_email='contact@aliasrobotics.com', 27 | description='A footprinting tool for ROS and SROS systems', 28 | long_description='''Aztarna, a footprinting tool for robots. 29 | Provides researchers a way for researching internet connected ROS, SROS robots, as well as industrial routers. 30 | 31 | Alias Robotics supports original robot manufacturers assessing their security and improving their quality of software. 32 | By no means we encourage or promote the unauthorized tampering with running robotic systems. 33 | This can cause serious human harm and material damages. 34 | ''', 35 | keywords=['network', 'footprinting', 'ros', 'sros', 'ics', 'industrialrouters'], 36 | entry_points = { 37 | 'console_scripts': ['aztarna=aztarna.cmd:main'], 38 | }, 39 | install_requires=[ 40 | 'aiohttp==3.5.4', 41 | 'aiohttp-xmlrpc==0.7.4', 42 | 'alabaster==0.7.12', 43 | 'argcomplete==1.9.5', 44 | 'asn1crypto==0.24.0', 45 | 'async-timeout==3.0.1', 46 | 'attrs==19.1.0', 47 | 'Babel==2.6.0', 48 | 'certifi==2019.3.9', 49 | 'cffi==1.12.2', 50 | 'chardet==3.0.4', 51 | 'Click==7.0', 52 | 'click-plugins==1.1.1', 53 | 'colorama==0.4.1', 54 | 'cryptography==2.6.1', 55 | 'dnspython==1.16.0', 56 | 'docutils==0.14', 57 | 'idna==2.8', 58 | 'idna-ssl==1.1.0', 59 | 'imagesize==1.1.0', 60 | 'ipwhois==1.1.0', 61 | 'Jinja2==2.10.1', 62 | 'lxml==4.3.3', 63 | 'MarkupSafe==1.1.1', 64 | 'multidict==4.5.2', 65 | 'packaging==19.0', 66 | 'property==2.2', 67 | 'pycparser==2.19', 68 | 'Pygments==2.3.1', 69 | 'pyparsing==2.4.0', 70 | 'pytz==2019.1', 71 | 'requests==2.22.0', 72 | 'scapy==2.4.2', 73 | 'shodan==1.12.1', 74 | 'six==1.12.0', 75 | 'snowballstemmer==1.2.1', 76 | 'Sphinx==2.0.1', 77 | 'sphinxcontrib-applehelp==1.0.1', 78 | 'sphinxcontrib-devhelp==1.0.1', 79 | 'sphinxcontrib-htmlhelp==1.0.2', 80 | 'sphinxcontrib-jsmath==1.0.1', 81 | 'sphinxcontrib-qthelp==1.0.2', 82 | 'sphinxcontrib-serializinghtml==1.1.3', 83 | 'sphinxcontrib-websupport==1.1.0', 84 | 'urllib3>=1.24.2', 85 | 'uvloop==0.12.2', 86 | 'XlsxWriter==1.1.6', 87 | 'yarl==1.3.0', 88 | 'pyshark==0.4.2.9', 89 | ], 90 | include_package_data=True 91 | ) 92 | --------------------------------------------------------------------------------