pax_global_header00006660000000000000000000000064132637160270014521gustar00rootroot0000000000000052 comment=f0d49c258f11f1b6fa49a0c227235b608470f023 dhcpcanon-0.8.5/000077500000000000000000000000001326371602700134705ustar00rootroot00000000000000dhcpcanon-0.8.5/.coveragerc000066400000000000000000000004211326371602700156060ustar00rootroot00000000000000[run] branch = True source = dhcpcanon parallel = True [paths] source = dhcpcanon .tox/*/lib/python*/site-packages/dhcpcanon .tox/*/lib/site-packages/dhcpcanon .tox/pypy*/site-packages/dhcpcanon [report] omit = */tests/* */python?.?/* *__init__* dhcpcanon-0.8.5/.editorconfig000066400000000000000000000005641326371602700161520ustar00rootroot00000000000000# this should work for all editors that support .editorconfig! # # on debian, emacs users should install elpa-editorconfig and vim # users should install vim-editorconfig. root = true [*] indent_style = space insert_final_newline = true trim_trailing_whitespace = true end_of_line = lf charset = utf-8 indent_size = 4 max_line_length = 78 [Makefile] indent_style = tab dhcpcanon-0.8.5/.gitignore000066400000000000000000000002601326371602700154560ustar00rootroot00000000000000*~ *.pyc .*swp *.egg-info/ deb_dist dist build __pycache__ docs/build docs/source/images/*.dot docs/source/images/*.pdf docs/source/images/*.dia .cache .tox .coverage* htmlcov dhcpcanon-0.8.5/.readthedocs.yml000066400000000000000000000002341326371602700165550ustar00rootroot00000000000000build: image: latest python: version: 3.5 # pip_install: true # extra_requirements: # - doc requirements_file: requirements_docs.txt dhcpcanon-0.8.5/.travis.yml000066400000000000000000000022071326371602700156020ustar00rootroot00000000000000language: python sudo: required dist: trusty python: - '3.5' before_install: - sudo apt update && sudo apt install -y inkscape libdbus-glib-1-dev # - echo "Python diagnostic Information" # - env # - sudo dpkg -l|grep python|grep dev # - 'which -a python' # - python --version # - python-config --includes # - ls -l ~/virtualenv install: - pip install "dhcpcanon[dev, test, doc]" env: global: # The dbus-python maintainer hit these bugs on travis, see his comments on the # travis bug report: https://github.com/travis-ci/travis-ci/issues/6530 # See https://github.com/mitya57/secretstorage/blob/2636a47b45aff51a21dfaf1cbd9f1f3c1347f7f4/.travis.yml for an example workaround # This is also a known issue and filed on the python bugtracker: https://bugs.python.org/issue7352 - 'PYTHON_LIBS="-L$(python-config --prefix)/lib $(python-config --libs)"' - PY_ENABLE_SHARD=0 - TOX_ENV=lint - TOX_ENV=py35,stats - TOX_ENV=doc script: - tox -c tox.ini -e $TOX_ENV # this is already in tox # - coverage run --source=dhcpcanon setup.py test after_success: - coveralls - codecov dhcpcanon-0.8.5/AUTHORS000066400000000000000000000000361326371602700145370ustar00rootroot00000000000000juga (juga at riseup dot net) dhcpcanon-0.8.5/CHANGELOG.rst000066400000000000000000000006771326371602700155230ustar00rootroot00000000000000Change Log =========== 0.3.1 ------ * Restore self.my_send for sendp: my_send uses L3Socket, my_send L2Socket * conf.checkIPaddr does not have effect * Remove forgotten GPL license text * Add changelog * Fix travis style and dependencies for documentation * Fix running documentation 0.3.0 ------ Multiple changes and refactoring. * Python 3 compatible * Add tests * Improve docstrings * Update documentation and diagrams v0.1.8.2 --------- dhcpcanon-0.8.5/CONTRIBUTING.md000066400000000000000000000037601326371602700157270ustar00rootroot00000000000000Contributing to `dhcpcanon` =========================== We welcome contributions of any kind (ideas, code, tests, documentation, examples, ...). General contribution guidelines ------------------------------- - Any non-trivial change should contain tests. - All the functions and methods should contain Sphinx docstrings which are used to generate the API documentation. Code style guide ---------------- - We follow [PEP8 Python Style Guide](http://www.python.org/dev/peps/pep-0008/) - Use 4 spaces for a tab - Use 79 characters in a line - Make sure edited file doesn't contain any trailing whitespace - You can verify that your modifications don't break any rules by running the `flake8` script - e.g. `flake8 dhcpcanon/edited_file.py` or `tox -e style`. Second command will run flake8 on all the files in the repository. And most importantly, follow the existing style in the file you are editing and **be consistent**. Docstring conventions --------------------- For documenting the API we we use Sphinx and reStructuredText syntax. Contribution workflow --------------------- ### 1. Open a new issue on our issue tracker Go to our [issue tracker](https://github.com/juga0/dhcpcanon/issues) and open a new issue for your changes there. ### 2. Fork our Github repository Fork our [Github git repository](https://github.com/juga0/dhcpcanon). Your fork will be used to hold your changes. ### 3. Create a new branch for your changes For example: ### 4. Make your changes Commit often and rebase master ### 5. Write tests for your changes and make sure all the tests pass Make sure that all the code you have added or modified has appropriate test coverage. Also make sure all the tests including the existing ones still pass using `tox` ### 6. Open a Pull request You can then push your feature branch to your remote and open a pull request. > **note** > > Partly copied from [libcloud > contributing](https://libcloud.readthedocs.io/en/latest/development.html#contributing) dhcpcanon-0.8.5/LICENSE000066400000000000000000000020641326371602700144770ustar00rootroot00000000000000Copyright 2016, 2017, juga Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. dhcpcanon-0.8.5/MANIFEST.in000066400000000000000000000002061326371602700152240ustar00rootroot00000000000000include *.md include *.rst include AUTHORS LICENSE requirements.txt recursive-include docs * prune docs/build recursive-include man * dhcpcanon-0.8.5/Makefile000066400000000000000000000101261326371602700151300ustar00rootroot00000000000000# Makefile for a source distribution of dhcpcanon. # # This package is not self-contained and the build products may require other # dependencies to function; it is given as a reference for distro packagers. PACKAGE = dhcpcanon VERSION = $(shell sh version.sh) DESTDIR = THISFILE = $(lastword $(MAKEFILE_LIST)) PYTHON = python3 # GNU command variables # see http://www.gnu.org/prep/standards/html_node/Command-Variables.html INSTALL = install INSTALL_DATA = $(INSTALL) -m 644 INSTALL_PROGRAM = $(INSTALL) INSTALL_SCRIPT = $(INSTALL) # GNU directory variables # see http://www.gnu.org/prep/standards/html_node/Directory-Variables.html prefix = /usr/local exec_prefix = $(prefix) sbindir = $(exec_prefix)/sbin datarootdir = $(prefix)/share datadir = $(datarootdir) sysconfdir = $(prefix)/etc docdir = $(datarootdir)/doc/$(PACKAGE) mandir = $(datarootdir)/man man8dir = $(mandir)/man8 # for systemd tmpfilesdir=/usr/lib/tmpfiles.d systemunitdir=/lib/systemd/system # for systemd udev networkdir=/lib/systemd/network # for apparmor apparmordir=/etc/apparmor.d srcdir = . SRC_MAN8 = man/dhcpcanon.8 SRC_SCRIPT = sbin/dhcpcanon-script SRC_DOC = README.md LICENSE SRC_TMPFILES = tmpfiles.d/dhcpcanon.conf SRC_UNITFILE = systemd/dhcpcanon.service SRC_APPARMOR = apparmor.d/sbin.dhcpcanon SRC_LINKFILE = systemd/network/90-dhcpcanon.link SRC_ALL = $(SRC_SCRIPT) $(SRC_DOC) $(SRC_MAN8) DST_MAN8 = $(SRC_MAN8) DST_SCRIPT = $(SRC_SCRIPT) DST_DOC = $(SRC_DOC) DST_TMPFILES = $(SRC_TMPFILES) DST_UNITFILE = $(SRC_UNITFILE) DST_APPARMOR = $(SRC_APPARMOR) DST_LINKFILE = $(SRC_LINKFILE) DST_ALL = $(DST_SCRIPT) $(DST_DOC) $(DST_MAN8) TEST_PY = dhcpcanon-test.py all: $(DST_ALL) $(THISFILE) install: all @echo $@ mkdir -p $(DESTDIR)$(sbindir) for i in $(DST_SCRIPT); do $(INSTALL_SCRIPT) "$$i" /sbin; done mkdir -p $(DESTDIR)$(docdir) for i in $(DST_DOC); do $(INSTALL_DATA) "$$i" $(DESTDIR)$(docdir); done mkdir -p $(DESTDIR)$(man8dir) for i in $(DST_MAN8); do $(INSTALL_DATA) "$$i" $(DESTDIR)$(man8dir); done $(PYTHON) setup.py install --record installed.txt $(if $(DESTDIR),--root=$(DESTDIR),--install-scripts=/sbin) if [ -n "$(WITH_SYSTEMD)" ]; then \ adduser --system dhcpcanon; \ mkdir -p $(DESTDIR)$(systemunitdir); \ for i in $(DST_UNITFILE); do $(INSTALL_DATA) "$$i" $(DESTDIR)$(systemunitdir); done; \ mkdir -p $(DESTDIR)$(tmpfilesdir); \ for i in $(DST_TMPFILES); do $(INSTALL_DATA) "$$i" $(DESTDIR)$(tmpfilesdir); done; \ systemctl enable $(DESTDIR)$(systemunitdir)/dhcpcanon.service; \ systemd-tmpfiles --create --root=$(DESTDIR)$(tmpfilesdir)/dhcpcanon.conf; \ systemctl start $(DESTDIR)$(systemunitdir)/dhcpcanon.service; \ systemctl status $(DESTDIR)$(systemunitdir)/dhcpcanon.service; \ fi if [ -n "$(WITH_SYSTEMD_UDEV)" ]; then \ mkdir -p $(DESTDIR)$(networkdir); \ for i in $(DST_LINKFILE); do $(INSTALL_DATA) "$$i" $(DESTDIR)$(networkdir); done; \ fi if [ -n "$(WITH_APPARMOR)" ]; then \ mkdir -p $(DESTDIR)$(apparmordir); \ for i in $(DST_APPARMOR); do $(INSTALL_DATA) "$$i" $(DESTDIR)$(apparmordir); done; \ for i in $(DST_APPARMOR); do aa-complain $(DESTDIR)$(apparmordir)/"$$i"; done; \ fi uninstall: @echo $@ for i in $(notdir $(DST_SCRIPT)); do rm -f $(DESTDIR)$(sbindir)/"$$i"; done for i in $(notdir $(DST_DOC)); do rm -f $(DESTDIR)$(docdir)/"$$i"; done for i in $(notdir $(DST_MAN8)); do rm -f $(DESTDIR)$(man8dir)/"$$i"; done # it will only work in the case that the file has not been removed cat installed.txt | xargs rm -rf # systemd files for i in $(notdir $(DST_UNITFILE)); do rm -f $(DESTDIR)$(systemunitdir)/"$$i"; done for i in $(notdir $(DST_TMPFILES)); do rm -f $(DESTDIR)$(tmpfilesdir)/"$$i"; done for i in $(notdir $(DST_APPARMOR)); do rm -f $(DESTDIR)$(apparmordir)/"$$i"; done for i in $(notdir $(DST_LINKFILE)); do rm -f $(DESTDIR)$(networkdir)/"$$i"; done clean: python setup.py clean rm -rf *.pyc build dist dhcpcanon.egg-info distclean: clean maintainer-clean: distclean rm -f $(DST_MAN8) pylint: $(SRC_SCRIPT) pylint -E $^ check: $(THISFILE) for i in $(TEST_PY); do $(PYTHON) "$$i"; done .PHONY: all install uninstall clean distclean maintainer-clean check pylint dhcpcanon-0.8.5/README.rst000066400000000000000000000112631326371602700151620ustar00rootroot00000000000000dhcpcanon - DHCP anonymity profile ================================== |PyPI| |Build Status| |Coverage Status| |Documentation status| |CII Best Practices| DHCP client disclosing less identifying information. Python implementation of the DHCP Anonymity Profile (`RFC7844 `__) designed for users that wish to remain anonymous to the visited network minimizing disclosure of identifying information. Technologies ------------ This implementation uses the Python `Scapy Automata `__ What is the Anonymity Profile? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ As the RFC7844 stats: Some DHCP options carry unique identifiers. These identifiers can enable device tracking even if the device administrator takes care of randomizing other potential identifications like link-layer addresses or IPv6 addresses. The anonymity profiles are designed for clients that wish to remain anonymous to the visited network. The profiles provide guidelines on the composition of DHCP or DHCPv6 messages, designed to minimize disclosure of identifying information. What is DHCP? ~~~~~~~~~~~~~ From `Wikipedia `__: The **Dynamic Host Configuration Protocol** (**DHCP**) is a standardized `network protocol `__ used on `Internet Protocol `__ (IP) networks. The DHCP is controlled by a DHCP server that dynamically distributes network configuration parameters, such as `IP addresses `__, for interfaces and services. A `router `__ or a `residential gateway `__ can be enabled to act as a DHCP server. A DHCP server enables computers to request IP addresses and networking parameters automatically, reducing the need for a `network administrator `__ or a user to configure these settings manually. In the absence of a DHCP server, each computer or other device (eg., a printer) on the network needs to be statically (ie., manually) assigned to an IP address. Documentation ------------- A more extensive online documentation is available in `Read the docs `__. The documentation source is in `this repository `__. Visit `DHCPAP `__ for an overview of all the repositories related to the RFC7844 implementation work. Installation ------------ See `Installation `__ and `Running `__ Download -------- You can download this project in either `zip `__ or `tar `__ formats. You can also clone the project with Git by running: :: git clone https://github.com/juga0/dhcpcanon Bugs and features ----------------- If you wish to signal a bug or report a feature request, please fill-in an issue on the `dhcpcanon issue tracker `__. Current status -------------- WIP, still not recommended for end users. Testers welcomed. See `TODO <./docs/source/todo.rst>`__ License ------- ``dhcpcanon`` is copyright 2016-2018 by juga ( juga at riseup dot net) and is licensed by the terms of the MIT license. Acknowledgments --------------- To all the persons that have given suggestions and comments about this implementation, the authors of the `RFC 7844 `__, the `Prototype Fund Project `__ of the `Open Knowledge Foundation Germany `__ and the `Federal Ministry of Education and Research `__ who partially funds this work. .. |PyPI| image:: https://img.shields.io/pypi/v/dhcpcanon.svg :target: https://pypi.python.org/pypi/dhcpcanon .. |Build Status| image:: https://www.travis-ci.org/juga0/dhcpcanon.svg?branch=master :target: https://www.travis-ci.org/juga0/dhcpcanon .. |Coverage Status| image:: https://coveralls.io/repos/github/juga0/dhcpcanon/badge.svg?branch=master :target: https://coveralls.io/github/juga0/dhcpcanon?branch=master .. |Documentation Status| image:: https://readthedocs.org/projects/dhcpcanon/badge/?version=latest :target: http://dhcpcanon.readthedocs.io/en/latest/?badge=latest .. |CII Best Practices| image:: https://bestpractices.coreinfrastructure.org/projects/1020/badge :target: https://bestpractices.coreinfrastructure.org/projects/1020 dhcpcanon-0.8.5/apparmor.d/000077500000000000000000000000001326371602700155335ustar00rootroot00000000000000dhcpcanon-0.8.5/apparmor.d/sbin.dhcpcanon000066400000000000000000000065011326371602700203470ustar00rootroot00000000000000# vim:syntax=apparmor # Last Modified: Fri Jul 17 11:46:19 2009 # Author: Jamie Strandboge # Modified for dhcpcanon by: juga , 2017. #include /sbin/dhcpcanon flags=(attach_disconnected) { #include #include #include capability net_bind_service, capability net_raw, capability sys_module, capability dac_override, capability net_admin, network packet, network raw, @{PROC}/[0-9]*/net/ r, @{PROC}/[0-9]*/net/** r, /sbin/dhcpcanon mr, # LP: #1197484 and LP: #1202203 - why is this needed? :( /bin/bash mr, /etc/dhcpcanon.conf r, /etc/dhcp/ r, /etc/dhcp/** r, /var/lib/dhcp{,3}/dhcpcanon* lrw, /{,var/}run/dhcpcanon*.pid lrw, /{,var/}run/dhcpcanon*.lease* lrw, # NetworkManager /{,var/}run/nm*conf r, /{,var/}run/sendsigs.omit.d/network-manager.dhcpcanon*.pid lrw, /var/lib/NetworkManager/dhcpcanon*.conf lrw, /var/lib/NetworkManager/dhcpcanon*.lease* lrw, signal (receive) peer=/usr/sbin/NetworkManager, ptrace (readby) peer=/usr/sbin/NetworkManager, # connman /{,var/}run/connman/dhcpcanon*.pid lrw, /{,var/}run/connman/dhcpcanon*.leases lrw, # synce-hal /usr/share/synce-hal/dhcpcanon.conf r, # if there is a custom script, let it run unconfined /etc/dhcp/dhcpcanon-script Uxr, # The dhcpcanon-script shell script sources other shell scripts rather than # executing them, so we can't just use a separate profile for dhcpcanon-script # with 'Uxr' on the hook scripts. However, for the long-running dhcpcanon3 # daemon to run arbitrary code via /sbin/dhcpcanon-script, it would need to be # able to subvert dhcpcanon-script or write to the hooks.d directories. As # such, if the dhcpcanon3 daemon is subverted, this effectively limits it to # only being able to run the hooks scripts. /sbin/dhcpcanon-script Uxr, # Run the ELF executables under their own unrestricted profiles /usr/lib/NetworkManager/nm-dhcp-client.action Pxrm, /usr/lib/connman/scripts/dhcpcanon-script Pxrm, # Support the new executable helper from NetworkManager. /usr/lib/NetworkManager/nm-dhcp-helper Pxrm, signal (receive) peer=/usr/lib/NetworkManager/nm-dhcp-helper, # Site-specific additions and overrides. See local/README for details. #include } /usr/lib/NetworkManager/nm-dhcp-client.action { #include #include /usr/lib/NetworkManager/nm-dhcp-client.action mr, /var/lib/NetworkManager/*lease r, signal (receive) peer=/usr/sbin/NetworkManager, ptrace (readby) peer=/usr/sbin/NetworkManager, network inet dgram, network inet6 dgram, } /usr/lib/NetworkManager/nm-dhcp-helper { #include #include /usr/lib/NetworkManager/nm-dhcp-helper mr, /run/NetworkManager/private-dhcp rw, signal (send) peer=/sbin/dhcpcanon, /var/lib/NetworkManager/*lease r, signal (receive) peer=/usr/sbin/NetworkManager, ptrace (readby) peer=/usr/sbin/NetworkManager, network inet dgram, network inet6 dgram, } /usr/lib/connman/scripts/dhcpcanon-script { #include #include /usr/lib/connman/scripts/dhcpcanon-script mr, network inet dgram, network inet6 dgram, } dhcpcanon-0.8.5/dhcpcanon/000077500000000000000000000000001326371602700154255ustar00rootroot00000000000000dhcpcanon-0.8.5/dhcpcanon/__init__.py000066400000000000000000000040511326371602700175360ustar00rootroot00000000000000# -*- coding: utf-8 -*- # vim:ts=4:sw=4:expandtab 2 # Copyright 2016, 2017 juga (juga at riseup dot net), MIT license. """__init__ for the DHCP client implementation of the Anonymity Profile ([:rfc:`7844`]).""" from __future__ import absolute_import try: from ._version import version except ImportError: try: from setuptools_scm import get_version version = get_version() except (ImportError, LookupError): version = "0.8.5" __version__ = version __author__ = "juga" __author_mail__ = "juga@riseup.net" __description__ = "DHCP client disclosing less identifying information" __long_description__ = "Python implmentation of the DHCP Anonymity Profiles \ (RFC7844) designed for users that \ wish to remain anonymous to the visited network \ minimizing disclosure of identifying information." __website__ = 'https://github.com/juga0/dhcpcanon' __documentation__ = 'http://dhcpcanon.readthedocs.io/en/' + __version__ __authors__ = [] __copyright__ = """Copyright (C) 2016 This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions. For details see the COPYRIGHT file distributed along this program.""" __license__ = """ This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or any later version. This package is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this package. If not, see . """ __all__ = ('clientscript', 'conflog', 'dhcpcapfsm', 'dhcpcaplease', 'dhcpcaputils', 'timers', 'constants', 'dhcpcap') dhcpcanon-0.8.5/dhcpcanon/_version.py000066400000000000000000000002121326371602700176160ustar00rootroot00000000000000# -*- coding: utf-8 -*- # vim:ts=4:sw=4:expandtab 2 # Copyright 2016, 2017 juga (juga at riseup dot net), MIT license. version = "0.8.5" dhcpcanon-0.8.5/dhcpcanon/clientscript.py000066400000000000000000000056241326371602700205110ustar00rootroot00000000000000# -*- coding: utf-8 -*- # vim:ts=4:sw=4:expandtab 2 # Copyright 2016, 2017 juga (juga at riseup dot net), MIT license. """Class to Initialize and call external script.""" from __future__ import absolute_import, unicode_literals import logging import os import subprocess import attr from .constants import (ENV_OPTIONS_REQ, LEASEATTRS2ENVKEYS, LEASEATTRS_SAMEAS_ENVKEYS, SCRIPT_ENV_KEYS, SCRIPT_PATH, STATES2REASONS) logger = logging.getLogger('dhcpcanon') @attr.s class ClientScript(object): """Simulates the behaviour of the isc-dhcp client-script or nm-dhcp-helper. `client-script `_ or `nm-dhcp-helper `_. """ scriptname = attr.ib(default=None) env = attr.ib(default=attr.Factory(dict)) def __attrs_post_init__(self, scriptfile=None, env=None): """.""" logger.debug('Modifying ClientScript obj after creating it.') self.scriptname = self.scriptname or scriptfile or SCRIPT_PATH if env is None: self.env = dict.fromkeys(SCRIPT_ENV_KEYS, str('')) else: self.env = env self.env['medium'] = str() self.env['pid'] = str(os.getpid()) def script_init(self, lease, state, prefix='', medium=''): """Initialize environment to pass to the external script.""" logger.debug('self.scriptname %s', self.scriptname) if self.scriptname is not None: logger.debug('Modifying ClientScript obj, setting env.') if isinstance(state, int): reason = STATES2REASONS[state] else: reason = state self.env['reason'] = str(reason) self.env['medium'] = self.env.get('medium') or str(medium) self.env['client'] = str('dhcpcanon') self.env['pid'] = str(os.getpid()) for k in LEASEATTRS_SAMEAS_ENVKEYS: self.env[k] = str(lease.__getattribute__(k)) for k, v in LEASEATTRS2ENVKEYS.items(): self.env[v] = str(lease.__getattribute__(k)) self.env.update(ENV_OPTIONS_REQ) else: logger.debug('There is not script path.') def script_go(self, scriptname=None, env=None): """Run the external script.""" scriptname = self.scriptname or scriptname if scriptname is not None: env = self.env or env logger.info('Calling script %s', scriptname) logger.info('with env %s', env) proc = subprocess.Popen([scriptname], env=env, stderr=subprocess.STDOUT) try: (stdout, stderr) = proc.communicate() return True except TypeError as e: logger.error(e) return False dhcpcanon-0.8.5/dhcpcanon/conflog.py000066400000000000000000000040431326371602700174270ustar00rootroot00000000000000# -*- coding: utf-8 -*- # vim:ts=4:sw=4:expandtab # Copyright 2016, 2017 juga (juga at riseup dot net), MIT license. """Logging configuration.""" import logging import sys LOGGING = { 'version': 1, 'disable_existing_loggers': True, 'formatters': { 'verbose': { 'format': '%(asctime)s %(levelname)s' ' %(filename)s:%(lineno)s -' ' %(funcName)s - %(message)s', 'datefmt': '%Y-%m-%d %H:%M:%S' }, 'simple': { 'format': "%(message)s", }, 'sys': { 'format': "%(module)s[%(process)s]: " "%(message)s" } }, 'handlers': { 'stdout': { 'class': 'logging.StreamHandler', 'stream': sys.stdout, 'formatter': 'verbose', 'level': 'DEBUG', }, 'stdoutscapy': { 'class': 'logging.StreamHandler', 'stream': sys.stdout, 'formatter': 'simple', 'level': 'DEBUG', }, 'syslog': { 'class': 'logging.handlers.SysLogHandler', 'address': '/dev/log', 'formatter': 'sys', 'level': 'INFO', }, }, 'loggers': { 'dhcpcanon': { 'handlers': ['syslog', 'stdout'], 'level': logging.INFO, 'propagate': False }, "scapy": { 'handlers': ['stdoutscapy'], 'level': logging.DEBUG, 'propagate': False }, # next ones set to ERROR to disable # WARNING: Failed to execute tcpdump. "scapy.interactive": { 'handlers': ['stdoutscapy'], 'level': logging.ERROR, 'propagate': False }, "scapy.runtime": { 'handlers': ['stdoutscapy'], 'level': logging.ERROR, 'propagate': False }, "scapy.loading": { 'handlers': ['stdoutscapy'], 'level': logging.ERROR, 'propagate': False } } } dhcpcanon-0.8.5/dhcpcanon/constants.py000066400000000000000000000137101326371602700200150ustar00rootroot00000000000000# -*- coding: utf-8 -*- # vim:ts=4:sw=4:expandtab 2 # Copyright 2016, 2017 juga (juga at riseup dot net), MIT license. """Constants for the DHCP client implementation of the Anonymity Profile ([:rfc:`7844`]).""" import logging logger = logging.getLogger(__name__) DT_PRINT_FORMAT = '%y-%m-%d %H:%M:%S' # client ########## BROADCAST_MAC = 'ff:ff:ff:ff:ff:ff' META_MAC = '00:00:00:00:00:00' BROADCAST_ADDR = '255.255.255.255' META_ADDR = '0.0.0.0' CLIENT_PORT = 68 SERVER_PORT = 67 # DHCP timers ########## LEASE_TIME = 1209600 # 14 DAYS RENEWING_TIME = 604800 # 7 DAYS REBINDING_TIME = 1058400 # 12 DAYS DELAY_SELECTING = 10 TIMEOUT_SELECTING = 60 TIMEOUT_REQUESTING = 60 TIMEOUT_REQUEST_RENEWING = 226800 TIMEOUT_REQUEST_REBINDING = 75600 MAX_DELAY_SELECTING = 10 RENEW_PERC = 0.5 REBIND_PERC = 0.875 # DHCP number packet retransmissions MAX_ATTEMPTS_DISCOVER = 5 MAX_OFFERS_COLLECTED = 1 MAX_ATTEMPTS_REQUEST = 5 # DHCP packet ############## DHCP_OFFER_OPTIONS = [ 'server_id', 'subnet_mask', 'broadcast_address', 'router', 'domain', 'name_server', 'lease_time', 'renewal_time', 'rebinding_time'] # DHCP FSM ############# STATE_ERROR = -1 STATE_PREINIT = 0 STATE_INIT = 1 STATE_SELECTING = 2 STATE_REQUESTING = 3 STATE_BOUND = 4 STATE_RENEWING = 5 STATE_REBINDING = 6 STATE_END = 7 STATES2NAMES = { STATE_ERROR: 'ERROR', STATE_PREINIT: 'PREINIT', STATE_INIT: 'INIT', STATE_SELECTING: 'SELECTING', STATE_REQUESTING: 'REQUESTING', STATE_BOUND: 'BOUND', STATE_RENEWING: 'RENEWING', STATE_REBINDING: 'REBINDING', STATE_END: 'END', } # NM integration ##################### REASONS_NM = ['bound', 'renew', 'rebind', 'expiry', 'fail', 'timeout', 'nak', 'end', 'abend'] REASONS_CL = ['MEDIUM', 'PREINIT', 'BOUND', 'RENEW', 'REBIND', 'REBOOT', 'EXPIRE', 'FAIL', 'STOP', 'RELEASE', 'NBI', 'TIMEOUT'] STATES2REASONS = { STATE_PREINIT: 'PREINIT', STATE_INIT: 'INIT', STATE_SELECTING: 'SELECTING', STATE_BOUND: 'BOUND', STATE_END: 'END', STATE_REBINDING: 'REBIND', STATE_RENEWING: 'RENEW', STATE_ERROR: "FAIL", # NOTE: there could be implemented a way toknow the reason for failure so # that it can be passed to NetworkManager, as dhclient does, ie: # "STOP", "EXPIRE" } # systemd events DHCP_EVENTS = { 'STOP': 0, 'IP_ACQUIRE': 1, 'IP_CHANGE': 2, 'EXPIRED': 3, 'RENEW': 4, } SCRIPT_ENV_KEYS = ['reason', 'medium', 'interface', # 'client', 'pid', 'new_ip_address', 'new_subnet_mask', 'new_network_number', 'new_domain_name_servers', 'new_domain_name', 'new_routers', 'new_broadcast_address', 'new_next_server', 'new_dhcp_server_id'] LEASEATTRS_SAMEAS_ENVKEYS = ['interface'] # , 'reason'] # 'client', 'pid', # these are not set as environment but put in lease file # , 'rebind', 'renew', 'expiry' LEASEATTRS2ENVKEYS = { 'address': 'new_ip_address', 'subnet_mask': 'new_subnet_mask', 'broadcast_address': 'new_broadcast_address', 'next_server': 'new_next_server', 'server_id': 'new_server_id', 'network': 'new_network_number', 'domain': 'new_domain_name', 'name_server': 'new_domain_name_servers', 'router': 'new_routers', } LEASE_ATTRS2LEASE_FILE = { 'interface': 'interface', 'address': 'fixed-address', 'subnet_mask': 'option subnet-mask', 'broadcast_address': 'option broadcast-address', 'domain': 'option domain-name', 'name_server': 'option domain-name-servers', 'router': 'option routers', 'lease_time': 'option dhcp-lease-time', 'rebinding_time': 'option dhcp-rebinding-time', 'renewal_time': 'option dhcp-renewal-time', 'server_id': 'option dhcp-server-identifier', 'renew': 'renew', 'rebind': 'rebind', 'expiry': 'expiry', } LEASE_ATTRS2LEASE_LOG = { 'interface': 'interface', 'subnet_mask': 'option subnet_mask', 'broadcast_address': 'option broadcast_address', 'address': 'ip_address', 'router': 'option routers', 'domain': 'option domain_name', 'name_server': 'option domain_name_servers', 'lease_time': 'option dhcp_lease_time', 'renewal_time': 'option dhcp_renewal_time', 'rebinding_time': 'option dhcp_rebinding_time', 'server_id': 'option dhcp_server_identifier', 'expiry': 'expiry', } ENV_OPTIONS_REQ = { 'requested_subnet_mask': '1', 'requested_router': '1', 'requested_domain_name_server': '1', 'requested_domain_name': '1', 'requested_router_discovery': '1', 'requested_static_route': '1', 'requested_vendor_specific': '1', 'requested_netbios_nameserver': '1', 'requested_netbios_node_type': '1', 'requested_netbios_scope': '1', 'requested_classless_static_route_option': '1', 'requested_private_classless_static_route': '1', 'requested_private_proxy_autodiscovery': '1', } PRL = b"\x01\x03\x06\x0f\x1f\x21\x2b\x2c\x2e\x2f\x79\xf9\xfc" """ SD_DHCP_OPTION_SUBNET_MASK = 1 SD_DHCP_OPTION_ROUTER = 3 SD_DHCP_OPTION_DOMAIN_NAME_SERVER = 6 SD_DHCP_OPTION_DOMAIN_NAME = 15 SD_DHCP_OPTION_ROUTER_DISCOVER = 31 SD_DHCP_OPTION_STATIC_ROUTE = 33 SD_DHCP_OPTION_VENDOR_SPECIFIC = 43 SD_DHCP_OPTION_NETBIOS_NAMESERVER = 44 SD_DHCP_OPTION_NETBIOS_NODETYPE = 46 SD_DHCP_OPTION_NETBIOS_SCOPE = 47 SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121 SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE = 249 SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY = 252 """ FSM_ATTRS = ['request_attempts', 'discover_attempts', 'script', 'time_sent_request', 'current_state', 'client'] XID_MIN = 1 XID_MAX = 900000000 SCRIPT_PATH = '/sbin/dhcpcanon-script' PID_PATH = '/var/run/dhcpcanon.pid' LEASE_PATH = '/var/lib/dhcp/dhcpcanon.leases' CONF_PATH = '/etc/dhcp/dhcpcanon.conf' RESOLVCONF = '/sbin/resolvconf' RESOLVCONF_ADMIN = '/usr/bin/resolvconf-admin' dhcpcanon-0.8.5/dhcpcanon/dhcpcanon.py000077500000000000000000000065601326371602700177460ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # vim:ts=4:sw=4:expandtab 2 # Copyright 2016, 2017 juga (juga at riseup dot net), MIT license. """DCHP client implementation of the anonymity profile (RFC7844).""" import argparse import logging import logging.config from lockfile.pidlockfile import (PIDLockFile, AlreadyLocked, LockTimeout, LockFailed) from scapy.config import conf # in python3 this seems to be the only way to to disable: # WARNING: Failed to execute tcpdump. conf.logLevel = logging.ERROR from . import __version__ from .conflog import LOGGING from .constants import (CLIENT_PORT, SERVER_PORT, SCRIPT_PATH, PID_PATH) from .dhcpcapfsm import DHCPCAPFSM logging.config.dictConfig(LOGGING) logger = logging.getLogger('dhcpcanon') def main(): parser = argparse.ArgumentParser() parser.add_argument('interface', nargs='?', help='interface to configure with DHCP') parser.add_argument('-v', '--verbose', help='Set logging level to debug', action='store_true') parser.add_argument('--version', action='version', help='version', version='%(prog)s ' + __version__) parser.add_argument('-s', '--delay_selecting', help='Selecting starts after a ramdon delay.', action='store_true') # options to looks like dhclient parser.add_argument( '-sf', metavar='script-file', nargs='?', const=SCRIPT_PATH, help='Path to the network configuration script invoked by ' 'dhcpcanon when it gets a lease. Without this option ' 'dhcpcanon will configure the network by itself.' 'If unspecified, the ' 'default /sbin/dhcpcanon-script is used, which is a copy of' 'dhclient-script(8) for a description of this file.' 'If dhcpcanon is running with NetworkManager, it will' 'be called with the script nm-dhcp-helper.') parser.add_argument( '-pf', metavar='pid-file', nargs='?', const=PID_PATH, help='Path to the process ID file. If unspecified, the' 'default /var/run/dhcpcanon.pid is used. ' 'This option is used by NetworkManager to check whether ' 'dhcpcanon is already running.') args = parser.parse_args() logger.debug('args %s', args) # do not put interfaces in promiscuous mode conf.sniff_promisc = conf.promisc = 0 conf.checkIPaddr = 1 if args.verbose: logger.setLevel(logging.DEBUG) logger.debug('args %s', args) if args.interface: conf.iface = args.interface logger.debug('interface %s' % conf.iface) if args.pf is not None: # This is only needed for nm pf = PIDLockFile(args.pf, timeout=5) try: pf.acquire() logger.debug('using pid file %s', pf) except AlreadyLocked as e: pf.break_lock() pf.acquire() except (LockTimeout, LockFailed) as e: logger.error(e) dhcpcap = DHCPCAPFSM(iface=conf.iface, server_port=SERVER_PORT, client_port=CLIENT_PORT, scriptfile=args.sf, delay_selecting=args.delay_selecting) dhcpcap.run() if __name__ == '__main__': main() dhcpcanon-0.8.5/dhcpcanon/dhcpcap.py000066400000000000000000000312141326371602700174020ustar00rootroot00000000000000# -*- coding: utf-8 -*- # vim:ts=4:sw=4:expandtab 2 # Copyright 2016, 2017 juga (juga at riseup dot net), MIT license. """Client class for the DHCP client implementation of the Anonymity Profile ([:rfc:`7844`]).""" from __future__ import absolute_import # from __future__ import unicode_literals import logging import attr from netaddr import IPNetwork from scapy.arch import get_if_raw_hwaddr from scapy.config import conf from scapy.layers.dhcp import BOOTP, DHCP from scapy.layers.inet import IP, UDP from scapy.layers.l2 import Ether from scapy.utils import mac2str, str2mac from .constants import (BROADCAST_ADDR, BROADCAST_MAC, CLIENT_PORT, DHCP_EVENTS, DHCP_OFFER_OPTIONS, META_ADDR, SERVER_PORT, PRL) from .dhcpcaputils import gen_xid from .dhcpcaplease import DHCPCAPLease logger = logging.getLogger('dhcpcanon') @attr.s class DHCPCAP(object): """.""" iface = attr.ib(default=None) client_mac = attr.ib(default=None) client_ip = attr.ib(default=META_ADDR) client_port = attr.ib(default=CLIENT_PORT) server_mac = attr.ib(default=BROADCAST_MAC) server_ip = attr.ib(default=BROADCAST_ADDR) server_port = attr.ib(default=SERVER_PORT) lease = attr.ib(default=attr.Factory(DHCPCAPLease)) event = attr.ib(default=None) prl = attr.ib(default=None) xid = attr.ib(default=None) def __attrs_post_init__(self): """Initializes attributes after attrs __init__. These attributes do not change during the life of the object. """ logger.debug('Creating new DHCPCAP obj.') if self.iface is None: self.iface = conf.iface if self.client_mac is None: _, client_mac = get_if_raw_hwaddr(self.iface) self.client_mac = str2mac(client_mac) if self.prl is None: self.prl = PRL if self.xid is None: self.xid = gen_xid() logger.debug('Modifying Lease obj, setting iface.') self.lease.interface = self.iface def gen_ether_ip(self): """Generates link layer and IP layer part of DHCP packet. For broadcast packets is: Ether(src=client_mac, dst="ff:ff:ff:ff:ff:ff") / IP(src="0.0.0.0", dst="255.255.255.255") / """ ether_ip = (Ether(src=self.client_mac, dst=BROADCAST_MAC) / IP(src=META_ADDR, dst=BROADCAST_ADDR)) return ether_ip def gen_ether_ip_unicast(self): """Generates link layer and IP layer part of DHCP packet. For unicast packets is: Ether(src=client_mac, dst=server_mac) / IP(src=client_ip?, dst=server_ip) / """ ether_ip = (Ether(src=self.client_mac, dst=self.server_mac) / IP(src=self.client_ip, dst=self.server_ip)) return ether_ip def gen_udp(self): """Generates UDP layer part of DHCP packet. UDP layer is always: UDP(sport=68, dport=67) / """ udp = (UDP(sport=self.client_port, dport=self.server_port)) return udp def gen_bootp(self): """Generates BOOTP layer part of DHCP packet. [ :rfc:`7844#section-3.4` ] :: The presence of this address is necessary for the proper operation of the DHCP service. [:rfc:`7844#section-3.`] :: MAY contain the Client Identifier option, """ bootp = ( BOOTP(chaddr=[mac2str(self.client_mac)], xid=self.xid) ) return bootp def gen_bootp_unicast(self): """Generates BOOTP layer part of unicast DHCP packet. Same comments as in gen_bootp """ bootp = ( BOOTP(chaddr=[mac2str(self.client_mac)], xid=self.xid, ciaddr=self.client_ip) ) return bootp def gen_discover(self): """ Generate DHCP DISCOVER packet. [:rfc:`7844#section-3.1`] :: SHOULD randomize the ordering of options If this can not be implemented MAY order the options by option code number (lowest to highest). [:rfc:`7844#section-3.`] :: MAY contain the Parameter Request List option. """ dhcp_discover = ( self.gen_ether_ip() / self.gen_udp() / self.gen_bootp() / DHCP(options=[ ("message-type", "discover"), ("client_id", mac2str(self.client_mac)), ("param_req_list", self.prl), "end" ]) ) logger.debug('Generated discover %s.', dhcp_discover.summary()) return dhcp_discover def gen_request(self): """ Generate DHCP REQUEST packet. [:rfc:`7844#section-3.1`] :: SHOULD randomize the ordering of options If this can not be implemented MAY order the options by option code number (lowest to highest). [:rfc:`7844#section-3.`] :: MAY contain the Parameter Request List option. If in response to a DHCPOFFER,:: MUST contain the corresponding Server Identifier option MUST contain the Requested IP address option. If the message is not in response to a DHCPOFFER (BOUND, RENEW),:: MAY contain a Requested IP address option """ dhcp_req = ( self.gen_ether_ip() / self.gen_udp() / self.gen_bootp() / DHCP(options=[ ("message-type", "request"), ("client_id", mac2str(self.client_mac)), ("param_req_list", self.prl), ("requested_addr", self.lease.address), ("server_id", self.lease.server_id), "end"]) ) logger.debug('Generated request %s.', dhcp_req.summary()) return dhcp_req def gen_request_unicast(self): """ Generate DHCP REQUEST unicast packet. Same comments as in gen_request apply. """ dhcp_req = ( self.gen_ether_ip_unicast() / self.gen_udp() / self.gen_bootp_unicast() / DHCP(options=[ ("message-type", "request"), ("client_id", mac2str(self.client_mac)), ("param_req_list", self.prl), "end"]) ) logger.debug('Generated request %s.', dhcp_req.summary()) return dhcp_req def gen_decline(self): """ Generate DHCP decline packet (broadcast). [:rfc:`7844#section-3.`] :: MUST contain the Message Type option, MUST contain the Server Identifier option, MUST contain the Requested IP address option; .. note:: currently not being used. """ dhcp_decline = ( self.gen_ether_ip() / self.gen_udp() / self.gen_bootp() / DHCP(options=[ ("message-type", "decline"), ("server_id", self.server_ip), ("requested_addr", self.client_ip), "end"]) ) logger.debug('Generated decline.') logger.debug(dhcp_decline.summary()) return dhcp_decline def gen_release(self): """ Generate DHCP release packet (broadcast?). [:rfc:`7844#section-3.`] :: MUST contain the Message Type option and MUST contain the Server Identifier option, .. note:: currently not being used. """ dhcp_release = ( self.gen_ether_ip() / self.gen_udp() / self.gen_bootp() / DHCP(options=[ ("message-type", "release"), ("client_id", mac2str(self.client_mac)), ("server_id", self.server_ip), "end"]) ) logger.debug('Generated release.') logger.debug(dhcp_release.summary()) return dhcp_release def gen_inform(self): """ Generate DHCP inform packet (unicast). [:rfc:`7844#section-3.`] :: MUST contain the Message Type option, .. note:: currently not being used. """ dhcp_inform = ( self.gen_ether_ip_unicast() / self.gen_udp() / self.gen_bootp_unicast() / DHCP(options=[ ("message-type", "inform"), ("client_id", mac2str(self.client_mac)), "end"]) ) logger.debug('Generated inform.') logger.debug(dhcp_inform.summary()) return dhcp_inform def gen_check_lease_attrs(self, attrs_dict): """Generate network mask in CIDR format and subnet. Validate the given arguments. Otherwise AddrFormatError exception will be raised and catched in the FSM. """ # without some minimal options given by the server, is not possible # to create new lease assert attrs_dict['subnet_mask'] assert attrs_dict['address'] # if address and/or network are not valid this will raise an exception # (AddrFormatError) ipn = IPNetwork(attrs_dict['address'] + '/' + attrs_dict['subnet_mask']) # FIXME:70 should be this option required? # assert attrs_dict['server_id'] if attrs_dict.get('server_id') is None: attrs_dict['server_id'] = self.server_ip # TODO: there should be more complex checking here about getting an # address in a subnet? # else: # if IPAddress('server_id') not in ipn: # raise ValueError("server_id is not in the same network as" # "the offered address.") if attrs_dict.get('router') is None: attrs_dict['router'] = attrs_dict['server_id'] ripn = IPNetwork(attrs_dict['router'] + '/' + attrs_dict['subnet_mask']) assert ripn.network == ipn.network # set the options that are not given by the server attrs_dict['subnet_mask_cidr'] = str(ipn.prefixlen) attrs_dict['subnet'] = str(ipn.network) # check other options that might not be given by the server if attrs_dict.get('broadcast_address') is None: attrs_dict['broadcast_address'] = str(ipn.broadcast) if attrs_dict.get('name_server') is None: attrs_dict['name_server'] = attrs_dict['server_id'] if attrs_dict.get('next_server') is None: attrs_dict['next_server'] = attrs_dict['server_id'] logger.debug('Net values are valid') return attrs_dict def handle_offer_ack(self, pkt, time_sent_request=None): """Create a lease object with the values in OFFER/ACK packet.""" attrs_dict = dict() for opt in pkt[DHCP].options: if isinstance(opt, tuple) and opt[0] in DHCP_OFFER_OPTIONS: v = opt[1] if len(opt[1:]) < 2 else ' '.join(opt[1:]) v = str(v.decode('utf8')) if isinstance(v, bytes) else str(v) attrs_dict[opt[0]] = v attrs_dict.update({ "interface": self.iface, "address": pkt[BOOTP].yiaddr, "next_server": pkt[BOOTP].siaddr, }) # this function changes the dict self.gen_check_lease_attrs(attrs_dict) logger.debug('Creating Lease obj.') logger.debug('with attrs %s', attrs_dict) lease = DHCPCAPLease(**attrs_dict) return lease def handle_offer(self, pkt): """.""" logger.debug("Handling Offer.") logger.debug('Modifying obj DHCPCAP, setting lease.') self.lease = self.handle_offer_ack(pkt) def handle_ack(self, pkt, time_sent_request): """.""" logger.debug("Handling ACK.") logger.debug('Modifying obj DHCPCAP, setting server data.') self.server_mac = pkt[Ether].src self.server_ip = pkt[IP].src self.server_port = pkt[UDP].sport event = DHCP_EVENTS['IP_ACQUIRE'] # FIXME:0 check the fields match the previously offered ones? # FIXME:50 create a new object also on renewing/rebinding # or only set_times? lease = self.handle_offer_ack(pkt, time_sent_request) lease.set_times(time_sent_request) if self.lease is not None: if (self.lease.address != lease.address or self.lease.subnet_mask != lease.subnet_mask or self.lease.router != lease.router): event = DHCP_EVENTS['IP_CHANGE'] else: event = DHCP_EVENTS['RENEW'] logger.debug('Modifying obj DHCPCAP, setting lease, client ip, event.') self.lease = lease self.client_ip = self.lease.address self.event = event return event dhcpcanon-0.8.5/dhcpcanon/dhcpcapfsm.py000066400000000000000000000633351326371602700201210ustar00rootroot00000000000000# -*- coding: utf-8 -*- # vim:ts=4:sw=4:expandtab # Copyright 2016, 2017 juga (juga at riseup dot net), MIT license. """DCHP client implementation of the Anonymity Profiles [:rfc:`7844`].""" from __future__ import absolute_import, unicode_literals import logging from netaddr import AddrFormatError from scapy.arch import get_if_raw_hwaddr from scapy.automaton import ATMT, Automaton from scapy.config import conf from scapy.sendrecv import sendp from scapy.utils import str2mac from .clientscript import ClientScript from .constants import (CLIENT_PORT, DELAY_SELECTING, FSM_ATTRS, LEASE_TIME, MAX_ATTEMPTS_DISCOVER, MAX_ATTEMPTS_REQUEST, MAX_OFFERS_COLLECTED, REBINDING_TIME, RENEWING_TIME, SERVER_PORT, STATE_BOUND, STATE_END, STATE_ERROR, STATE_INIT, STATE_PREINIT, STATE_REBINDING, STATE_RENEWING, STATE_REQUESTING, STATE_SELECTING, STATES2NAMES, TIMEOUT_REQUEST_REBINDING, TIMEOUT_REQUEST_RENEWING, TIMEOUT_REQUESTING, TIMEOUT_SELECTING) from .dhcpcap import DHCPCAP from .dhcpcaputils import isack, isnak, isoffer from .timers import (gen_delay_selecting, gen_timeout_request_rebind, gen_timeout_request_renew, gen_timeout_resend, nowutc) from .netutils import set_net logger = logging.getLogger(__name__) class DHCPCAPFSM(Automaton): """DHCP client Finite State Machine (FSM).""" def dict_self(self): """Return the self object attributes not inherited as dict.""" return {k: v for k, v in self.__dict__.items() if k in FSM_ATTRS} def __str__(self): return str(self.dict_self()) def __eq__(self, other): return self.__dict__ == other.__dict__ def reset(self, iface=None, client_mac=None, xid=None, scriptfile=None): """Reset object attributes when state is INIT.""" logger.debug('Reseting attributes.') if iface is None: iface = conf.iface if client_mac is None: # scapy for python 3 returns byte, not tuple tempmac = get_if_raw_hwaddr(iface) if isinstance(tempmac, tuple) and len(tempmac) == 2: mac = tempmac[1] else: mac = tempmac client_mac = str2mac(mac) self.client = DHCPCAP(iface=iface, client_mac=client_mac, xid=xid) if scriptfile is not None: self.script = ClientScript(scriptfile) else: self.script = None self.time_sent_request = None self.discover_attempts = 0 self.request_attempts = 0 self.current_state = STATE_PREINIT self.offers = list() def __init__(self, iface=None, server_port=None, client_port=None, client_mac=None, xid=None, scriptfile=None, delay_selecting=False, delay_before_selecting=None, timeout_select=None, debug_level=5, *args, **kargs): """Overwrites Automaton __init__ method. [ :rfc:`7844#section-3.4` ] :: If the hardware address is reset to a new randomized value, the DHCP client SHOULD use the new randomized value in the DHCP messages """ logger.debug('Inizializating FSM.') super(DHCPCAPFSM, self).__init__(*args, **kargs) self.debug_level = debug_level self.delay_selecting = delay_selecting self.delay_before_selecting = delay_before_selecting self.timeout_select = timeout_select self.reset(iface, client_mac, xid, scriptfile) self.client.server_port = server_port or SERVER_PORT self.client.client_port = client_port or CLIENT_PORT self.socket_kargs = { 'filter': 'udp and src port {0} and dst port {1}' ' and ether dst {2}'. format(self.client.server_port, self.client.client_port, self.client.client_mac) } if self.script is not None: self.script.script_init(self.client.lease, self.current_state) self.script.script_go() logger.debug('FSM thread id: %s.', self.threadid) def get_timeout(self, state, function): """Workaround to get timeout in the ATMT.timeout class method.""" state = STATES2NAMES[state] for timeout_fn_t in self.timeout[state]: # access the function name if timeout_fn_t[1] is not None and \ timeout_fn_t[1].atmt_condname == function.atmt_condname: logger.debug('Timeout for state %s, function %s, is %s', state, function.atmt_condname, timeout_fn_t[0]) return timeout_fn_t[0] return None def set_timeout(self, state, function, newtimeout): """ Workaround to change timeout values in the ATMT.timeout class method. self.timeout format is:: {'STATE': [ (TIMEOUT0, ), (TIMEOUT1, )), (None, None) ], } """ state = STATES2NAMES[state] for timeout_fn_t in self.timeout[state]: # access the function name if timeout_fn_t[1] is not None and \ timeout_fn_t[1].atmt_condname == function.atmt_condname: # convert list to tuple to make it mutable timeout_l = list(timeout_fn_t) # modify the timeout timeout_l[0] = newtimeout # set the new timeoute to self.timeout i = self.timeout[state].index(timeout_fn_t) self.timeout[state][i] = tuple(timeout_l) logger.debug('Set state %s, function %s, to timeout %s', state, function.atmt_condname, newtimeout) def send_discover(self): """Send discover.""" assert self.client assert self.current_state == STATE_INIT or \ self.current_state == STATE_SELECTING pkt = self.client.gen_discover() sendp(pkt) # FIXME:20 check that this is correct,: all or only discover? if self.discover_attempts < MAX_ATTEMPTS_DISCOVER: self.discover_attempts += 1 timeout = gen_timeout_resend(self.discover_attempts) self.set_timeout(self.current_state, self.timeout_selecting, timeout) # logger.info('DHCPDISCOVER on %s to %s port %s' % # (self.client.iface, self.client.server_mac, # self.client.server_port))) def select_offer(self): """Select an offer from the offers received. [:rfc:`2131#section-4.2`]:: DHCP clients are free to use any strategy in selecting a DHCP server among those from which the client receives a DHCPOFFER. [:rfc:`2131#section-4.4.1`]:: The time over which the client collects messages and the mechanism used to select one DHCPOFFER are implementation dependent. Nor [:rfc:`7844`] nor [:rfc:`2131`] specify the algorithm. Here, currently the first offer is selected. .. todo:: - Check other implementations algorithm to select offer. """ logger.debug('Selecting offer.') pkt = self.offers[0] self.client.handle_offer(pkt) def send_request(self): """Send request. [:rfc:`2131#section-3.1`]:: a client retransmitting as described in section 4.1 might retransmit the DHCPREQUEST message four times, for a total delay of 60 seconds .. todo:: - The maximum number of retransmitted REQUESTs is per state or in total? - Are the retransmitted REQUESTs independent to the retransmitted DISCOVERs? """ assert self.client if self.current_state == STATE_BOUND: pkt = self.client.gen_request_unicast() else: pkt = self.client.gen_request() sendp(pkt) logger.debug('Modifying FSM obj, setting time_sent_request.') self.time_sent_request = nowutc() logger.info('DHCPREQUEST of %s on %s to %s port %s', self.client.iface, self.client.client_ip, self.client.server_ip, self.client.server_port) # NOTE: see previous TODO, maybe the MAX_ATTEMPTS_REQUEST needs to be # calculated per state. if self.request_attempts < MAX_ATTEMPTS_REQUEST: self.request_attempts *= 2 logger.debug('Increased request attempts to %s', self.request_attempts) if self.current_state == STATE_RENEWING: timeout_renewing = gen_timeout_request_renew(self.client.lease) self.set_timeout(self.current_state, self.timeout_request_renewing, timeout_renewing) elif self.current_state == STATE_REBINDING: timeout_rebinding = gen_timeout_request_rebind(self.client.lease) self.set_timeout(self.current_state, self.timeout_request_rebinding, timeout_rebinding) else: timeout_requesting = \ gen_timeout_resend(self.request_attempts) self.set_timeout(self.current_state, self.timeout_requesting, timeout_requesting) def set_timers(self): """Set renewal, rebinding times.""" logger.debug('setting timeouts') self.set_timeout(self.current_state, self.renewing_time_expires, self.client.lease.renewal_time) self.set_timeout(self.current_state, self.rebinding_time_expires, self.client.lease.rebinding_time) def process_received_ack(self, pkt): """Process a received ACK packet. Not specifiyed in [:rfc:`7844`]. Probe the offered IP in [:rfc:`2131#section-2.2.`]:: the allocating server SHOULD probe the reused address before allocating the address, e.g., with an ICMP echo request, and the client SHOULD probe the newly received address, e.g., with ARP. The client SHOULD broadcast an ARP reply to announce the client's new IP address and clear any outdated ARP cache entries in hosts on the client's subnet. It is also not specifiyed in [:rfc:`7844`] nor [:rfc:`2131`] how to check that the offered IP is valid. .. todo:: - Check that nor ``dhclient`` nor ``systemd-networkd`` send an ARP. - Check how other implementations check that the ACK paremeters are valid, ie, if the ACK fields match the fields in the OFFER. - Check to which state the client should go back to when the offered parameters are not valid. """ if isack(pkt): try: self.event = self.client.handle_ack(pkt, self.time_sent_request) except AddrFormatError as err: logger.error(err) # NOTE: see previous TODO, maybe should go back to other state. raise self.SELECTING() # NOTE: see previous TODO, not checking address with ARP. logger.info('DHCPACK of %s from %s' % (self.client.client_ip, self.client.server_ip)) return True return False def process_received_nak(self, pkt): """Process a received NAK packet.""" if isnak(pkt): logger.info('DHCPNAK of %s from %s', self.client.client_ip, self.client.server_ip) return True return False ################################################################# # State machine ################################################################# # STATES ######### @ATMT.state(initial=1) def INIT(self): """INIT state. [:rfc:`2131#section-4.4.1`]:: The client SHOULD wait a random time between one and ten seconds to desynchronize the use of DHCP at startup .. todo:: - The initial delay is implemented, but probably is not in other implementations. Check what other implementations do. """ # NOTE: in case INIT is reached from other state, initialize attributes # reset all variables. logger.debug('In state: INIT') if self.current_state is not STATE_PREINIT: self.reset() self.current_state = STATE_INIT # NOTE: see previous TODO, maybe this is not needed. if self.delay_selecting: if self.delay_before_selecting is None: delay_before_selecting = gen_delay_selecting() else: delay_before_selecting = self.delay_before_selecting else: delay_before_selecting = 0 self.set_timeout(self.current_state, self.timeout_delay_before_selecting, delay_before_selecting) if self.timeout_select is not None: self.set_timeout(STATE_SELECTING, self.timeout_selecting, self.timeout_select) @ATMT.state() def SELECTING(self): """SELECTING state.""" logger.debug('In state: SELECTING') self.current_state = STATE_SELECTING @ATMT.state() def REQUESTING(self): """REQUESTING state.""" logger.debug('In state: REQUESTING') self.current_state = STATE_REQUESTING @ATMT.state() def BOUND(self): """BOUND state.""" logger.debug('In state: BOUND') logger.info('(%s) state changed %s -> bound', self.client.iface, STATES2NAMES[self.current_state]) self.current_state = STATE_BOUND self.client.lease.info_lease() if self.script is not None: self.script.script_init(self.client.lease, self.current_state) self.script.script_go() else: try: set_net(self.client.lease) except Exception as e: logger.error('Can not set IP', exc_info=True) # raise self.END() # TODO: go daemon? @ATMT.state() def RENEWING(self): """RENEWING state.""" logger.debug('In state: RENEWING') self.current_state = STATE_RENEWING if self.script is not None: self.script.script_init(self.client.lease, self.current_state) self.script.script_go() else: set_net(self.client.lease) @ATMT.state() def REBINDING(self): """REBINDING state.""" logger.debug('In state: REBINDING') self.current_state = STATE_REBINDING if self.script is not None: self.script.script_init(self.client.lease, self.current_state) self.script.script_go() else: set_net(self.client.lease) @ATMT.state(final=1) def END(self): """END state.""" logger.debug('In state: END') self.current_state = STATE_END if self.script is not None: self.script.script_init(self.client.lease, self.current_state) self.script.script_go() else: set_net(self.client.lease) return @ATMT.state(error=1) def ERROR(self): """ERROR state.""" logger.debug('In state: ERROR') self.current_state = STATE_ERROR if self.script is not None: self.script.script_init(self.client.lease, self.current_state) self.script.script_go() set_net(self.client.lease) raise self.INIT() # TIMEOUTS ########### # TIMEOUTS: retransmissions # ---------------------------- @ATMT.timeout(INIT, DELAY_SELECTING) def timeout_delay_before_selecting(self): """Timeout delay selecting in INIT state.""" logger.debug('C1:T. In %s, timeout delay selecting, raise SELECTING', self.current_state) raise self.SELECTING() @ATMT.timeout(SELECTING, TIMEOUT_SELECTING) def timeout_selecting(self): """Timeout of selecting on SELECTING state. Not specifiyed in [:rfc:`7844`]. See comments in :func:`dhcpcapfsm.DHCPCAPFSM.timeout_request`. """ logger.debug('C2.1: T In %s, timeout receiving response to select.', self.current_state) if len(self.offers) >= MAX_OFFERS_COLLECTED: logger.debug('C2.2: T Maximum number of offers reached, ' 'raise REQUESTING.') raise self.REQUESTING() if self.discover_attempts >= MAX_ATTEMPTS_DISCOVER: logger.debug('C2.3: T Maximum number of discover retries is %s' ' and already sent %s.', MAX_ATTEMPTS_DISCOVER, self.discover_attempts) if len(self.offers) <= 0: logger.debug('C2.4: T. But no OFFERS where received, ' 'raise ERROR.') raise self.ERROR() logger.debug('C2.4: F. But there is some OFFERS, ' 'raise REQUESTING.') raise self.REQUESTING() logger.debug('C2.2: F. Still not received all OFFERS, but not ' 'max # attemps reached, raise SELECTING.') raise self.SELECTING() @ATMT.timeout(REQUESTING, TIMEOUT_REQUESTING) def timeout_requesting(self): """Timeout requesting in REQUESTING state. Not specifiyed in [:rfc:`7844`] [:rfc:`2131#section-3.1`]:: might retransmit the DHCPREQUEST message four times, for a total delay of 60 seconds """ logger.debug("C3.2: T. In %s, timeout receiving response to request, ", self.current_state) if self.discover_requests >= MAX_ATTEMPTS_REQUEST: logger.debug('C2.3: T. Maximum number %s of REQUESTs ' 'reached, already sent %s, raise ERROR.', MAX_ATTEMPTS_REQUEST, self.disover_requests) raise self.ERROR() logger.debug("C2.3: F. Maximum number of REQUESTs retries not reached," "raise REQUESTING.") raise self.REQUESTING() @ATMT.timeout(RENEWING, TIMEOUT_REQUEST_RENEWING) def timeout_request_renewing(self): """Timeout of renewing on RENEWING state. Same comments as in :func:`dhcpcapfsm.DHCPCAPFSM.timeout_requesting`. """ logger.debug("C5.2:T In %s, timeout receiving response to request.", self.current_state) if self.request_attempts >= MAX_ATTEMPTS_REQUEST: logger.debug('C2.3: T Maximum number %s of REQUESTs ' 'reached, already sent %s, wait to rebinding time.', MAX_ATTEMPTS_REQUEST, self.disover_requests) # raise self.ERROR() logger.debug("C2.3: F. Maximum number of REQUESTs retries not reached," "raise RENEWING.") raise self.RENEWING() @ATMT.timeout(REBINDING, TIMEOUT_REQUEST_REBINDING) def timeout_request_rebinding(self): """Timeout of request rebinding on REBINDING state. Same comments as in :func:`dhcpcapfsm.DHCPCAPFSM.timeout_requesting`. """ logger.debug("C6.2:T In %s, timeout receiving response to request.", self.current_state) if self.request_attempts >= MAX_ATTEMPTS_REQUEST: logger.debug('C.2.3: T. Maximum number %s of REQUESTs ' 'reached, already sent %s, wait lease time expires.', MAX_ATTEMPTS_REQUEST, self.disover_requests) # raise self.ERROR() logger.debug("C2.3: F. Maximum number of REQUESTs retries not reached," "raise REBINDING.") raise self.REBINDING() # TIMEOUTS: timers # ----------------- @ATMT.timeout(BOUND, RENEWING_TIME) def renewing_time_expires(self): """Timeout renewing time (T1), transition to RENEWING.""" logger.debug("C4. Timeout renewing time, in BOUND state, " "raise RENEWING.") raise self.RENEWING() @ATMT.timeout(RENEWING, REBINDING_TIME) def rebinding_time_expires(self): """Timeout rebinding time (T2), transition to REBINDING.""" logger.debug("C5.3. Timeout rebinding time, in RENEWING state, " "raise REBINDING.") raise self.REBINDING() @ATMT.timeout(REBINDING, LEASE_TIME) def lease_expires(self): """Timeout lease time, transition to INIT. Not sending DHCPRELEASE to minimize deanonymization [:rfc:`2131#section-4.4.6`]:: Note that the correct operation of DHCP does not depend on the transmission of DHCPRELEASE. """ logger.debug("C6.3. Timeout lease time, in REBINDING state, " "raise INIT.") raise self.STATE_INIT() # RECEIVE CONDITIONS #################### @ATMT.receive_condition(SELECTING) def receive_offer(self, pkt): """Receive offer on SELECTING state.""" logger.debug("C2. Received OFFER?, in SELECTING state.") if isoffer(pkt): logger.debug("C2: T, OFFER received") self.offers.append(pkt) if len(self.offers) >= MAX_OFFERS_COLLECTED: logger.debug("C2.5: T, raise REQUESTING.") self.select_offer() raise self.REQUESTING() logger.debug("C2.5: F, raise SELECTING.") raise self.SELECTING() # same as:, but would can not be overloaded # @ATMT.receive_condition(RENEWING) # @ATMT.receive_condition(REBINDING) @ATMT.receive_condition(REQUESTING) def receive_ack_requesting(self, pkt): """Receive ACK in REQUESTING state.""" logger.debug("C3. Received ACK?, in REQUESTING state.") if self.process_received_ack(pkt): logger.debug("C3: T. Received ACK, in REQUESTING state, " "raise BOUND.") raise self.BOUND() # same as:, but would can not be overloaded # @ATMT.receive_condition(RENEWING) # @ATMT.receive_condition(REBINDING) @ATMT.receive_condition(REQUESTING) def receive_nak_requesting(self, pkt): """Receive NAK in REQUESTING state.""" logger.debug("C3.1. Received NAK?, in REQUESTING state.") if self.process_received_nak(pkt): logger.debug("C3.1: T. Received NAK, in REQUESTING state, " "raise INIT.") raise self.INIT() @ATMT.receive_condition(RENEWING) def receive_ack_renewing(self, pkt): """Receive ACK in RENEWING state.""" logger.debug("C3. Received ACK?, in RENEWING state.") if self.process_received_ack(pkt): logger.debug("C3: T. Received ACK, in RENEWING state, " "raise BOUND.") raise self.BOUND() @ATMT.receive_condition(RENEWING) def receive_nak_renewing(self, pkt): """Receive NAK in RENEWING state.""" logger.debug("C3.1. Received NAK?, in RENEWING state.") if self.process_received_nak(pkt): logger.debug("C3.1: T. Received NAK, in RENEWING state, " " raise INIT.") raise self.INIT() @ATMT.receive_condition(REBINDING) def receive_ack_rebinding(self, pkt): """Receive ACK in REBINDING state.""" logger.debug("C3. Received ACK?, in REBINDING state.") if self.process_received_ack(pkt): logger.debug("C3: T. Received ACK, in REBINDING state, " "raise BOUND.") raise self.BOUND() @ATMT.receive_condition(REBINDING) def receive_nak_rebinding(self, pkt): """Receive NAK in REBINDING state.""" logger.debug("C3.1. Received NAK?, in RENEWING state.") if self.process_received_nak(pkt): logger.debug("C3.1: T. Received NAK, in RENEWING state, " "raise INIT.") raise self.INIT() # ACTIONS ########## # ACTIONS: on timeouts # ----------------------- @ATMT.action(timeout_delay_before_selecting) @ATMT.action(timeout_selecting) def action_transmit_discover(self): """Action on timeout, send DISCOVER.""" logger.debug('Action on timeout, in state %s: send DISCOVER.', self.current_state) self.send_discover() @ATMT.action(timeout_requesting) @ATMT.action(timeout_request_rebinding) @ATMT.action(rebinding_time_expires) @ATMT.action(receive_offer) @ATMT.action(timeout_request_renewing) @ATMT.action(renewing_time_expires) def action_transmit_request(self): """Action on X: send REQUEST.""" logger.debug('Action on timeout X/receive X, in state %s: ' 'send REQUEST.', self.current_state) self.send_request() # ACTIONS: on receive conditions # ------------------------------- @ATMT.action(receive_ack_rebinding) @ATMT.action(receive_ack_requesting) def on_ack_requesting(self): """Action on receive ACK requesting in REQUESTING state: set timers.""" # NOTE: not recording lease logger.debug('Action on receive ACK in REQUESTING state: set timers.') self.set_timers() @ATMT.action(receive_ack_renewing) def on_renewing(self): """Action on renewing on RENEWING state. Not recording lease, but restarting timers. """ self.client.lease.sanitize_net_values() self.client.lease.set_times(self.time_sent_request) self.set_timers() dhcpcanon-0.8.5/dhcpcanon/dhcpcaplease.py000066400000000000000000000074271326371602700204250ustar00rootroot00000000000000# -*- coding: utf-8 -*- # vim:ts=4:sw=4:expandtab 2 # Copyright 2016, 2017 juga (juga at riseup dot net), MIT license. """Lease class for the DHCP client implementation of the Anonymity Profile ([:rfc:`7844`])..""" from __future__ import absolute_import import logging import attr from attr.validators import instance_of from .timers import (future_dt_str, gen_rebinding_time, gen_renewing_time, nowutc) from.constants import (LEASE_ATTRS2LEASE_LOG, ENV_OPTIONS_REQ) logger = logging.getLogger('dhcpcanon') @attr.s class DHCPCAPLease(object): """.""" address = attr.ib(default='', validator=instance_of(str)) server_id = attr.ib(default='', validator=instance_of(str)) next_server = attr.ib(default='', validator=instance_of(str)) router = attr.ib(default='', validator=instance_of(str)) subnet_mask = attr.ib(default='', validator=instance_of(str)) broadcast_address = attr.ib(default='', validator=instance_of(str)) domain = attr.ib(default='', validator=instance_of(str)) name_server = attr.ib(default='', validator=instance_of(str)) subnet = attr.ib(default='', validator=instance_of(str)) lease_time = attr.ib(default='', validator=instance_of(str)) renewal_time = attr.ib(default='', validator=instance_of(str)) rebinding_time = attr.ib(default='', validator=instance_of(str)) # not given by the server interface = attr.ib(default='', validator=instance_of(str)) # not given by the server, calculated on previous subnet_mask_cidr = attr.ib(default='', validator=instance_of(str)) network = attr.ib(default='', validator=instance_of(str)) expiry = attr.ib(default='', validator=instance_of(str)) renew = attr.ib(default='', validator=instance_of(str)) rebind = attr.ib(default='', validator=instance_of(str)) # def __attrs_post_init__(self, sent_dt): # """Initializes attributes after attrs __init__.""" # self.set_times(sent_dt) def set_times(self, sent_dt): """ Set timers for the lease given the time in which the request was sent. [:rfc:`2131#section-4.4.1`]:: The client records the lease expiration time as the sum of the time at which the original request was sent and the duration of the lease from the DHCPACK message. """ logger.debug('Modifying Lease obj, setting timers.') elapsed = (nowutc() - sent_dt).seconds if self.renewal_time == '': self.renewal_time = gen_renewing_time(self.lease_time, elapsed) if self.rebinding_time == '': self.rebinding_time = gen_rebinding_time(self.lease_time, elapsed) self.expiry = future_dt_str(sent_dt, self.lease_time) self.renew = future_dt_str(sent_dt, self.renewal_time) self.rebind = future_dt_str(sent_dt, self.rebinding_time) logger.debug('lease time: %s, expires on %s', self.lease_time, self.expiry) logger.debug('renewal_time: %s, expires on %s', self.renewal_time, self.renew) logger.debug('rebinding time: %s, expires on %s', self.rebinding_time, self.rebind) def info_lease(self): """Print lease information.""" for k, v in LEASE_ATTRS2LEASE_LOG. items(): logger.debug("'%s'=>'%s'", v, getattr(self, k)) for k, v in ENV_OPTIONS_REQ.items(): logger.debug("option '%s'=>'1'", k) logger.info('address %s', self.address) logger.info('plen %s (%s)', self.subnet_mask_cidr, self.subnet_mask) logger.info('gateway %s', self.router) logger.info('server identifier %s', self.server_id) logger.info('nameserver %s', self.name_server) logger.info('domain name %s', self.domain) logger.info('lease time %s', self.lease_time) dhcpcanon-0.8.5/dhcpcanon/dhcpcaputils.py000066400000000000000000000032071326371602700204640ustar00rootroot00000000000000# -*- coding: utf-8 -*- # vim:ts=4:sw=4:expandtab 2 # Copyright 2016, 2017 juga (juga at riseup dot net), MIT license. """Util functions for the DHCP client implementation of the Anonymity Profile ([:rfc:`7844`]).""" from __future__ import absolute_import import logging import random from scapy.arch.linux import get_if_list from scapy.layers.dhcp import DHCP, DHCPTypes from .constants import XID_MIN, XID_MAX logger = logging.getLogger(__name__) def isoffer(packet): """.""" if DHCP in packet and (DHCPTypes.get(packet[DHCP].options[0][1]) == 'offer' or packet[DHCP].options[0][1] == "offer"): logger.debug('Packet is Offer.') return True return False def isnak(packet): """.""" if DHCP in packet and (DHCPTypes.get(packet[DHCP].options[0][1]) == 'nak' or packet[DHCP].options[0][1] == 'nak'): logger.debug('Packet is NAK.') return True return False def isack(packet): """.""" if DHCP in packet and (DHCPTypes.get(packet[DHCP].options[0][1]) == 'ack' or packet[DHCP].options[0][1] == 'ack'): logger.debug('Packet is ACK.') return True return False def gen_xid(): return random.randint(XID_MIN, XID_MAX) def discover_ifaces(): ifaces = get_if_list() ifaces.remove('lo') logger.debug('Disovered interfaces %s.', ifaces) return ifaces def detect_speed_network(): # 100 Mbps = 100 Mb/s with open('/sys/class/net/eth0/speed') as fd: speed = fd.read() logger.debug('Net speed %s', speed) return speed # TODO def detect_initial_network(): pass dhcpcanon-0.8.5/dhcpcanon/netutils.py000066400000000000000000000107751326371602700176600ustar00rootroot00000000000000# -*- coding: utf-8 -*- # vim:ts=4:sw=4:expandtab 2 # Copyright 2016, 2017 juga (juga at riseup dot net), MIT license. """Netowrk utils for the DHCP client implementation of the Anonymity Profile ([:rfc:`7844`]).""" import logging import os.path import subprocess from dbus import SystemBus, Interface, DBusException from pyroute2 import IPRoute from pyroute2.netlink import NetlinkError from .constants import RESOLVCONF, RESOLVCONF_ADMIN logger = logging.getLogger(__name__) def set_net(lease): ipr = IPRoute() try: index = ipr.link_lookup(ifname=lease.interface)[0] except IndexError as e: logger.error('Interface %s not found, can not set IP.', lease.interface) try: ipr.addr('add', index, address=lease.address, mask=int(lease.subnet_mask_cidr)) except NetlinkError as e: if ipr.get_addr(index=index)[0].\ get_attrs('IFA_ADDRESS')[0] == lease.address: logger.debug('Interface %s is already set to IP %s' % (lease.interface, lease.address)) else: logger.error(e) else: logger.debug('Interface %s set to IP %s' % (lease.interface, lease.address)) try: ipr.route('add', dst='0.0.0.0', gateway=lease.router, oif=index) except NetlinkError as e: if ipr.get_routes(table=254)[0].\ get_attrs('RTA_GATEWAY')[0] == lease.router: logger.debug('Default gateway is already set to %s' % (lease.router)) else: logger.error(e) else: logger.debug('Default gateway set to %s', lease.router) ipr.close() set_dns(lease) def set_dns(lease): if systemd_resolved_status() is True: set_dns_systemd_resolved(lease) elif os.path.exists(RESOLVCONF_ADMIN): set_dns_resolvconf_admin(lease) elif os.path.exists(RESOLVCONF): set_dns_resolvconf(lease) def set_dns_resolvconf_admin(lease): cmd = [RESOLVCONF_ADMIN, 'add', lease.interface, lease.name_server] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: (stdout, stderr) = proc.communicate() return True except TypeError as e: logger.error(e) return False def set_dns_resolvconf(lease): cmd = [RESOLVCONF, '-a', lease.interface] proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdin = '\n'.join(['nameserver ' + nm for nm in lease.name_server.split()]) stdin = str.encode(stdin) try: (stdout, stderr) = proc.communicate(stdin) return True except TypeError as e: logger.error(e) return False def set_dns_systemd_resolved(lease): # NOTE: if systemd-resolved is not already running, we might not want to # run it in case there's specific system configuration for other resolvers ipr = IPRoute() index = ipr.link_lookup(ifname=lease.interface)[0] # Construct the argument to pass to DBUS. # the equivalent argument for: # busctl call org.freedesktop.resolve1 /org/freedesktop/resolve1 \ # org.freedesktop.resolve1.Manager SetLinkDNS 'ia(iay)' 2 1 2 4 1 2 3 4 # is SetLinkDNS(2, [(2, [8, 8, 8, 8])]_ iay = [(2, [int(b) for b in ns.split('.')]) for ns in lease.name_server.split()] # if '.' in ns # else (10, [ord(x) for x in # socket.inet_pton(socket.AF_INET6, ns)]) bus = SystemBus() resolved = bus.get_object('org.freedesktop.resolve1', '/org/freedesktop/resolve1') manager = Interface(resolved, dbus_interface='org.freedesktop.resolve1.Manager') try: manager.SetLinkDNS(index, iay) return True except DBusException as e: logger.error(e) return False def systemd_resolved_status(): bus = SystemBus() systemd = bus.get_object('org.freedesktop.systemd1', '/org/freedesktop/systemd1') manager = Interface(systemd, dbus_interface='org.freedesktop.systemd1.Manager') unit = manager.LoadUnit('sytemd-resolved.service') proxy = bus.get_object('org.freedesktop.systemd1', str(unit)) r = proxy.Get('org.freedesktop.systemd1.Unit', 'ActiveState', dbus_interface='org.freedesktop.DBus.Properties') if str(r) == 'active': return True return False dhcpcanon-0.8.5/dhcpcanon/timers.py000066400000000000000000000116651326371602700173130ustar00rootroot00000000000000# -*- coding: utf-8 -*- # vim:ts=4:sw=4:expandtab 2 # Copyright 2016, 2017 juga (juga at riseup dot net), MIT license. """Timers for the DHCP client implementation of the Anonymity Profile ([:rfc:`7844`]).""" from __future__ import absolute_import import logging import random from datetime import datetime, timedelta from .constants import (DT_PRINT_FORMAT, MAX_DELAY_SELECTING, REBIND_PERC, RENEW_PERC) logger = logging.getLogger(__name__) def future_dt_str(dt, td): """.""" if isinstance(td, str): td = float(td) td = timedelta(seconds=td) future_dt = dt + td return future_dt.strftime(DT_PRINT_FORMAT) def nowutc(): """.""" # NOTE: Not using UTC, as all the timers are set in reference to local time # now = datetime.utcnow().replace(tzinfo=utc) now = datetime.now() return now def gen_delay_selecting(): """Generate the delay in seconds in which the DISCOVER will be sent. [:rfc:`2131#section-4.4.1`]:: The client SHOULD wait a random time between one and ten seconds to desynchronize the use of DHCP at startup. """ delay = float(random.randint(0, MAX_DELAY_SELECTING)) logger.debug('Delay to enter in SELECTING %s.', delay) logger.debug('SELECTING will happen on %s', future_dt_str(nowutc(), delay)) return delay def gen_timeout_resend(attempts): """Generate the time in seconds in which DHCPDISCOVER wil be retransmited. [:rfc:`2131#section-3.1`]:: might retransmit the DHCPREQUEST message four times, for a total delay of 60 seconds [:rfc:`2131#section-4.1`]:: For example, in a 10Mb/sec Ethernet internetwork, the delay before the first retransmission SHOULD be 4 seconds randomized by the value of a uniform random number chosen from the range -1 to +1. Clients with clocks that provide resolution granularity of less than one second may choose a non-integer randomization value. The delay before the next retransmission SHOULD be 8 seconds randomized by the value of a uniform number chosen from the range -1 to +1. The retransmission delay SHOULD be doubled with subsequent retransmissions up to a maximum of 64 seconds. """ timeout = 2 ** (attempts + 1) + random.uniform(-1, +1) logger.debug('next timeout resending will happen on %s', future_dt_str(nowutc(), timeout)) return timeout def gen_timeout_request_renew(lease): """Generate time in seconds to retransmit DHCPREQUEST. [:rfc:`2131#section-4..4.5`]:: In both RENEWING and REBINDING states, if the client receives no response to its DHCPREQUEST message, the client SHOULD wait one-half of the remaining time until T2 (in RENEWING state) and one-half of the remaining lease time (in REBINDING state), down to a minimum of 60 seconds, before retransmitting the DHCPREQUEST message. """ time_left = (lease.rebinding_time - lease.renewing_time) * RENEW_PERC if time_left < 60: time_left = 60 logger.debug('Next request in renew will happen on %s', future_dt_str(nowutc(), time_left)) return time_left def gen_timeout_request_rebind(lease): """.""" time_left = (lease.lease_time - lease.rebinding_time) * RENEW_PERC if time_left < 60: time_left = 60 logger.debug('Next request on rebinding will happen on %s', future_dt_str(nowutc(), time_left)) return time_left def gen_renewing_time(lease_time, elapsed=0): """Generate RENEWING time. [:rfc:`2131#section-4.4.5`]:: T1 defaults to (0.5 * duration_of_lease). T2 defaults to (0.875 * duration_of_lease). Times T1 and T2 SHOULD be chosen with some random "fuzz" around a fixed value, to avoid synchronization of client reacquisition. """ renewing_time = int(lease_time) * RENEW_PERC - elapsed # FIXME:80 [:rfc:`2131#section-4.4.5`]: the chosen "fuzz" could fingerprint # the implementation # NOTE: here using same "fuzz" as systemd? range_fuzz = int(lease_time) * REBIND_PERC - renewing_time logger.debug('rebinding fuzz range %s', range_fuzz) fuzz = random.uniform(-(range_fuzz), +(range_fuzz)) renewing_time += fuzz logger.debug('Renewing time %s.', renewing_time) return renewing_time def gen_rebinding_time(lease_time, elapsed=0): """.""" rebinding_time = int(lease_time) * REBIND_PERC - elapsed # FIXME:90 [:rfc:`2131#section-4.4.5`]: the chosen "fuzz" could fingerprint # the implementation # NOTE: here using same "fuzz" as systemd? range_fuzz = int(lease_time) - rebinding_time logger.debug('rebinding fuzz range %s', range_fuzz) fuzz = random.uniform(-(range_fuzz), +(range_fuzz)) rebinding_time += fuzz logger.debug('Rebinding time %s.', rebinding_time) return rebinding_time dhcpcanon-0.8.5/docs/000077500000000000000000000000001326371602700144205ustar00rootroot00000000000000dhcpcanon-0.8.5/docs/Makefile000066400000000000000000000211471326371602700160650ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source # not in auto-generated conf SOURCEDIR = source IMAGEDIRS = source/images BUILDDIRIMAGES = $(BUILDDIR)/html/_images # generate SVG PYREVERSE = pyreverse PYREVERSE_FLAGS = -o svg -p dhcpcanon ../dhcpcanon # SVG to PDF conversion SVG2PDF = inkscape SVG2PDF_FLAGS = .PHONY: help help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " applehelp to make an Apple Help Book" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " epub3 to make an epub3" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" @echo " coverage to run coverage check of the documentation (if enabled)" @echo " dummy to check syntax errors of document sources" # Not in auto-generated conf FSMSVG := python -c "from dhcpcanon.dhcpcapfsm import DHCPCAPFSM; DHCPCAPFSM.graph(target='$(IMAGEDIRS)/dhcpcapfsm.svg')";mkdir -p $(BUILDDIRIMAGES);cp $(IMAGEDIRS)/*.svg $(BUILDDIRIMAGES) fsmsvg: @echo "Generating FSM SVG" $(FSMSVG) UMLSVG := $(PYREVERSE) $(PYREVERSE_FLAGS);mv *.svg $(IMAGEDIRS);mkdir -p $(BUILDDIRIMAGES);cp $(IMAGEDIRS)/*.svg $(BUILDDIRIMAGES) umlsvg: @echo "Generating UML SVG" $($UMLSVG) # Pattern rule for converting SVG to PDF %.pdf : %.svg $(SVG2PDF) -f $< -A $@ # Build a list of SVG files to convert to PDFs PDFs := $(foreach dir, $(IMAGEDIRS), $(patsubst %.svg,%.pdf,$(wildcard $(SOURCEDIR)/$(dir)/*.svg))) # Make a rule to build the PDFs images: @echo "Generating PDFs" $(PDFs) .PHONY: clean clean: rm -rf $(BUILDDIR)/* rm -f $(PDFs) .PHONY: html html: #$(FSMSVG) #$(UMLSVG) $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." .PHONY: dirhtml dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." .PHONY: singlehtml singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." .PHONY: pickle pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." .PHONY: json json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." .PHONY: htmlhelp htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." .PHONY: qthelp qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/DHCPanonymityprofile.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/DHCPanonymityprofile.qhc" .PHONY: applehelp applehelp: $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp @echo @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." @echo "N.B. You won't be able to view it unless you put it in" \ "~/Library/Documentation/Help or install it in your application" \ "bundle." .PHONY: devhelp devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/DHCPanonymityprofile" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/DHCPanonymityprofile" @echo "# devhelp" .PHONY: epub epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." .PHONY: epub3 epub3: $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 @echo @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." .PHONY: latex latex: $(FSMSVG) #$(UMLSVG) $(PDFs) $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." .PHONY: latexpdf latexpdf: $(FSMSVG) #$(UMLSVG) $(PDFs) $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." .PHONY: latexpdfja latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." .PHONY: text text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." .PHONY: man man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." .PHONY: texinfo texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." .PHONY: info info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." .PHONY: gettext gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." .PHONY: changes changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." .PHONY: linkcheck linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." .PHONY: doctest doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." .PHONY: coverage coverage: $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage @echo "Testing of coverage in the sources finished, look at the " \ "results in $(BUILDDIR)/coverage/python.txt." .PHONY: xml xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." .PHONY: pseudoxml pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." .PHONY: dummy dummy: $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy @echo @echo "Build finished. Dummy builder generates no files." dhcpcanon-0.8.5/docs/make.bat000066400000000000000000000171311326371602700160300ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source set I18NSPHINXOPTS=%SPHINXOPTS% source if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. epub3 to make an epub3 echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled echo. coverage to run coverage check of the documentation if enabled echo. dummy to check syntax errors of document sources goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) REM Check if sphinx-build is available and fallback to Python version if any %SPHINXBUILD% 1>NUL 2>NUL if errorlevel 9009 goto sphinx_python goto sphinx_ok :sphinx_python set SPHINXBUILD=python -m sphinx.__init__ %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) :sphinx_ok if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\DHCPanonymityprofile.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\DHCPanonymityprofile.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "epub3" ( %SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3 if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub3 file is in %BUILDDIR%/epub3. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %~dp0 echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %~dp0 echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "coverage" ( %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage if errorlevel 1 exit /b 1 echo. echo.Testing of coverage in the sources finished, look at the ^ results in %BUILDDIR%/coverage/python.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) if "%1" == "dummy" ( %SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy if errorlevel 1 exit /b 1 echo. echo.Build finished. Dummy builder generates no files. goto end ) :end dhcpcanon-0.8.5/docs/source/000077500000000000000000000000001326371602700157205ustar00rootroot00000000000000dhcpcanon-0.8.5/docs/source/README.rst000077700000000000000000000000001326371602700215172../../README.rstustar00rootroot00000000000000dhcpcanon-0.8.5/docs/source/api.rst000066400000000000000000000022071326371602700172240ustar00rootroot00000000000000.. _api: dhcpcanon Python API Reference =============================== .. autosummary:: dhcpcanon.dhcpcapfsm dhcpcanon.dhcpcap dhcpcanon.dhcpcaplease dhcpcanon.clientscript dhcpcanon.timers dhcpcanon.dhcpcaputils dhcpcanon.constants dhcpcanon.conflog dhcpcapfsm module ------------------- .. automodule:: dhcpcanon.dhcpcapfsm :members: :undoc-members: dhcpcap module ------------------- .. automodule:: dhcpcanon.dhcpcap :members: :undoc-members: dhcpcaplease module ------------------- .. automodule:: dhcpcanon.dhcpcaplease :members: :undoc-members: clientscript module -------------------- .. automodule:: dhcpcanon.clientscript :members: :undoc-members: timers module ------------------- .. automodule:: dhcpcanon.timers :members: :undoc-members: dhcpcaputils module -------------------- .. automodule:: dhcpcanon.dhcpcaputils :members: :undoc-members: constants module ------------------- .. automodule:: dhcpcanon.constants :members: :undoc-members: conflog module ------------------- .. automodule:: dhcpcanon.conflog :members: :undoc-members: dhcpcanon-0.8.5/docs/source/conf.py000066400000000000000000000347321326371602700172300ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # DHCP anonymity profile documentation build configuration file, created by # sphinx-quickstart on Sat Jul 23 19:57:52 2016. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # import os import sys sys.path.insert(0, os.path.dirname(os.path.dirname( os.path.abspath('.')))) from dhcpcanon import __version__ import sphinx_bootstrap_theme # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.imgmath', 'sphinx.ext.viewcode', 'sphinx.ext.githubpages', 'sphinx.ext.autosummary', ] html_theme = 'bootstrap' html_theme_path = sphinx_bootstrap_theme.get_html_theme_path() # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The encoding of source files. # # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'dhcpcanon' copyright = u'2016-2018 juga' author = u'juga' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. # version = u'0.8' version = __version__ # The full version, including alpha/beta/rc tags. # release = u'0.8.2' release = version # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # # today = '' # # Else, today_fmt is used as the format for a strftime call. # # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. # # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'bootstrap' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. html_theme_options = { # Navigation bar title. (Default: ``project`` value) 'navbar_title': project, # Tab name for entire site. (Default: "Site") 'navbar_site_name': "Site", # Tab name for the current pages TOC. (Default: "Page") 'navbar_pagenav_name': "Page", # A list of tuples containing pages or urls to link to. # Valid tuples should be in the following forms: # (name, page) # a link to a page # (name, "/aa/bb", 1) # a link to an arbitrary relative url # (name, "http://example.com", True) # arbitrary absolute url # Note the "1" or "True" value above as the third argument to indicate # an arbitrary url. # 'navbar_links': [ # ("Examples", "examples"), # ("Link", "http://example.com", True), # ], # Global TOC depth for "site" navbar tab. (Default: 1) # Switching to -1 shows all levels. 'globaltoc_depth': 2, # Include hidden TOCs in Site navbar? # # Note: If this is "false", you cannot have mixed ``:hidden:`` and # non-hidden ``toctree`` directives in the same page, or else the build # will break. # # Values: "true" (default) or "false" 'globaltoc_includehidden': "true", # HTML navbar class (Default: "navbar") to attach to
element. # For black navbar, do "navbar navbar-inverse" 'navbar_class': "navbar", # Fix navigation bar to top of page? # Values: "true" (default) or "false" 'navbar_fixed_top': "true", # Location of link to source. # Options are "nav" (default), "footer" or anything else to exclude. 'source_link_position': "nav", # Bootswatch (http://bootswatch.com/) theme. # # Options are nothing (default) or the name of a valid theme such # such as "cosmo" or "sandstone". # # Example themes: # * flatly # * sandstone (v3 only) # * united # * yeti (v3 only) 'bootswatch_theme': "sandstone", # Choose Bootstrap version. # Values: "3" (default) or "2" (in quotes) 'bootstrap_version': "3", } # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. # " v documentation" by default. # # html_title = u'dhcpcanon v0.1.0' # A shorter title for the navigation bar. Default is the same as html_title. # # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = 'images/dhcpcanon_logo.png' # The name of an image file (relative to this directory) to use as a favicon of # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = 'images/dhcpcanon.ico' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # # html_extra_path = [] # If not None, a 'Last updated on:' timestamp is inserted at every page # bottom, using the given strftime format. # The empty string is equivalent to '%b %d, %Y'. # # html_last_updated_fmt = None # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # # html_sidebars = {} html_sidebars = {'sidebar': ['localtoc.html', 'sourcelink.html', 'searchbox.html']} # Additional templates that should be rendered to pages, maps page names to # template names. # # html_additional_pages = {} # If false, no module index is generated. # # html_domain_indices = True # If false, no index is generated. # # html_use_index = True # If true, the index is split into individual pages for each letter. # # html_split_index = False # If true, links to the reST sources are added to the pages. # # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh' # # html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # 'ja' uses this config value. # 'zh' user can custom change `jieba` dictionary path. # # html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. # # html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. htmlhelp_basename = 'DHCPanonymityprofiledoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'dhcpcanon.tex', u'dhcpcanon Documentation', u'juga', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # # latex_use_parts = False # If true, show page references after internal links. # # latex_show_pagerefs = False # If true, show URL addresses after external links. # # latex_show_urls = False # Documents to append as an appendix to all manuals. # # latex_appendices = [] # It false, will not define \strong, \code, itleref, \crossref ... but only # \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added # packages. # # latex_keep_old_macro_names = True # If false, no module index is generated. # # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'dhcpcanon', u'dhcpcanon Documentation', [author], 1) ] # If true, show URL addresses after external links. # # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'dhcpcanon', u'dhcpcanon Documentation', author, 'DHCPanonymityprofile', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # # texinfo_appendices = [] # If false, no module index is generated. # # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # # texinfo_no_detailmenu = False # -- Options for Epub output ---------------------------------------------- # Bibliographic Dublin Core info. epub_title = project epub_author = author epub_publisher = author epub_copyright = copyright # The basename for the epub file. It defaults to the project name. # epub_basename = project # The HTML theme for the epub output. Since the default themes are not # optimized for small screen space, using the same theme for HTML and epub # output is usually not wise. This defaults to 'epub', a theme designed to save # visual space. # # epub_theme = 'epub' # The language of the text. It defaults to the language option # or 'en' if the language is not set. # # epub_language = '' # The scheme of the identifier. Typical schemes are ISBN or URL. # epub_scheme = '' # The unique identifier of the text. This can be a ISBN number # or the project homepage. # # epub_identifier = '' # A unique identification for the text. # # epub_uid = '' # A tuple containing the cover image and cover page html template filenames. # # epub_cover = () # A sequence of (type, uri, title) tuples for the guide element of content.opf. # # epub_guide = () # HTML files that should be inserted before the pages created by sphinx. # The format is a list of tuples containing the path and title. # # epub_pre_files = [] # HTML files that should be inserted after the pages created by sphinx. # The format is a list of tuples containing the path and title. # # epub_post_files = [] # A list of files that should not be packed into the epub file. epub_exclude_files = ['search.html'] # The depth of the table of contents in toc.ncx. # # epub_tocdepth = 3 # Allow duplicate toc entries. # # epub_tocdup = True # Choose between 'default' and 'includehidden'. # # epub_tocscope = 'default' # Fix unsupported image types using the Pillow. # # epub_fix_images = False # Scale large images. # # epub_max_image_width = 0 # How to display URL addresses: 'footnote', 'no', or 'inline'. # # epub_show_urls = 'inline' # If false, no index is generated. # # epub_use_index = True # Example configuration for intersphinx: refer to the Python standard library. # intersphinx_mapping = {'https://docs.python.org/': None} # intersphinx_mapping = { # 'rfc7844_comments': ('https://rfc7844-comments.readthedocs.io/en/latest', # '../build/html/rfc7844_comments.inv') # } dhcpcanon-0.8.5/docs/source/contributing.rst000066400000000000000000000052571326371602700211720ustar00rootroot00000000000000.. _contributing: Contributing to ``dhcpcanon`` ============================== We welcome contributions of any kind (ideas, code, tests, documentation, examples, ...). General contribution guidelines ------------------------------- * Any non-trivial change should contain tests. * All the functions and methods should contain Sphinx docstrings which are used to generate the API documentation. Code style guide ----------------- * We follow `PEP8 Python Style Guide`_ * Use 4 spaces for a tab * Use 79 characters in a line * Make sure edited file doesn't contain any trailing whitespace * You can verify that your modifications don't break any rules by running the ``flake8`` script - e.g. ``flake8 dhcpcanon/edited_file.py`` or ``tox -e style``. Second command will run flake8 on all the files in the repository. And most importantly, follow the existing style in the file you are editing and **be consistent**. Docstring conventions --------------------- For documenting the API we we use Sphinx and reStructuredText syntax. Contribution workflow --------------------- 1. Open a new issue on our issue tracker ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Go to our `issue tracker`_ and open a new issue for your changes there. 2. Fork our Github repository ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Fork our `Github git repository`_. Your fork will be used to hold your changes. 3. Create a new branch for your changes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ For example: .. sourcecode:: bash git checkout -b 4. Make your changes ~~~~~~~~~~~~~~~~~~~~ Commit often and rebase master 5. Write tests for your changes and make sure all the tests pass ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Make sure that all the code you have added or modified has appropriate test coverage. Also make sure all the tests including the existing ones still pass using ``tox`` .. sourcecode:: bash tox 6. Open a Pull request ~~~~~~~~~~~~~~~~~~~~~~~~~~ You can then push your feature branch to your remote and open a pull request. Reporting a Vulnerability ---------------------------- Please do not report security issues using the public `Issue tracker`_. Send a description of it to juga at riseup dot net. You are also encouraged to encrypt this email using GPG. The key can be found in the public servers. This docummentation is partly copied from `libcloud contributing`_ .. _`PEP8 Python Style Guide`: http://www.python.org/dev/peps/pep-0008/ .. _`Issue tracker`: https://github.com/juga0/dhcpcanon/issues .. _`Github git repository`: https://github.com/juga0/dhcpcanon .. _`libcloud contributing`: https://libcloud.readthedocs.io/en/latest/development.html#contributing dhcpcanon-0.8.5/docs/source/diagrams.rst000066400000000000000000000010511326371602700202360ustar00rootroot00000000000000.. _diagrams: ``dhcpcanon`` diagrams ========================= Finite State Machine diagram -------------------------------- .. image:: ./images/dhcpcapfsm.* Classes diagram --------------- .. image:: ./images/classes_dhcpcanon.* Packages diagram -------------------- .. image:: ./images/packages_dhcpcanon.* Calls diagram --------------- .. image:: ./images/calls_dhcpcanon.* Organigram ----------- This organigram does not reflect the current status of ``dhcpcanon``, but as it should be changed .. image:: ./images/organigram_dhcpcanon.* dhcpcanon-0.8.5/docs/source/download.rst000066400000000000000000000005071326371602700202630ustar00rootroot00000000000000.. _download: Download dhcpcanon =================== You can download this project in either `zip `__ or `tar `__ formats. You can also clone the project with Git by running: git clone git://github.com/juga0/dhcpcanon dhcpcanon-0.8.5/docs/source/images/000077500000000000000000000000001326371602700171655ustar00rootroot00000000000000dhcpcanon-0.8.5/docs/source/images/calls_dhcpcanon.svg000066400000000000000000002124071326371602700230270ustar00rootroot00000000000000 G Generated by Python Call Graph v1.0.1 http://pycallgraph.slowchop.com cluster_DHCPCAP DHCPCAP cluster_scapy scapy cluster_logging logging cluster_socket socket cluster_dhcpcanon dhcpcanon cluster_attr attr cluster_threading threading cluster_ClientScript ClientScript cluster___main__ __main__ cluster_DHCPCAPLease DHCPCAPLease DHCPCAP.__init__ DHCPCAP.__init__ calls: 1 time: 0.002912s dhcpcanon.dhcpcap.DHCPCAP.__attrs_post_init__ dhcpcanon.dhcpcap.DHCPCAP.__attrs_post_init__ calls: 1 time: 0.001950s DHCPCAP.__init__->dhcpcanon.dhcpcap.DHCPCAP.__attrs_post_init__ 1 DHCPCAPLease.__init__ DHCPCAPLease.__init__ calls: 1 time: 0.000845s DHCPCAP.__init__->DHCPCAPLease.__init__ 1 scapy.automaton._instance_state.__init__ scapy.automaton._instance_state.__init__ calls: 8 time: 0.000092s scapy.automaton.ObjectPipe.__init__ scapy.automaton.ObjectPipe.__init__ calls: 2 time: 0.000039s scapy.automaton.ObjectPipe.recv scapy.automaton.ObjectPipe.recv calls: 1 time: 5.493335s scapy.utils.str2mac scapy.utils.str2mac calls: 1 time: 0.000026s scapy.automaton.DHCPCAPFSM.start scapy.automaton.DHCPCAPFSM.start calls: 1 time: 0.000094s scapy.automaton.DHCPCAPFSM._do_start scapy.automaton.DHCPCAPFSM._do_start calls: 1 time: 0.000047s scapy.automaton.DHCPCAPFSM.start->scapy.automaton.DHCPCAPFSM._do_start 1 scapy.automaton.DHCPCAPFSM.run scapy.automaton.DHCPCAPFSM.run calls: 1 time: 5.493628s scapy.automaton.DHCPCAPFSM.run->scapy.automaton.ObjectPipe.recv 1 scapy.automaton.ObjectPipe.send scapy.automaton.ObjectPipe.send calls: 2 time: 0.000040s scapy.automaton.DHCPCAPFSM.run->scapy.automaton.ObjectPipe.send 2 scapy.automaton.Message.__init__ scapy.automaton.Message.__init__ calls: 2 time: 0.000018s scapy.automaton.DHCPCAPFSM.run->scapy.automaton.Message.__init__ 2 scapy.automaton.DHCPCAPFSM.__init__ scapy.automaton.DHCPCAPFSM.__init__ calls: 1 time: 0.000865s scapy.automaton.DHCPCAPFSM.__init__->scapy.automaton._instance_state.__init__ 8 scapy.automaton.DHCPCAPFSM.__init__->scapy.automaton.ObjectPipe.__init__ 2 scapy.automaton.DHCPCAPFSM.__init__->scapy.automaton.DHCPCAPFSM.start 1 scapy.automaton.DHCPCAPFSM.parse_args scapy.automaton.DHCPCAPFSM.parse_args calls: 1 time: 0.000012s scapy.automaton.DHCPCAPFSM.__init__->scapy.automaton.DHCPCAPFSM.parse_args 1 scapy.arch.common.get_if scapy.arch.common.get_if calls: 1 time: 0.000479s socket._socketobject.close socket._socketobject.close calls: 1 time: 0.000066s scapy.arch.common.get_if->socket._socketobject.close 1 socket._socketobject.__init__ socket._socketobject.__init__ calls: 1 time: 0.000125s scapy.arch.common.get_if->socket._socketobject.__init__ 1 socket._socketobject.meth socket._socketobject.meth calls: 2 time: 0.000021s scapy.arch.common.get_if->socket._socketobject.meth 2 scapy.arch.linux.get_if_raw_hwaddr scapy.arch.linux.get_if_raw_hwaddr calls: 1 time: 0.000582s scapy.arch.linux.get_if_raw_hwaddr->scapy.arch.common.get_if 1 logging.Formatter.format logging.Formatter.format calls: 6 time: 0.001383s logging.Formatter.usesTime logging.Formatter.usesTime calls: 6 time: 0.000061s logging.Formatter.format->logging.Formatter.usesTime 6 logging.LogRecord.getMessage logging.LogRecord.getMessage calls: 6 time: 0.000129s logging.Formatter.format->logging.LogRecord.getMessage 6 logging.Formatter.formatTime logging.Formatter.formatTime calls: 6 time: 0.000260s logging.Formatter.format->logging.Formatter.formatTime 6 logging.Logger.makeRecord logging.Logger.makeRecord calls: 6 time: 0.007793s logging.LogRecord.__init__ logging.LogRecord.__init__ calls: 6 time: 0.007412s logging.Logger.makeRecord->logging.LogRecord.__init__ 6 logging.Logger.findCaller logging.Logger.findCaller calls: 6 time: 0.000758s logging.<lambda> logging.<lambda> calls: 6 time: 0.000066s logging.Logger.findCaller->logging.<lambda> 6 logging.getLevelName logging.getLevelName calls: 6 time: 0.000082s logging.LogRecord.__init__->logging.getLevelName 6 threading._MainThread.name threading._MainThread.name calls: 6 time: 0.000072s logging.LogRecord.__init__->threading._MainThread.name 6 threading.currentThread threading.currentThread calls: 6 time: 0.000078s logging.LogRecord.__init__->threading.currentThread 6 logging.Logger._log logging.Logger._log calls: 6 time: 0.017842s logging.Logger._log->logging.Logger.makeRecord 6 logging.Logger._log->logging.Logger.findCaller 6 logging.Logger.handle logging.Logger.handle calls: 6 time: 0.008303s logging.Logger._log->logging.Logger.handle 6 logging.Logger.getEffectiveLevel logging.Logger.getEffectiveLevel calls: 6 time: 0.000111s logging.StreamHandler.emit logging.StreamHandler.emit calls: 6 time: 0.004519s logging.StreamHandler.flush logging.StreamHandler.flush calls: 6 time: 0.001646s logging.StreamHandler.emit->logging.StreamHandler.flush 6 logging.StreamHandler.format logging.StreamHandler.format calls: 6 time: 0.001698s logging.StreamHandler.emit->logging.StreamHandler.format 6 logging.StreamHandler.release logging.StreamHandler.release calls: 12 time: 0.001213s threading._RLock.release threading._RLock.release calls: 12 time: 0.000670s logging.StreamHandler.release->threading._RLock.release 12 logging.StreamHandler.acquire logging.StreamHandler.acquire calls: 12 time: 0.001393s threading._RLock.acquire threading._RLock.acquire calls: 12 time: 0.000861s logging.StreamHandler.acquire->threading._RLock.acquire 12 logging.StreamHandler.handle logging.StreamHandler.handle calls: 6 time: 0.007098s logging.StreamHandler.handle->logging.StreamHandler.emit 6 logging.StreamHandler.handle->logging.StreamHandler.release 6 logging.StreamHandler.handle->logging.StreamHandler.acquire 6 logging.StreamHandler.filter logging.StreamHandler.filter calls: 6 time: 0.000122s logging.StreamHandler.handle->logging.StreamHandler.filter 6 logging.Logger.debug logging.Logger.debug calls: 6 time: 0.018795s logging.Logger.debug->logging.Logger._log 6 logging.Logger.isEnabledFor logging.Logger.isEnabledFor calls: 6 time: 0.000362s logging.Logger.debug->logging.Logger.isEnabledFor 6 logging.StreamHandler.flush->logging.StreamHandler.release 6 logging.StreamHandler.flush->logging.StreamHandler.acquire 6 logging.Logger.isEnabledFor->logging.Logger.getEffectiveLevel 6 logging.Logger.callHandlers logging.Logger.callHandlers calls: 6 time: 0.007645s logging.Logger.handle->logging.Logger.callHandlers 6 logging.Logger.filter logging.Logger.filter calls: 6 time: 0.000104s logging.Logger.handle->logging.Logger.filter 6 logging.Logger.callHandlers->logging.StreamHandler.handle 6 logging.StreamHandler.format->logging.Formatter.format 6 dhcpcanon.dhcpcapfsm.DHCPCAPFSM.__init__ dhcpcanon.dhcpcapfsm.DHCPCAPFSM.__init__ calls: 1 time: 0.022029s dhcpcanon.dhcpcapfsm.DHCPCAPFSM.__init__->scapy.automaton.DHCPCAPFSM.__init__ 1 dhcpcanon.dhcpcapfsm.DHCPCAPFSM.__init__->logging.Logger.debug 2 dhcpcanon.clientscript.ClientScript.script_go dhcpcanon.clientscript.ClientScript.script_go calls: 1 time: 0.000016s dhcpcanon.dhcpcapfsm.DHCPCAPFSM.__init__->dhcpcanon.clientscript.ClientScript.script_go 1 dhcpcanon.clientscript.ClientScript.script_init dhcpcanon.clientscript.ClientScript.script_init calls: 1 time: 0.001991s dhcpcanon.dhcpcapfsm.DHCPCAPFSM.__init__->dhcpcanon.clientscript.ClientScript.script_init 1 dhcpcanon.dhcpcapfsm.DHCPCAPFSM.reset dhcpcanon.dhcpcapfsm.DHCPCAPFSM.reset calls: 1 time: 0.008135s dhcpcanon.dhcpcapfsm.DHCPCAPFSM.__init__->dhcpcanon.dhcpcapfsm.DHCPCAPFSM.reset 1 dhcpcanon.clientscript.ClientScript.__attrs_post_init__ dhcpcanon.clientscript.ClientScript.__attrs_post_init__ calls: 1 time: 0.002260s dhcpcanon.clientscript.ClientScript.__attrs_post_init__->logging.Logger.debug 1 dhcpcanon.clientscript.ClientScript.script_init->logging.Logger.debug 1 dhcpcanon.dhcpcap.DHCPCAP.__attrs_post_init__->logging.Logger.debug 1 dhcpcanon.dhcpcapfsm.DHCPCAPFSM.reset->DHCPCAP.__init__ 1 dhcpcanon.dhcpcapfsm.DHCPCAPFSM.reset->scapy.utils.str2mac 1 dhcpcanon.dhcpcapfsm.DHCPCAPFSM.reset->scapy.arch.linux.get_if_raw_hwaddr 1 dhcpcanon.dhcpcapfsm.DHCPCAPFSM.reset->logging.Logger.debug 1 ClientScript.__init__ ClientScript.__init__ calls: 1 time: 0.002316s dhcpcanon.dhcpcapfsm.DHCPCAPFSM.reset->ClientScript.__init__ 1 attr.validators._InstanceOfValidator.__call__ attr.validators._InstanceOfValidator.__call__ calls: 18 time: 0.000155s threading._RLock._note threading._RLock._note calls: 24 time: 0.000211s threading._RLock.release->threading._RLock._note 12 threading._RLock.acquire->threading._RLock._note 12 ClientScript.__init__->dhcpcanon.clientscript.ClientScript.__attrs_post_init__ 1 __main__ __main__ calls: 1 time: 0.000000s __main__->scapy.automaton.DHCPCAPFSM.run 1 __main__->dhcpcanon.dhcpcapfsm.DHCPCAPFSM.__init__ 1 DHCPCAPLease.__init__->attr.validators._InstanceOfValidator.__call__ 18 dhcpcanon-0.8.5/docs/source/images/classes_dhcpcanon.svg000066400000000000000000000371141326371602700233660ustar00rootroot00000000000000 classes_dhcpcanon 0 ClientScript env : NoneType env : _CountingAttr scriptname scriptname : _CountingAttr script_go() script_init() 1 DHCPCAP client_ip client_ip : _CountingAttr client_mac client_mac : _CountingAttr client_port : _CountingAttr event event : _CountingAttr iface iface : _CountingAttr lease lease : _CountingAttr prl prl : _CountingAttr server_ip server_ip : _CountingAttr server_mac server_mac : _CountingAttr server_port server_port : _CountingAttr xid xid : _CountingAttr gen_bootp() gen_bootp_unicast() gen_check_lease_attrs() gen_decline() gen_discover() gen_ether_ip() gen_ether_ip_unicast() gen_inform() gen_release() gen_request() gen_request_unicast() gen_udp() handle_ack() handle_offer() handle_offer_ack() 2 DHCPCAPFSM client current_state debug_level : int delay_before_selecting : NoneType delay_selecting : bool discover_attempts : int event offers : list request_attempts : int script : NoneType socket_kargs : dict time_sent_request : NoneType timeout_select : NoneType BOUND() END() ERROR() INIT() REBINDING() RENEWING() REQUESTING() SELECTING() action_transmit_discover() action_transmit_request() dict_self() get_timeout() lease_expires() on_ack_requesting() on_renewing() process_received_ack() process_received_nak() rebinding_time_expires() receive_ack_rebinding() receive_ack_renewing() receive_ack_requesting() receive_nak_rebinding() receive_nak_renewing() receive_nak_requesting() receive_offer() renewing_time_expires() reset() select_offer() send_discover() send_request() set_timeout() set_timers() timeout_delay_before_selecting() timeout_request_rebinding() timeout_request_renewing() timeout_requesting() timeout_selecting() 3 DHCPCAPLease address : _CountingAttr broadcast_address : _CountingAttr domain : _CountingAttr expiry expiry : _CountingAttr interface : _CountingAttr lease_time : _CountingAttr name_server : _CountingAttr network : _CountingAttr next_server : _CountingAttr rebind rebind : _CountingAttr rebinding_time rebinding_time : _CountingAttr renew renew : _CountingAttr renewal_time renewal_time : _CountingAttr router : _CountingAttr server_id : _CountingAttr subnet : _CountingAttr subnet_mask : _CountingAttr subnet_mask_cidr : _CountingAttr info_lease() set_times() dhcpcanon-0.8.5/docs/source/images/dhcpcanon.ico000066400000000000000000002516661326371602700216360ustar00rootroot00000000000000µ™ S(µ2 Eÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿãããnon$%$DED›››øøøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽŽŽ ±±±ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþijinonûûûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ()(´´´ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¸¸¸JKJÖÖÖÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôôô"#"fgféééÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿtutùùùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜÜÜ$%$ªªªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿMNM898ÇÇÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¼¼¼UVUáááÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûûû,-, wxwóóóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ™™™ýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿííí454ÂÂÂÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿijiLMLÝÝÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕÕÕnonðððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿFGFijiÄÄÄöööÿÿÿûûûËËËvwvûûûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ­­­010ØØØÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîîîopo()(´´´ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùù%&%343ðððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÖÖÖGHGDEDÓÓÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ……… ÛÛÛÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ´´´&'&fgféééÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèè臇‡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúúú‘‘‘ˆˆˆøøøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^_^"#"÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìììopo$%$ªªªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÌÌ®®®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÎÎÎ>?>@A@ÎÎÎÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþ<=<BCBÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³³³!"!UVUáááÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢¢¢ÑÑÑÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöööˆˆˆ   wxwóóóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõklkÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåååfgf™™™ýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ{|{ëëëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÊÊÊ:;:454ÂÂÂÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿááá ’’’ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæææ‹‹‹ÇÇÇÿÿÿÿÿÿÿÿÿÿÿÿþþþªªªLMLÝÝÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿSTS)*)úúúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷———ôôôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôôô€€€ nonðððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾¾¾¸¸¸ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÊÊÊ~~~èèèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâââ^_^ûûûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüü/0/NONÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©©©šššÿÿÿÿÿÿÿÿÿ€€€êêêÿÿÿâââÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾¾¾./.()(´´´ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ———ÙÙÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷ÈÈÈÿÿÿÿÿÿÞÞÞ}}}ºººÿÿÿÚÚÚ|||ÈÈÈÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýý¢¢¢DEDÓÓÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïïïvwvÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ­­­ŒŒŒŒŒŒˆˆˆŒŒŒˆˆˆˆˆˆˆˆˆ”””’’’ˆˆˆ‚‚‚íííÿÿÿÿÿÿ¦¦¦–––ûûûÿÿÿ£££˜˜˜üüüýýý®®®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïïïwxwfgféééÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpqpðððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòòòûûûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷‹‹‹ÅÅÅÿÿÿööö€€€ËËËÿÿÿìììyyyÜÜÜèèèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙÙÙKLKˆˆˆøøøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×××ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ£££ˆˆˆŽŽŽˆˆˆŠŠŠ‘‘‘ˆˆˆˆˆˆ“““ˆˆˆˆˆˆ‡‡‡|||ëëëÿÿÿîîîvvvëëëÿÿÿÇÇÇööö¢¢¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»»»+,+%&%ªªªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿIJI121ýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýîîîîîîîîîîîîîîîùùùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿßßßzzzöööÿÿÿ˜˜˜¦¦¦þþþ‡‡‡ÎÎÎöööŠŠŠýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúúú‘‘‘ABAÎÎÎÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³³³ÂÂÂÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëë눈ˆ‰‰‰ŒŒŒ™™™™™™™™™™™™™™™ŒŒŒˆˆˆ®®®ƒƒƒüüüþþþƒƒƒÑÑÑúúú~~~ÚÚÚôôôwwwíííÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìììopoUVUáááÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùù'('YZYÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ«««‹‹‹ˆˆˆˆˆˆˆˆˆ‹‹‹¤¤¤½½½ÔÔÔñññÿÿÿÿÿÿÿÿÿõõõÈÈÈÿÿÿÿÿÿÙÙÙþþþ€€€äääþþþïïïµµµÐÐÐÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÎÎÎ>?> wxwóóóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‹‹‹ àààÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåååúúúÿÿÿÿÿÿÿÿÿÿÿÿûûûæææÏÏϵµµ™™™ŠŠŠ‰‰‰ŽŽŽÄÄÄüüüÿÿÿÒÒÒ¨¨¨ÿÿÿ–––ÕÕÕÿÿÿŽŽŽÙÙÙëë눈ˆéééÛÛÛÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³³³!"!™™™ýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéé退€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ«««‰‰‰nnnˆˆˆ¼¼¼ïï‰ˆˆˆ³³³òòòùùùˆˆˆŒŒŒxxxþþþÏÏÏ£££ÿÿÿ½½½µµµÿÿÿöööðððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöööˆˆˆ  454ÂÂÂÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿdedôôôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüü¤¤¤___˜˜˜‡‡‡‹‹‹ÇÇÇÑÑÑ”””†††•••åååííí›››ÿÿÿÿÿÿúúúÿÿÿ±±±ÇÇÇùùù‰‰‰ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿååå^_^LMLÝÝÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏÏϨ¨¨ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÐÐÐŠŠŠˆˆˆ¿¿¿ýýýÿÿÿÜÜ܃ƒƒÿÿÿûûû¾¾¾ˆˆˆ‡‡‡ËËËïïï   „„„­­­ãããžžžÿÿÿ¢¢¢ÚÚÚúúú€€€ÿÿÿ±±±ÕÕÕùùùÂÂÂÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÊÊÊ:;:nonðððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ?@?:;:þþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûûûÅÅÅ………   þþþÓÓÓ›››±±±¼¼¼òòò¸¸¸„„„–––ñññÑÑÑTTT³³³ÿÿÿ‡‡‡ÿÿÿàààÿÿÿ‡‡‡ÿÿÿ———ãããÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþªªªüüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¨¨¨ËËËÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûûûÿÿÿÿÿÿÿÿÿÿÿÿÖÖÖ{{{ûûû———ÑÑÑ‚‚‚ÎÎΑ‘‘‡‡‡£££áááŽŽŽ‰‰‰ÕÕÕÝÝÝÿÿÿ‹‹‹ÿÿÿ‡‡‡ÿÿÿÿÿÿŒŒŒÿÿÿˆˆˆÿÿÿòòò¹¹¹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôôô€€€ )*)µµµÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿööö ! efeÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿËËËŠŠŠ†††øøøÿÿÿãããùùù¤¤¤ÎÎÎÇÇÇ………¥¥¥’’’„„„ÉÉÉšššˆˆˆééé´´´ÿÿÿ………ÿÿÿÿÿÿ•••ââ⌌ŒÿÿÿÒÒÒ¹¹¹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâââ^_^DEDÓÓÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€€€  çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿããã~~~ÌÌÌÿÿÿŸŸŸÚÚÚ©©©ÒÒÒ{{{þþþzzz“““}}}ÇÇÇ‘‘‘˜˜˜ûûûÀÀÀ†††öööûûû™™™åååää䦦¦ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾¾¾./.fgféééÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿããã ŒŒŒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëë눈ˆšššûûûÿÿÿ”””ÉÉÉüüü€€€üüü‚‚‚ÑÑÑ®®®ììì‘‘‘÷÷÷ÌÌÌ¥¥¥©©©yyyÃÃÃxxxáááîîîyyyýýý™™™ááኊŠÿÿÿ†††ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýý¢¢¢ˆˆˆøøøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿYZY#$#øøøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáááwwwìììÿÿÿ€€€ðð𨨨×××™™™åå倀€ûûû†††óóóKKK~~~ÕÕÕ|||ÒÒÒµµµ~~~ëëëÃÃÃÂÂÂÖÖÖ²²²‰‰‰ööö‰‰‰øøøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîîîwxw%&%ªªªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÆÆÆ³³³ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷wwwöööñññ‰‰‰üüü„„„õõõ‰‰‰ÜÜÜŸŸŸÓÓÓ®®®ðððŒŒŒÆÆÆ~~~ôôô˜˜˜ÝÝÝ‹‹‹ââ⨨¨ýýýŒŒŒààà   ØØØ«««ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙÙÙKLKABAÎÎÎÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýý565DEDÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³³³üüüëë뇇‡ÿÿÿ”””åååšššáááïïƒþþþààà   ÿÿÿ‚‚‚ÚÚÚ¡¡¡ÑÑѺºº‹‹‹ÈÈÈÁÁÁÿÿÿ‹‹‹ÿÿÿ‡‡‡ÿÿÿ………ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»»»*+*]^]æææÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓÓÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿââ⇇‡ÿÿÿ®®®ÊÊÊõõõŠŠŠöööŠŠŠììì‘‘‘ÅÅÅ»»»­­­ÑÑÑ………ëëë¿¿¿ÇÇÇ‹‹‹ÀÀÀÉÉÉ‹‹‹¢¢¢æææûûûŽŽŽÏÏÏŽŽŽùùù’’’òòòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúúú‘‘‘ wxwóóóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòòòpqpÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»»»»»»ÿÿÿ}}}ÿÿÿôôô‹‹‹óó󈈈õõõ‚‚‚þþþzzzôôô•••™™™ÆÆÆ²²²‡‡‡÷÷÷‰‰‰‡‡‡ÿÿÿààપª‹‹‹¾¾¾ËËËÎÎι¹¹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìììopo™™™ýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿvwvìììÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ~~~ôôô···ÇÇÇää䜜œèèè“““ééé´´´ÄÄÄááázzz‡‡‡–––ÔÔÔ¶¶¶ÉÉɈˆˆÿÿÿ¨¨¨ÞÞÞ‹‹‹ÒÒÒ¹¹¹ûûûŽŽŽÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÎÎÎ>?>454ÂÂÂÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜÜÜ———ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëëë‹‹‹þþþ‚‚‚ÿÿÿ‚‚‚üüü‘‘‘ééé“““æææ‰‰‰ÅÅÅþþþïïïÿÿÿÆÆÆ‡‡‡òòò€€€³³³ÌÌÌÿÿÿ‡‡‡ÿÿÿŠŠŠäää§§§ÿÿÿ‹‹‹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³³³!"!LMLÝÝÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿNON+,+ûûûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢¢¢×××’’’ðððÕÕÕ©©©õõõ‚‚‚÷÷÷|||ÿÿÿ¶¶¶ˆˆˆ‰‰‰³³³ÐÐÐ}}}èèèÿÿÿâââšššÈÈÈ‹‹‹åå奥¥ÿÿÿŒŒŒéééÅÅÅÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöööˆˆˆ  nonðððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¼¼¼ºººÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýý|||ää䟟Ÿÿÿÿ€€€üüü···ºººÔÔÔ„„„÷÷÷ÿÿÿôôô³³³ƒƒƒ©©©ðððwwwïïïüüüyyyéé阘˜ŠŠŠÈÈÈÂÂÂÿÿÿ‹‹‹ñññšššÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿååå^_^üüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûûû-.-STSÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÆÆÆ²²²ùùùÜÜÜ™™™ÿÿÿ×××ççç‘‘‘ŒŒŒ–––ÑÑÑúúú¢¢¢„„„óóóûûûàààëëëŠŠŠªªªàààþþþŒŒŒÿÿÿ‹‹‹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÊÊÊ9:9)*)µµµÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ’’’ÛÛÛÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€€€ÚÚÚÿÿÿ£££ÂÂÂþþþ   ÌÌÌÌÌ̪ªªˆˆˆ†††ÜÜÜÿÿÿãããxxxâââÙÙÙ™™™ôôô“““‹‹‹üüü××ׯ¯¯ÿÿÿ‹‹‹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþªªªDEDÓÓÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîîî{|{ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØØØëëëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìì숈ˆ³³³²²²ÿÿÿŸŸŸ———ôôôâââ»»»»»»ÝÝÝÿÿÿÿÿÿãã㌌Œ•••úúúÞÞÞ~~~ýýý–––ããㆆ†ÿÿÿ”””ðððþþþŒŒŒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôôô€€€ fgféééÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿklkñññÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýý565‘‘‘úúúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÁÁÁšššÊÊʵµµÛÛÛ“““ŠŠŠ‰‰‰ˆˆˆˆˆˆ‰‰‰ˆˆˆ¢¢¢ôôôììì”””üüü­­­®®®ÔÔÔ­­­ÿÿÿ‚‚‚   ëëë‘‘‘ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâââ^_^ˆˆˆøøøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕÕÕ¢¢¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸŸŸ)*)¸¸¸ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‚‚‚ååå”””±±±zzzÑÑÑÿÿÿŸŸŸ»»»ÌÌÌÈÈÈ¢¢¢ˆˆˆ‡‡‡šššëëëäää»»»ÿÿÿüüüÌÌ̳³³‡‡‡ÛÛÛ®®®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾¾¾./.%&%ªªªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿFGF343ýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿßßßTUTLMLÚÚÚÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûûû|||ÚÚÚ¦¦¦¨¨¨¦¦¦„„„ÇÇÇËËË»»»ÀÀÀæææ÷÷÷ÌÌÌ–––„„„žžžõõõÿÿÿ£££ÄÄÄøøøyyyùùù‡‡‡¦¦¦áááÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýý¢¢¢ABAÏÏÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ®®®ÈÈÈÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëëëúúúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþ“““žžž¾¾¾€€€ààุ¸‹‹‹ŠŠŠ‰‰‰‹‹‹’’’½½½ñññÿÿÿÿÿÿæææ„„„ºººïïïwwwêê꟟ŸÛÛÛ………ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîîîwxw]^]æææÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøøø"#"^_^ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿijišššŒŒŒLMLˆˆˆêêêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍÍÍÛÛÛ~~~²²²ˆˆˆ‹‹‹«««ÇÇÇÝÝÝÝÝÝÉÉɰ°°ŒŒŒ†††›››õõõ®®®„„„ôôôää䎎Žáá᜜œÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØØØKLK „„„öööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ††† åååÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÑÑѪªªïï‰ßßß ´´´ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉÉÉ€€€ôôôÁÁÁƒƒƒ–––çççßßß¹¹¹¬¬¬ÁÁÁÙÙÙüüüããã™™™‚‚‚ËËËÿÿÿåååyyyúúú‚‚‚øøøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»»»*+*™™™ûûûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæææ  ………ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïïïÿÿÿÿÿÿABA?@?ÿÿÿÿÿÿÿÿÿWXW ÖÖÖÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿííí„„„¨¨¨üüüììì™™™ˆˆˆŠŠŠ‹‹‹ŽŽŽ‰‰‰ŠŠŠˆˆˆ£££êêêÿÿÿòòò’’’úúúÀÀÀ´´´ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúúú‘‘‘()(ÀÀÀÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ_`_!"!÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèèè´´´­­­ÏÏÏÿÿÿÿÿÿûûûÝÝÝÓÓÓÖÖÖ@A@ijiÿÿÿïïïôôôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅÅÅÍÍÍ„„„ÖÖÖÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüü½½½†††ŒŒŒëëëÿÿÿ»»»‘‘‘ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìììopoghg÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍÍÍ­­­ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿcdcfgfÿÿÿÿÿÿ¿¿¿&'&-.-<=<ÿÿÿfgf¤¤¤þþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîîîyyyÔÔÔõõõ¯¯¯ˆˆˆŠŠŠ™™™™™™ŠŠŠÈÈÈþþþÿÿÿ¾¾¾€€€»»»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÎÎÎ>?>898éééÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþ<=<ABAÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþ>?>êêêÿÿÿÓÓÓ GHG×××9:9ÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþ¢¢¢………ßßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüüÌÌÌ‹‹‹………ÁÁÁÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³³³!"!232öööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ£££ÑÑÑÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöööMNMŽŽŽÿÿÿÿÿÿ:;:DED‚‚‚ááá`a`æææÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷¡¡¡‡‡‡ŠŠŠŒŒŒ•••‹‹‹ŠŠŠŒŒŒºººùùùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöööˆˆˆ  lmlÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõjkjÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûûûvwv()(úúúÿÿÿÔÔÔ ÚÚÚÿÿÿ'(' çççÿÿÿ     ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøøøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäää^_^ØØØÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ|}|ëëëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÚÚÚÉÉÉÿÿÿÿÿÿµµµTUTÿÿÿççç   bcbÿÿÿÿÿÿZ[ZZ[Z ¼¼¼ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÊÊÊ9:9mnmÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿááá ‘‘‘ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÌÌ¥¥¥:;:ëëëÿÿÿÿÿÿ•••’’’ååå]^]RSRÿÿÿÿÿÿ÷÷÷^_^ÓÓÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýŸŸŸüüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿbcb%&%úúúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ?@?©©©êêêÿÿÿÿÿÿÌÌÌ[\[ÿÿÿÿÿÿûûûÿÿÿÿÿÿ………jkjóóóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäää787ÈÈÈÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØØØ–––ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíííDED ¤¤¤tut]^]ÿÿÿÿÿÿ÷÷÷-.-ÿÿÿÿÿÿÿÿÿÿÿÿååå  )*)úúúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîîî+,+‹‹‹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿQRQæææÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþººº‘‘‘ãããÙÙÙ½½½ÿÿÿýýýÿÿÿÿÿÿÿÿÿÿÿÿ······ÿÿÿÿÿÿÿÿÿÞÞÞÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿËËËMNMÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿßßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ®®®›››ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéééfgfNONÿÿÿÿÿÿÿÿÿààà MNMÚÚÚÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿIJIýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ„„„"#"ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿööö454[\[ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüüÿÿÿÿÿÿÿÿÿVWVtutñññÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿžžžÒÒÒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>?> ýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèèèABA ! çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜˜˜›››ýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿËˢ˜˜ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÐÐÐÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüü®®®STSDED|}|çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§§§stsÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÖÖÖabaÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿqrqÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùù%&%ÝÝÝüüüÞÞÞðððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃÃÃ*+*ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÛÛÛÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‹‹‹  çççµµµ’’’ŸŸŸNONæææÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŠŠŠñññÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ]^],-,íííÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùùÿÿÿèè興ˆÿÿÿÿÿÿ‹‹‹ãããÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ./.¼¼¼ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛÛÛ*+*ÐÐÐÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬¬¬BCBòòòcdc#$#øøøÿÿÿÇÇÇ232#$#jkjÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ­­­‡‡‡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽŽŽopoðððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷!"!+,+°°°ÿÿÿ÷÷÷$%$:;:üüüÝÝÝ121ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùù%&%OPOÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€€€‘‘‘üüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍÍÍDEDÿÿÿÿÿÿœœœ'('¡¡¡ÿÿÿžžž:;:ÄÄÄ;<;hiháááÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ†††ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¦¦¦ 121ÁÁÁÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ½½½  ÔÔÔÿÿÿþþþ-.-JKJ©©©bcb  ÃÃÃÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèèèâââÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅÅÅMNMÞÞÞÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍÍÍ)*)lmlÿÿÿÿÿÿÜÜÜ!"!BCB  õõõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ_`_«««ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæææfgf wxwòòòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúúú“““íííÿÿÿÿÿÿ¿¿¿./.ÎÎÎËËË÷÷÷565¹¹¹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÌÌtutÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÌÌ<=<¢¢¢þþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕÕÕÚÚÚ¡¡¡sts‘‘‘ñññýýý$%$ êêêûûû./.­­­ÿÿÿÿÿÿÌÌÌ’’’ûûûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþ<=<=>=ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþªªª 565ÆÆÆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþ./.PQPWXWÖÖÖßßß232/0/îîîÿÿÿþþþ;<;*+*ºººÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ£££ ûûûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõˆˆˆ UVUâââÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÐÐÐ343ýýý=>=‚‚‚ÿÿÿÿÿÿ¦¦¦  nonÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõÐÐÐÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿããã]^] €€€÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâââaba ÃÃÃÞÞÞŸŸŸCDC&'&™™™þþþÿÿÿÿÿÿZ[Zîîîÿÿÿÿÿÿýýýñññÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ|}|™™™ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈÈÈ787¢¢¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÞÞÞÿÿÿÚÚÚ&'&úúú’’’‚‚‚ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªªª ! •••ÿÿÿÿÿÿÿÿÿŸŸŸ›››ýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâââ bcbÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýý¡¡¡ABAÑÑÑÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúúú&'& ›››ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöööüüüÿÿÿÿÿÿòòò232ÂÂÂÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿTUT+,+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóóó^_^éééÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄÄÄ$%$ñññÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäääIJIWXWáááÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃÃÃñññÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÚÚÚUVUˆˆˆùùùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÝÝÝ_`_ LMLÛÛÛÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿMNM ÍÍÍÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýý343½½½ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¼¼¼,-,'('µµµÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÑÑÑ 121ýýýÿÿÿõõõþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜˜˜†††ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûûû™™™DEDÕÕÕÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿABA?@?éééfgfÂÂÂÿÿÿÿÿÿ_`_;<;ËËËÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñññPQPÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿííínonopoðððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþÿÿÿÿÿÿ¬¬¬ÏÏÏÿÿÿÿÿÿÿÿÿÿÿÿÊÊÊ’’’ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿqrqÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×××HIH‘‘‘üüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿhih§§§÷÷÷!"!fgfÿÿÿÿÿÿÿÿÿÿÿÿþþþ9:9  ×××ÿÿÿÿÿÿýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×××âââÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²"#"121ÁÁÁÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍÍÍBCBêêêÿÿÿÿÿÿÿÿÿÿÿÿýýý–––qrqÿÿÿÿÿÿÏÏÏ&'&¯¯¯ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿIJI«««ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùùMNMÞÞÞÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿqrqŽŽŽÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîîîwxwïïïÿÿÿÿÿÿ>?>DEDÓÓÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ´´´tutÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæææfgf wxwòòòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùùYZY()(úúúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ! klkìììÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúúú'('=>=ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÌÌ;<;¢¢¢þþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýuvu¶¶¶ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ]^]“““ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ûûûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþªªª 565ÆÆÆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÑÑÑ}~}ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ™™™+,+ RSR»»»îîîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿêêêÏÏÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõ ^_^çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕÕÕSTSÞÞÞRSR <=<°°°ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿfgf˜˜˜ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿããã]^] €€€÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôôôÿÿÿÿÿÿýýýÿÿÿÿÿÿÙÙÙ$%$\]\õõõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏÏÏbcbÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈÈÈ787#$#ªªªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙÙÙ<=<ýýýÿÿÿÿÿÿ=>=çççÿÿÿÒÒÒijiOPO ! lmlÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ?@?+,+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýý¡¡¡BCBÑÑÑÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿKLK”””ÿÿÿÿÿÿSTSÐÐÐÿÿÿÿÿÿÿÿÿÿÿÿýýýéééÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©©©ñññÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóóó^_^êêêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿËËË  µµµõõõ ! ÆÆÆÿÿÿÿÿÿ   ÚÚÚÿÿÿÿÿÿ+,+ÈÈÈÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿööö ! ½½½ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÚÚÚUVUˆˆˆùùùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýMNM  ñññÿÿÿÙÙÙefelmlíííÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ†††ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¼¼¼,-,'('µµµÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñññCDCŒŒŒâââÿÿÿ^_^`a`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäää OPOÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûûû™™™DEDÕÕÕÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüü˜˜˜232*+*™™™îîî®®®òòòvwvUVUóóóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ[\[ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿííínonopoðððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿopo ···ÿÿÿxyxŽŽŽÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÆÆÆáááÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×××HIH‘‘‘üüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@A@OPO˜˜˜ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýý676ªªªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²"#"232ÂÂÂÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõHIHçççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿžžž *+*"#"tutÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùùMNMßßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüü‰‰‰ÁÁÁÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòòòLML½½½ýýýÿÿÿÿÿÿ÷÷÷µµµBCB=>=ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæææfgf wxwóóóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùùÍÍÍËËËûûûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿwxw¤¤¤ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ——— ûûûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÌÌ;<;¢¢¢þþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÝÝÝ¥¥¥ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡¡¡ÏÏÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþªªª 565ÆÆÆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿOPOPQPÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿUVU˜˜˜ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõ ^_^çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ½½½ÄÄÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÑÑÑabaÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàààUVU €€€÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüü-.-  üüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ$%$676ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀÀÀ010$%$ªªªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ“““*+*ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿNON#$#ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýý¡¡¡BCBÑÑÑÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîîî&'&ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿRSRÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðððrsrfgfêêêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿlmlöööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ121ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÚÚÚUVUˆˆˆùùùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕÕÕ©©©ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿççç+,+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ···)*)'('µµµÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGHG121ûûûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿyzyIJIÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûûû™™™DEDÖÖÖÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ°°°xyxÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈÈȉ‰‰ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿííínonopoðððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùù&'&vwvúúúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÁÁÁØØØÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÑÑÑABA‘‘‘üüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿˆˆˆ-.-¤¤¤òòòÿÿÿÿÿÿÿÿÿÌÌÌcdc898ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²"#"232ÂÂÂÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèèè ! -.-¼¼¼ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷††† MNMßßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿabaKLKÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæææfgf wxwóóóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍÍÍêêêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉÉÉ898¢¢¢þþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþ=>= ÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþªªª 676ÇÇÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥¥¥ÆÆÆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôôô|}| cdcèèèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôôô9:9ÚÚÚÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿßßßUVU €€€øøøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿfgfüüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀÀÀ010$%$±±±ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽŽŽ676šššöööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýý¡¡¡EFEÏÏÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìììbcb*+*ÚÚÚÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóóóCDCŒŒŒ¸¸¸ºººššš]^] *+*ÙÙÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÚÚÚUVU*+*ÙÙÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»»»,-,*+*ØØØÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûûû™™™*+*ØØØÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿííínon*+*×××ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÖÖÖHIH*+*×××ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²"#"*+*ÖÖÖÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùù*+*ÖÖÖÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæææfgf*+*ÕÕÕÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÌÌ;<;"#"wxwÕÕÕÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþ£££)*)ÔÔÔÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõˆˆˆ  !"!nonÃÃÃÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿããã]^]'('}~}ÒÒÒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØØØtut454454^_^ÜÜÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿdhcpcanon-0.8.5/docs/source/images/dhcpcanon_logo.png000066400000000000000000000021231326371602700226460ustar00rootroot00000000000000‰PNG  IHDR—µýƒsBIT|dˆ pHYsÎÎ3Kò#tEXtSoftwarewww.inkscape.org›î<tEXtTitledhcpcanon¢´• tEXtAuthorjuganw[³tEXtCreation Time2017Ø‘VRtEXtCopyrightCC Attribution-ShareAlike http://creativecommons.org/licenses/by-sa/4.0/ÃTb"IDAT8­”KH\W†¿s5¤¶ª1Ò6«$…4U\$Ý´«Ršî»hZ»(hbC !]¤`»qÜ4ºÓap¡•À ¢ƒÏÔ(>A0>¦q_cǹý»«3™$æÑÜ÷~ÿ¹ÿ½çÀ«ó1†~ ¥ÀÛ¯áÛç],ê1Ø…E…Ê?ž/ ÎHbn'_Vü¿aØ:}æ´ÚÚÚ$IÊ>’âŒ8ðˈ- ÃBî±\UVVjggG©TüT‘&ïììT †¿“‚!`eY*ý¾TËËËiâÑÑQÙ¶­X,¦'Oìx<¹ÝnaXx–ø=,`ø÷Òg—‡õ$UUUº÷ë=µ··K’êêêžVÓŸ™=C%†í³œUKKK†Øãñh{{[r»ÝÚÜÜ”$Ù¶­ /¤À'{^ÌËÏ“ËåJëÙ¶mµ¶¶juuU>ŸO ZZZÊ÷ûý2–I øà"†±ì#Ù*+/S4M{hddD·¾­ËŸ_VMMfff2Ä©\ûöš° ¼ †¨±Œ@ÚSSSŠÇãêèèPùå …BÏ;LNN:cÙÀðUAAëëëx½^Æ'Æ9õþ)Š‹‹¹ÿÇ}rrrýŸ“É$ÍÍÍ{C'Ú»<ƒáŸ«%W%IMMMºYqSÕÕÕZXXP0”ßïW$‘mÛÏ\yOO ‹ Õ? ¢-\ÔÕÕ¥­­­ý:fggÕß߯¡¡! jmm-C<==­’¯Kñ_ÀO{»\ Î}xN‰DB’Ç …äóùÔÛÛ«`0˜&ŽÅbºûË]}ã¨0<Ê€¬çUøÕÖÖJ’500 ¯×«H$¢ùùyIÒîî®êë뻉… 8vè †¼ü Automaton_metaclass INIT INIT SELECTING SELECTING INIT->SELECTING timeout_delay_before_selecting/10.0s >[action_transmit_discover] END END ERROR ERROR ERROR->END REBINDING REBINDING REBINDING->INIT receive_nak_rebinding REBINDING->REBINDING timeout_request_rebinding/75600.0s >[action_transmit_request] BOUND BOUND REBINDING->BOUND receive_ack_rebinding >[on_ack_requesting] RENEWING RENEWING BOUND->RENEWING renewing_time_expires/604800.0s >[action_transmit_request] SELECTING->ERROR timeout_selecting/60.0s >[action_transmit_discover] SELECTING->SELECTING receive_offer >[action_transmit_request] SELECTING->SELECTING timeout_selecting/60.0s >[action_transmit_discover] REQUESTING REQUESTING SELECTING->REQUESTING receive_offer >[action_transmit_request] SELECTING->REQUESTING timeout_selecting/60.0s >[action_transmit_discover] REQUESTING->INIT receive_nak_requesting REQUESTING->ERROR timeout_requesting/60.0s >[action_transmit_request] REQUESTING->BOUND receive_ack_requesting >[on_ack_requesting] REQUESTING->REQUESTING timeout_requesting/60.0s >[action_transmit_request] RENEWING->INIT receive_nak_renewing RENEWING->REBINDING rebinding_time_expires/1058400.0s >[action_transmit_request] RENEWING->BOUND receive_ack_renewing >[on_renewing] RENEWING->RENEWING timeout_request_renewing/226800.0s >[action_transmit_request] dhcpcanon-0.8.5/docs/source/images/organigram_dhcpcanon.svg000066400000000000000000003143731326371602700240640ustar00rootroot00000000000000 dhcpcanon-0.8.5/docs/source/images/packages_dhcpcanon.svg000066400000000000000000000106561326371602700235110ustar00rootroot00000000000000 packages_dhcpcanon 0 /home/user/_my/code/dhcp-related/dhcpcanon/dhcpcanon/__init__.py 1 /home/user/_my/code/dhcp-related/dhcpcanon/dhcpcanon/_version.py 2 /home/user/_my/code/dhcp-related/dhcpcanon/dhcpcanon/clientscript.py 3 /home/user/_my/code/dhcp-related/dhcpcanon/dhcpcanon/conflog.py 4 /home/user/_my/code/dhcp-related/dhcpcanon/dhcpcanon/constants.py 5 /home/user/_my/code/dhcp-related/dhcpcanon/dhcpcanon/dhcpcanon.py 6 /home/user/_my/code/dhcp-related/dhcpcanon/dhcpcanon/dhcpcap.py 7 /home/user/_my/code/dhcp-related/dhcpcanon/dhcpcanon/dhcpcapfsm.py 8 /home/user/_my/code/dhcp-related/dhcpcanon/dhcpcanon/dhcpcaplease.py 9 /home/user/_my/code/dhcp-related/dhcpcanon/dhcpcanon/dhcpcaputils.py 10 /home/user/_my/code/dhcp-related/dhcpcanon/dhcpcanon/netutils.py 11 /home/user/_my/code/dhcp-related/dhcpcanon/dhcpcanon/timers.py dhcpcanon-0.8.5/docs/source/implementation.rst000066400000000000000000000051521326371602700215020ustar00rootroot00000000000000.. _implementation: Message types and options details in all layers ------------------------------------------------ DHCPDISCOVER ~~~~~~~~~~~~~ Always broadcast in AP:: Ehter: src=client_mac, dst="ff:ff:ff:ff:ff:ff" IP: src="0.0.0.0", dst="255.255.255.255" UDP: sport=68, dport=67 BOOTP: Client Hardware address (chaddr in scapy) DHCP: Message Type option (message-type in scapy) DHCPREQUEST ~~~~~~~~~~~~~ In SELECTING state: Broadcast in AP:: Ehter: src=client_mac, dst="ff:ff:ff:ff:ff:ff" IP: src="0.0.0.0", dst="255.255.255.255" UDP: sport=68, dport=67 BOOTP: Client Hardware address (chaddr in scapy) DHCP: Message Type option (message-type in scapy) DHCP: Server Identifier option (server_id in scapy, siaddr in server BOOTP offer) DHCP: Requested IP option (requested_addr in scapy, yiaddr in server BOOTP offer) In RENEWING state: Unicast to server id:: Ehter: src=client_mac, dst=server_mac IP: src=client_ip, dst=server_ip UDP: sport=68, dport=67 BOOTP: Client Hardware address (chaddr in scapy) DHCP: Message Type option (message-type in scapy) Client IP address (ciaddr=client_ip)? In REBINDING state: broadcast:: Ehter: src=client_mac, dst="ff:ff:ff:ff:ff:ff" IP: src="0.0.0.0", dst="255.255.255.255" UDP: sport=68, dport=67 BOOTP: Client Hardware address (chaddr in scapy) DHCP: Message Type option (message-type in scapy) Client IP address (ciaddr=client_ip)? DHCPDECLINE ~~~~~~~~~~~~~ Always broadcast?:: Ehter: src=client_mac, dst="ff:ff:ff:ff:ff:ff" IP: src="0.0.0.0", dst="255.255.255.255" UDP: sport=68, dport=67 BOOTP: Client Hardware address (chaddr in scapy) DHCP: Message Type option (message-type in scapy) DHCP: Server Identifier option (server_id in scapy, siaddr in server BOOTP offer) DHCP: Requested IP option (requested_addr in scapy, yiaddr in server BOOTP offer) DHCPRELEASE ~~~~~~~~~~~~~ Always unicast, is not being used:: Ehter: src=client_mac, dst=server_mac IP: src=client_ip, dst=server_ip UDP: sport=68, dport=67 BOOTP: Client Hardware address (chaddr in scapy) DHCP: Message Type option (message-type in scapy) DHCP: Server Identifier option (server_id in scapy, siaddr in server BOOTP offer) DHCPINFORM ~~~~~~~~~~~~~ Always broadcast in Anonymity Profile, is not being used:: Ehter: src=client_mac, dst="ff:ff:ff:ff:ff:ff" IP: src=client_ip, dst="255.255.255.255" UDP: sport=68, dport=67 BOOTP: Client Hardware address (chaddr in scapy) BOOTP: Client IP address (ciaddr=client_ip) DHCP: Message Type option (message-type in scapy) dhcpcanon-0.8.5/docs/source/index.rst000066400000000000000000000011071326371602700175600ustar00rootroot00000000000000.. DHCP anonymity profile documentation master file, created by sphinx-quickstart on Sat Jul 23 19:57:52 2016. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. .. include:: README.rst Contents: ========= .. toctree:: :maxdepth: 2 install download running install_run_cases todo contributing soa specification questions implementation privileges integration api diagrams Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` dhcpcanon-0.8.5/docs/source/install.rst000066400000000000000000000074441326371602700201310ustar00rootroot00000000000000.. _install: Install dhcpcanon ================= The recommended way to install ``dhcpcanon`` is with your package source distribution, as it will also install other system files. Currently is availabe for Debian unstable/testing. It can be installed with a package manager or in command line:: sudo apt install dhcpcanon The main script will be installed in ``/sbin/dhcpcanon``, a systemd service will be enabled and run by default, so there is no need to run anything manually. **Important**: when running ``dhcpcanon`` the hardware address (`MAC `__) should be randomized. You can use `macchanger `__, `macouflage `__ or other. Installation from source code in Debian/Ubuntu =============================================== In case you would like to have a newer version or it is not packaged for your distribution, you can install it from the source code. Install system dependencies, in Debian/Ubuntu:: sudo apt install python3-dev Obtain the source code:: git clone https://github.com/juga0/dhcpcanon/ Install ``dhcpcanon`` and system files:: sudo ./install.sh for advanced users -------------------- Follow the two first steps in the previous paragraph. To install ``dhcpcanon`` and the ``systemd`` service:: sudo make install WITH_SYSTEMD=true In Debian this will install all the required files under ``/usr/local``. ``WITH_SYSTEMD`` will install a systemd service and enable it, to run it:: systemctl start dhcpcanon It's possible to also install support for udev:: sudo apt install sudo make install WITH_SYSTEMD=true sudo make install WITH_SYSTEMD=true WITH_SYSTEMD_UDEV=true And apparmor profile:: sudo apt install apparmor sudo make install WITH_APPARMOR=true In the case that you would like to install without root privileges, you can install it without the systemd service and you can specify an alternative location, for instance:: make --prefix=/home/user/.local install Note however that without systemd ``dhcpcanon`` will need to be run with root privileges, while the systemd service drop ``dhcpcanon`` root privileges and only keeps the required network capabilities. You would also need to install `resolvconf-admin `_ to be able to run it as non root user and set up DNS servers provided by the DHCP server. It will be possible to set up DNS servers with ``systemd`` too soon. An alternative to do not run ``dhcpcanon`` with root privileges nor systemd, is to use `ambient-rs wrapper `_ and run:: RUST_BACKTRACE=1 ./target/debug/ambient \ -c NET_RAW,NET_ADMIN,NET_BIND_SERVICE \ /usr/bin/python3 -m dhcpcanon.dhcpcanon -v Installation with pip ========================== The pip package does not install either system files and it can be installed without root, but it still needs to be run as root, as commented in the last section.:: pip3 install dhcpcanon In Debian this will install the files in ``/home/youruser/.local`` Note also that if you install it in a virtualenv, when executing ``dhcpcanon`` with ``sudo``, won't use the virtualenv. To keep the virtualenv run it with:: sudo /pathtovirtualenv/bin/dhcpcanon Installation for developers ============================= It is recommended to install ``dhcpcanon`` in a python virtual environment. Check https://virtualenv.pypa.io/en/latest/installation.html. In Debian:: sudo apt install python3-virtualenv Create a virtual environment:: mkdir ~/.virtualenvs virtualenv ~/.virtualenvs/dhcpcanonenv -p /usr/bin/python3 source ~/.virtualenvs/dhcpcanonenv/bin/activate Get the sources:: git clone https://github.com/juga0/dhcpcanon Install it:: pip3 install -e . dhcpcanon-0.8.5/docs/source/install_run_cases.rst000066400000000000000000000016221326371602700221630ustar00rootroot00000000000000.. _install_run_cases: Installation and running cases =================================== system files ------------- sbin/dhcpcanon-script systemd/dhcpcanon.service tmpfiles.d/dhcpcanon.conf systemd/network/90-dhcpcanon.link console_scripts -> /sbin/dhcpcanon run cases ---------- 1. standalone without systemd, using -sp sbin/dhcpcanon-script 2. standalone without systemd, using resolvconf 3. standalone without systemd, using resolvconf-admin 4. launched with a wrapper, using -sp sbin/dhcpcanon-script 5. launched with a wrapper, sing resolvconf 6. launched with a wrapper, using resolvconf-admin 7. launched as systemd service, using systemd-resolved install from ------------- * setup.py: dhcpcanon-scriptresolvconf, resolvconf-admin and/or systemd need to be installed manually * pip: dhcpcanon-scriptresolvconf, resolvconf-admin and/or systemd need to be installed manually * Makefile * Debian dhcpcanon-0.8.5/docs/source/integration.rst000066400000000000000000000106521326371602700210010ustar00rootroot00000000000000.. _integration: ``dhcpcanon`` integration with network managers ================================================ Integration with Gnome ``Network Manager`` ------------------------------------------- `Gnome Network Manager `_ has several components. In Debian the service ``NetworkManager`` by default calls `dhclient `_ which in turn calls ``nm-dhcp-helper``. Depending on the configuration, dhclient is called with the parameters:: /sbin/dhclient -d -q -sf /usr/lib/NetworkManager/nm-dhcp-helper -pf /var/run/dhclient-.pid -lf /var/lib/NetworkManager/dhclient--.lease -cf /var/lib/NetworkManager/dhclient-.conf Dclient calls ``nm-dhcp-helper`` via the ``-sf`` parameter, which seems to communicate back with ``NetworkManager`` via D-Bus. ``NetworkManager`` can be configured to use `dhcpcd `_ or ``internal``, as DHCP clients instead of ``dhclient``. .. parsed-literal:: FIXME: Configuring ``NetworkManager`` to use ``internal`` did not work (why?). Is it using systemd DHCP client code? (``libsystemd-network `_ is included in ``NetworkManager`` source code, which is in ``systemd`` `code `_). It does not work either with ``dhcpcd``: NetworkManager[12712]: [1493146345.7994] dhcp-init: DHCP client 'dhcpcd' not available Environment variables that ``dhclient`` returns ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When ``dhclient`` call the script, by default ``/sbin/dhcpcanon-script``, or when called by ``NetworkManager``, ``nm-dhcp-helper``, it pass environment variables. .. parsed-literal:: FIXME: Are these variables documented somewhere?. In ``man dhclient-script`` there is the list of values that the variable ``reason`` can take:: The following reasons are currently defined: MEDIUM, PREINIT, BOUND, RENEW, REBIND, REBOOT, EXPIRE, FAIL, STOP, RELEASE, NBI and TIMEOUT. But there are more variables. By setting ``RUN=yes`` in ``/etc/dhcp/debug``, these variables are found in ``/tmp/dhclient-script.debug``:: reason='PREINIT' interface= -------------------------- reason='REBOOT' interface= new_ip_address= new_network_number= new_subnet_mask= new_broadcast_address= new_routers= new_domain_name= new_domain_name_servers= Looking at the code `dhclient v4.3.5 `_ there seem to be more variables. Environment variables that ``nm-dhcp-helper`` gets ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TBD ?? ``dhcpcanon`` required modifications ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If ``dhcpcanon`` accepts the same arguments as ``dhclient`` and calls the script ``nm-dhcp-helper`` with the same environment variables as ``dhclient``, it should be integrated. .. parsed-literal:: FIXME: however for some reason this generates D-Bus errors. ``dhcpcanon`` could also implement the D-Bus input/output that ``NetworkManager`` needs. There's a `NetworkManager D-Bus API `_ specification. There's also a Python API, `python-networkmanager `_, so ``dhcpcanon`` could communicate directly with ``NetworkManager`` instead communicating with ``nm-dhcp-helper``. nm notes --------- Debugging: [logging] level=DEBUG It is not possible to set ``dhcp-send-hostname`` (`Bug 768076 - No way to set dhcp-send-hostname globally `_) globally. To modify ``dhcp-send-hostname`` per interface: nmcli connection modify "Wired connection" ipv4.dhcp-send-hostname no nmcli connection show "Wired connection" Or the files: /etc/NetworkManager/system-connections/Wired\ connection There is currently no way that when a new device is create it defaults to a configuration. Integration with ``wicd`` --------------------------- TBD `wicd `_ `wicd documentation `_ dhcpcanon-0.8.5/docs/source/privileges.rst000066400000000000000000000074571326371602700206400ustar00rootroot00000000000000.. _privileges: Minimising ``dhcpcanon`` privileges ==================================== Reasons why a DHCP client needs to run with root privileges: * open sockets in privilege ports (68) * open RAW sockets: to receive packets without having an IP set yet * to set the IP offered .. note:: ``dhcpcanon`` does not need privileges to set up the IP, as that is done by a separated script, as ``dhclient`` does. Possible solutions to minimise privileges and their associated problems: 1. drop privileges after BOUND DHCP state (sockets binded): * problem: if the client stays connected until the renewing/rebinding time, privileges would be needed again and dropping privileges `temporally` it is not recommended []. * possible solutions: do not implement RENEWING/REBINDING states. * problem: this would not be compliant with RFC 2131 nor 7844. * pro: in "usual" networks, if the client stays enough time connected to the network, the lease would expire it could just restart in the INIT state. .. todo:: which would be the associated problems to this solution? 2. wrapper with privileges to set linux network capabilities to the client, open sockets, then call the client inheriting the sockets: * problem: same as 1. .. note:: it's not possible to set net capabilities directly to a python script, they would need to be set to the python binary, but that would give the capabilities to any python script. Python binary could also be copied, set the capabilies, and that script call the client, but would have the same problem as giving the capabilities to the original python binary 3. ``dhcpcanon`` could call a binary with privileges to create the sockets every time it needs to do so. It's needed to change several parts of the current implementation. 4. to have the process be granted just the capabilities it needs, by the system-level process manager. This is already implemented with ``systemd`` 5. wrapper that does the same as in 4. without a system-level process manager. See section "wrapper to inherit capabilities" It could be solved with `infinity0's wrapper ` running:: RUST_BACKTRACE=1 ./target/debug/ambient -c NET_RAW,NET_ADMIN,NET_BIND_SERVICE /usr/bin/python3 -m dhcpcanon.dhcpcanon -v 6. wrapper with privileges to disable linux Remote Path (RP) filter, open sockets, then call the client: * problems: * it still needs root to change the default RP settings * it would only allow that the DHCP offers are received from other interfaces [], but still RAW sockets are needed to receive packets in the same interface that does not have an IP address yet * same as 1. Wrapper to inherit capabilities -------------------------------- With ``capsh``, ``dhcpcanon`` could be launched as another user and inherit only the required capabilities, in a similar way as ``systemd.service`` does:: capsh --caps=cap_net_raw,cap_net_bind_service,cap_net_admin+epi --keep=1 -- -c "mkdir -p /run/dhcpcanon && cd /run/dhcpcanon && su -c 'exec /sbin/dhcpcanon enp0s25' -s /bin/sh dhcpcanon" ``-s`` is needed cause dhcpcanon shell is ``/bin/false`` However this does not have capabilities to create the socket. To show the capabilities that are actually inherited:: capsh --keep=1 --secbits=0x1C --caps=cap_net_raw,cap_net_bind_service,cap_net_admin+epi -- -c "mkdir -p /run/dhcpcanon && cd /run/dhcpcanon && su -c '/sbin/capsh --print' -s /bin/sh dhcpcanon" In ``man capsh`` ``--securebits`` is not documented, ``securebits.h`` has some documentation, but it seems to be needed a newer version of ``libcap`` as commented in this `post `_ dhcpcanon-0.8.5/docs/source/questions.rst000066400000000000000000000046131326371602700205100ustar00rootroot00000000000000.. _questions: Summary of questions regarding the RFCs and the implementations =============================================================== This is a summary of the questions stated in `RFC7844 DHCPv4 restricted version summary `_ Message Options ----------------- Requested IP Address Option (code 50) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [:rfc:`7844#section-3.3`] - Is there a way to know ``if`` the link-layer address changed without leaking the link-layer? Not specified in RFC7844, but in RFC2131 ----------------------------------------- Probe the offered IP ~~~~~~~~~~~~~~~~~~~~~ [:rfc:`2131#section-2.2`] - does any implementation issue an ARP request to probe the offered address? - is it issued after DHCPOFFER and before DHCPREQUEST, or after DHCPACK and before passing to BOUND state? Retransmission delays ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Sending DHCPDISCOVER [:rfc:`2131#section-4.4.1`] - is the DISCOVER retranmitted in the same way as the REQUEST [:rfc:`2131#section-3.1`], [:rfc:`2131#section-4.4.5`], [:rfc:`2131#section-4.1`] - the delay for the next retransmission is calculated with respect to the type of DHCP message or for the total of DHCP messages sent indendent of the type? - without this algorithm being mandatory, **it'd be possible to fingerprint the the implementation depending on the delay of the retransmission** - how does other implementations do? Selecting offer algorithm ~~~~~~~~~~~~~~~~~~~~~~~~~~~ [:rfc:`2131#section-4.2`] - what is a "no acceptable offer"? - which are the "strategies" to select OFFER implemented? - how many offers to wait for? - different algorithms to select an OFFER **could fingerprint the implementation** [:rfc:`2131#section-4.4.1`] - Is it different the retransmission delays waiting for offer or ack/nak?, in all states? Timers ~~~~~~~ [:rfc:`2131#section-4.4.5`] - what's the fixed value for the fuzz and how is it calculated? - The "fuzz" range is not specified, the fuzz chosen **could fingerprint** the implementation. Leases ~~~~~~~ [:rfc:`7844#section-3.3`] - is there a way to know if the network the client is connected to is the same to which it was connected previously? Not specified in any RFC ------------------------- - is it needed to check that the ACK options match with the OFFER ones? - is it needed to check that all options make sense?, which ones? dhcpcanon-0.8.5/docs/source/running.rst000066400000000000000000000013211326371602700201270ustar00rootroot00000000000000.. _running: Running dhcpcanon ================== If ``dhcpcanon`` has be installed with systemd, it can be started with:: sudo systemctl start dhcpcanon After installing, it can also be run manually:: sudo dhcpcanon There is no need to pass any argument, most of the arguments are only used when ``dhcpcanon`` is called by other program (``systemd`` or ``gnome network manager``) and mimic the ``dhclient`` arguments. You can specify which network interface to use passing it as an argument. Without specificying the network interface, it will use the active interface. An useful argument when reporting bugs is ``-v``. An updated command line usage description can be obtained with:: dhcpcanon -h dhcpcanon-0.8.5/docs/source/soa.rst000066400000000000000000000055311326371602700172400ustar00rootroot00000000000000.. _soa: ================== State of the Art ================== on DHCP clients, network managers and libraries in Debian/Ubuntu ISC-DHCP ----------- Reference ISC implementation `ISC License `__ `homepage `__ `tar.gz `__ Debian DHCP clients ====================== isc-dhcp-client ------------------ Debian default `debian `__ `debian source `__ network-manager built-in -------------------------- systemd-networkd -------------------- ``man 5 systemd.network`` => DHCP options udhcpc ----------- Busybox implementation `debian `__ Debian network managers ======================== Gnome Network Manager ------------------------ Can use 3 DHCP clients: - ISC DHCP client: package `isc-dhpc-client`, binarry `dhclient` - systemd DHCP client - built-in DHCP client `debian `__ wicd ----- `debian `__ Python DHCP libraries/tools =============================== python-isc-dhcp-leases -------------------------- Python module for reading dhcp leases files `debian `__ pydhcplib ------------------- Pure Python library. GPL. Last updated XX. Commiters: 1. `pypi `__, `repo `__, `wiki `__ `debian `__ pydhcpd ----------- DHCP command-line query and testing tool. Uses pydhcplib GPL. Last updated: 2009 `code `__ staticdhcpd ---------------- is an all-Python, RFC 2131-compliant DHCP server, with support for most common DHCP extensions and extensive site-specific customisation. GPL. Last updated 12/03/2017. Commiters: +3 `repo `__ dhquery ---------- DHCP command line query and testing tool `code `__ `one github fork `__ (updated 2016) dhcpy6d ------------ MAC address aware DHCPv6 server written in Python Last updated 28/06/2017. Commiters: 2? `homepage `__ `repo `__ `doc `__ `debian `__ dhcpscapy ----------- Simple DCHP client and server implemented with scapy Last updated. 18/05/2014. Commiters: 1 `repo `__ dhcpcanon-0.8.5/docs/source/specification.rst000066400000000000000000000314061326371602700212760ustar00rootroot00000000000000.. _specification: RFC7844 DHCPv4 restricted version summary, questions and ``dhcpcanon`` specification ====================================================================================== This document is a more restrictive version summary of [:rfc:`7844`], where the keywords (``key words`` [:rfc:`2119`]) commented in `RFC7844 comments `_ are actually replaced. Use ``diff`` to see specific differences between these two documents. See :ref:`questions` for a summary of the questions stated here. .. note:: * Extracts from the [:rfc:`7844`] marked as `literal blocks `_. * Replacements are marked as `parsed literal `_ with the keyword replaced in bold Message types ----------------- .. note:: See :ref:`implementation` for a summary of the messages implementation DHCP* ~~~~~~ [:rfc:`7844#section-3.1`]:: SHOULD randomize the ordering of options .. parsed-literal:: If this can not be implemented **MUST** order the options by option code number (lowest to highest). DHCPDISCOVER ~~~~~~~~~~~~~ [:rfc:`7844#section-3.`]:: MUST contain the Message Type option, .. parsed-literal:: **MUST** NOT contain the Client Identifier option, **MUST** NOT contain the Parameter Request List option. **MUST** NOT contain any other option. DHCPREQUEST ~~~~~~~~~~~~~ [:rfc:`7844#section-3.`]:: MUST contain the Message Type option, .. parsed-literal:: **MUST** NOT contain the Client Identifier option, **MUST** NOT contain the Parameter Request List option. **MUST** NOT contain any other option. :: If in response to a DHCPOFFER, MUST contain the corresponding Server Identifier option MUST contain the Requested IP address option. If the message is not in response to a DHCPOFFER (BOUND, RENEW),: .. parsed-literal:: **MUST** NOT contain a Requested IP address option DHCPDECLINE ~~~~~~~~~~~~~ [:rfc:`7844#section-3.`]:: MUST contain the Message Type option, MUST contain the Server Identifier option, MUST contain the Requested IP address option; .. parsed-literal:: **MUST** NOT contain the Client Identifier option. - is it always broadcast? DHCPRELEASE ~~~~~~~~~~~~~ [:rfc:`7844#section-3.`] To do not leak when the client leaves the network, this message type **MUST** NOT be implemented. In this case, servers might run out of leases, but that is something that servers should fix decreasing the lease time. DHCPINFORM ~~~~~~~~~~~~~ [:rfc:`7844#section-3.`]:: MUST contain the Message Type option, .. parsed-literal:: **MUST** NOT contain the Client Identifier option, **MUST** NOT contain the Parameter Request List option. It **MUST** NOT contain any other option. Message Options ----------------- Client IP address (ciaddr) ~~~~~~~~~~~~~~~~~~~~~~~~~~ [:rfc:`7844#section-3.2`]:: MUST NOT include in the message a Client IP address that has been obtained with a different link-layer address. Requested IP Address Option (code 50) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [:rfc:`7844#section-3.3`] .. parsed-literal:: **MUST** NOT use the Requested IP address option in DHCPDISCOVER messages. :: MUST use the option when mandated (DHCPREQUEST) If in INIT-REBOOT: .. parsed-literal:: **MUST** perform a complete four-way handshake, starting with a DHCPDISCOVER - This is like not having INIT-REBOOT state?:: If the client can ascertain that this is exactly the same network to which it was previously connected, and if the link-layer address did not change, MAY issue a DHCPREQUEST to try to reclaim the current address. - This is like INIT-REBOOT state? - Is there a way to know ``if`` the link-layer address changed without leaking the link-layer? Client Hardware Address Field ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [:rfc:`7844#section-3.4`]:: If the hardware address is reset to a new randomized value, .. parsed-literal:: the DHCP client **MUST** use the new randomized value in the DHCP messages The client should be restarted when the hardware address changes and use the current address instead of the permanent one. Client Identifier Option (code 61) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [:rfc:`7844#section-3.5`] .. parsed-literal:: **MUST** NOT have this option In the case that it would have this option because otherwise the server does not answer to the requests,:: DHCP clients MUST use client identifiers based solely on the link-layer address that will be used in the underlying connection. Parameter Request List Option (PRL) (code 55) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [:rfc:`7844#section-3.6`] .. parsed-literal:: **MUST** NOT have this option Host Name option (code 12) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [:rfc:`7844#section-3.7`] .. parsed-literal:: **MUST** NOT send the Host Name option. Client FQDN Option (code 81) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [:rfc:`7844#section-3.8`] .. parsed-literal:: **MUST** NOT include the Client FQDN option UUID/GUID-Based Client Machine Identifier Option (code 97) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [:rfc:`7844#section-3.9`]:: Nodes visiting untrusted networks MUST NOT send or use the PXE options. - And in the hypotetical case that nodes are visiting a "trusted" network, must this option be included for the PXE to work properly? User and Vendor Class DHCP Options ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [:rfc:`7844#section-3.10`] .. parsed-literal:: **MUST** NOT use the :: Vendor-Specific Information option (code 43), the Vendor Class Identifier option (code 60), the V-I Vendor Class option (code 124), or the V-I Vendor-Specific Information option (code 125), Operational considerations --------------------------- [:rfc:`7844#section-5.`] :: Implementers SHOULD provide a way for clients to control when the anonymity profiles are used and when standard behavior is preferred. ``dhcpcanon`` does not currently implement the standard behavior described in [:rfc:`2131`] in order to keep the implementation simple and because all existing implementations already implement it Not specified in RFC7844, but in RFC2131 ----------------------------------------- Probe the offered IP ~~~~~~~~~~~~~~~~~~~~~ [:rfc:`2131#section-2.2`]:: the allocating server SHOULD probe the reused address before allocating the address, e.g., with an ICMP echo request, and the client SHOULD probe the newly received address, e.g., with ARP. The client SHOULD perform a check on the suggested address to ensure that the address is not already in use. For example, if the client is on a network that supports ARP, the client may issue an ARP request for the suggested request. When broadcasting an ARP request for the suggested address, the client must fill in its own hardware address as the sender's hardware address, and 0 as the sender's IP address, to avoid confusing ARP caches in other hosts on the same subnet.>> The client SHOULD broadcast an ARP reply to announce the client's new IP address and clear any outdated ARP cache entries in hosts on the client's subnet. - does any implementation issue an ARP request to probe the offered address? - is it issued after DHCPOFFER and before DHCPREQUEST, or after DHCPACK and before passing to BOUND state? Currently, there is not any probe Retransmission delays ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Sending DHCPDISCOVER [:rfc:`2131#section-4.4.1`]:: The client SHOULD wait a random time between one and ten seconds to desynchronize the use of DHCP at startup. - is the DISCOVER retranmitted in the same way as the REQUEST? [:rfc:`2131#section-3.1`]:: a client retransmitting as described in section 4.1 might retransmit the DHCPREQUEST message four times, for a total delay of 60 seconds [:rfc:`2131#section-4.4.5`]:: In both RENEWING and REBINDING states, if the client receives no response to its DHCPREQUEST message, the client SHOULD wait one-half of the remaining time until T2 (in RENEWING state) and one-half of the remaining lease time (in REBINDING state), down to a minimum of 60 seconds, before retransmitting the DHCPREQUEST message. [:rfc:`2131#section-4.1`]:: For example, in a 10Mb/sec Ethernet internetwork, the delay before the first retransmission SHOULD be 4 seconds randomized by the value of a uniform random number chosen from the range -1 to +1 Clients with clocks that provide resolution granularity of less than one second may choose a non-integer randomization value. The delay before the next retransmission SHOULD be 8 seconds randomized by the value of a uniform number chosen from the range -1 to +1. The retransmission delay SHOULD be doubled with subsequent retransmissions up to a maximum of 64 seconds. - the delay for the next retransmission is calculated with respect to the type of DHCP message or for the total of DHCP messages sent indendent of the type? - without this algorithm being mandatory, **it'd be possible to fingerprint the the implementation depending on the delay of the retransmission** - how does other implementations do? Selecting offer algorithm ~~~~~~~~~~~~~~~~~~~~~~~~~~~ [:rfc:`2131#section-4.2`]:: DHCP clients are free to use any strategy in selecting a DHCP server among those from which the client receives a DHCPOFFER message. client may choose to collect several DHCPOFFER messages and select the "best" offer. If the client receives no acceptable offers, the client may choose to try another DHCPDISCOVER message. - what is a "no acceptable offer"? - which are the "strategies" to select OFFER implemented? - different algorithms to select an OFFER **could fingerprint the implementation** [:rfc:`2131#section-4.4.1`]:: The client collects DHCPOFFER messages over a period of time, selects one DHCPOFFER message from the (possibly many) incoming DHCPOFFER messages The time over which the client collects messages and the mechanism used to select one DHCPOFFER are implementation dependent. - Is it different the retransmission delays waiting for offer or ack/nak?, in all states? Currently, the first OFFER is chosen Timers ~~~~~~~ [:rfc:`2131#section-4.4.5`]:: Times T1 and T2 are configurable by the server through options. T1 defaults to (0.5 * duration_of_lease). T2 defaults to (0.875 * duration_of_lease). Times T1 and T2 SHOULD be chosen with some random "fuzz" around a fixed value, to avoid synchronization of client reacquisition. T1 is then calculated as:: renewing_time = lease_time * 0.5 - time_elapsed_after_request range_fuzz = lease_time * 0.875 - renewing_time renewing_time += random.uniform(-(range_fuzz), +(range_fuzz)) And T2:: rebinding_time = lease_time * 0.875 - time_elapsed_after_request range_fuzz = lease_time - rebinding_time rebinding_time += random.uniform(-(range_fuzz), +(range_fuzz)) The range_fuzz is calculated in the same way that ``systemd`` implementation does - what's the fixed value for the fuzz and how is it calculated? - The "fuzz" range is not specified, the fuzz chosen **could fingerprint** the implementation. Leases ~~~~~~~ [:rfc:`7844#section-3.3`]:: There are scenarios in which a client connecting to a network remembers a previously allocated address, i.e., when it is in the INIT-REBOOT state. In that state, any client that is concerned with privacy SHOULD perform a complete four-way handshake, starting with a DHCPDISCOVER, to obtain a new address lease. If the client can ascertain that this is exactly the same network to which it was previously connected, and if the link-layer address did not change, the client MAY issue a DHCPREQUEST to try to reclaim the current address. - is there a way to know if the network the client is connected to is the same to which it was connected previously? For the sake of simplicity and privacy ``dhcpcanon`` does not currently implement the INIT-REBOOT state nor reuse previously allocated addresses. In future stages of ``dhcpcanon`` would be possible to reuse a previously allocated address. In order to do not leak identifying information when doing so, it would be needed: * to keep a database with previously allocated addresses associated to: * the link network where the address was obtained (without revealing the MAC being used). * the MAC address that was used in that network It is possible also that ``dhcpcanon`` will include a MAC randomization module in the same distribution package or would require it in order to start. dhcpcanon-0.8.5/docs/source/todo.rst000066400000000000000000000006451326371602700174240ustar00rootroot00000000000000.. _todo: TODO ===== [X] create debian package [X] create documentation [X] calculate retransmission times for DISCOVER [X] create tests [ ] (WIP) integrate with Network Manager [ ] listen in several interfaces [X] create systemd service [X] create init.d daemon: Won't fix. [X] limit privileges [X] include MAC anonymization module: Debian package suggest it. [X] create apparmor profile [ ] implement IPv6 dhcpcanon-0.8.5/install.sh000066400000000000000000000017761326371602700155050ustar00rootroot00000000000000#!/bin/bash mkdir -p /sbin for i in sbin/dhcpcanon-script; do install "$i" /sbin; done mkdir -p /share/doc/dhcpcanon for i in README.md LICENSE; do install -m 644 "$i" /share/doc/dhcpcanon; done mkdir -p /share/man/man8 for i in man/dhcpcanon.8; do install -m 644 "$i" /share/man/man8; done python3 setup.py install --record installed.txt --install-scripts=/sbin adduser --system dhcpcanon mkdir -p /lib/systemd/system cp systemd/dhcpcanon.service /lib/systemd/system/dhcpcanon.service mkdir -p /lib/tmpfiles.d for i in tmpfiles.d/dhcpcanon.conf; do install -m 644 "$i" /lib/tmpfiles.d; done systemctl enable /lib/systemd/system/dhcpcanon.service systemd-tmpfiles --create --root=/lib/tmpfiles.d/dhcpcanon.conf mkdir -p /lib/systemd/network for i in systemd/network/90-dhcpcanon.link; do install -m 644 "$i" /lib/systemd/network; done mkdir -p /etc/apparmor.d for i in apparmor.d/sbin.dhcpcanon; do install -m 644 "$i" /etc/apparmor.d; done for i in apparmor.d/sbin.dhcpcanon; do aa-complain /etc/apparmor.d/"$i"; done dhcpcanon-0.8.5/man/000077500000000000000000000000001326371602700142435ustar00rootroot00000000000000dhcpcanon-0.8.5/man/dhcpcanon-script.8000066400000000000000000000250551326371602700176020ustar00rootroot00000000000000.\" dhcpcanon-script.8 .\" .\" Copyright (c) 2012,2014,2016 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 2009-2010 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 2004-2005 by Internet Systems Consortium, Inc. ("ISC") .\" Copyright (c) 1996-2003 by Internet Software Consortium .\" Modified by juga for dhcpcanon, 2017. .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT .\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" Internet Systems Consortium, Inc. .\" 950 Charter Street .\" Redwood City, CA 94063 .\" .\" https://www.isc.org/ .\" .\" Support and other services are available for ISC products - see .\" https://www.isc.org for more information or to learn more about ISC. .\" .\" $Id: dhcpcanon-script.8,v 1.14 2010/07/02 23:09:14 sar Exp $ .\" .TH dhcpcanon-script 8 .SH NAME dhcpcanon-script - DHCP client network configuration script .SH DESCRIPTION The DHCP client network configuration script is invoked from time to time by \fBdhcpcanon(8)\fR. This script is used by the dhcp client to set each interface's initial configuration prior to requesting an address, to test the address once it has been offered, and to set the interface's final configuration once a lease has been acquired. If no lease is acquired, the script is used to test predefined leases, if any, and also called once if no valid lease can be identified. .PP This script is not meant to be customized by the end user. If local customizations are needed, they should be possible using the enter and exit hooks provided (see HOOKS for details). These hooks will allow the user to override the default behaviour of the client in creating a .B /etc/resolv.conf file. .PP No standard client script exists for some operating systems, even though the actual client may work, so a pioneering user may well need to create a new script or modify an existing one. .SH HOOKS When it starts, the client script first defines a shell function, .B make_resolv_conf , which is later used to create the .B /etc/resolv.conf file. To override the default behaviour, redefine this function in the enter hook script. .PP On after defining the make_resolv_conf function, the client script checks for the presence of an executable .B ETCDIR/dhcpcanon-enter-hooks script, and if present, it invokes the script inline, using the Bourne shell \'.\' command. The entire environment documented under OPERATION is available to this script, which may modify the environment if needed to change the behaviour of the script. If an error occurs during the execution of the script, it can set the exit_status variable to a nonzero value, and .B CLIENTBINDIR/dhcpcanon-script will exit with that error code immediately after the client script exits. .PP After all processing has completed, .B CLIENTBINDIR/dhcpcanon-script checks for the presence of an executable .B ETCDIR/dhcpcanon-exit-hooks script, which if present is invoked using the \'.\' command. The exit status of dhcpcanon-script will be passed to dhcpcanon-exit-hooks in the exit_status shell variable, and will always be zero if the script succeeded at the task for which it was invoked. The rest of the environment as described previously for dhcpcanon-enter-hooks is also present. The .B ETCDIR/dhcpcanon-exit-hooks script can modify the valid of exit_status to change the exit status of dhcpcanon-script. .SH OPERATION When dhcpcanon needs to invoke the client configuration script, it defines a set of variables in the environment, and then invokes .B CLIENTBINDIR/dhcpcanon-script. In all cases, $reason is set to the name of the reason why the script has been invoked. The following reasons are currently defined: MEDIUM, PREINIT, BOUND, RENEW, REBIND, REBOOT, EXPIRE, FAIL, STOP, RELEASE, NBI and TIMEOUT. .PP .SH MEDIUM The DHCP client is requesting that an interface's media type be set. The interface name is passed in $interface, and the media type is passed in $medium. .SH PREINIT The DHCP client is requesting that an interface be configured as required in order to send packets prior to receiving an actual address. For clients which use the BSD socket library, this means configuring the interface with an IP address of 0.0.0.0 and a broadcast address of 255.255.255.255. For other clients, it may be possible to simply configure the interface up without actually giving it an IP address at all. The interface name is passed in $interface, and the media type in $medium. .PP The DHCP client has done an initial binding to a new address. The new ip address is passed in $new_ip_address, and the interface name is passed in $interface. The media type is passed in $medium. Any options acquired from the server are passed using the option name described in \fBdhcp-options\fR, except that dashes (\'-\') are replaced by underscores (\'_\') in order to make valid shell variables, and the variable names start with new_. So for example, the new subnet mask would be passed in $new_subnet_mask. Options from a non-default universe will have the universe name prepended to the option name, for example $new_dhcp6_server_id. The options that the client explicitly requested via a PRL or ORO option are passed with the same option name as above but prepended with requested_ and with a value of 1, for example requested_subnet_mask=1. No such variable is defined for options not requested by the client or options that don't require a request option, such as the ip address (*_ip_address) or expiration time (*_expire). .PP Before actually configuring the address, dhcpcanon-script should somehow ARP for it and exit with a nonzero status if it receives a reply. In this case, the client will send a DHCPDECLINE message to the server and acquire a different address. This may also be done in the RENEW, REBIND, or REBOOT states, but is not required, and indeed may not be desirable. .PP When a binding has been completed, a lot of network parameters are likely to need to be set up. A new /etc/resolv.conf needs to be created, using the values of $new_domain_name and $new_domain_name_servers (which may list more than one server, separated by spaces). A default route should be set using $new_routers, and static routes may need to be set up using $new_static_routes. .PP If an IP alias has been declared, it must be set up here. The alias IP address will be written as $alias_ip_address, and other DHCP options that are set for the alias (e.g., subnet mask) will be passed in variables named as described previously except starting with $alias_ instead of $new_. Care should be taken that the alias IP address not be used if it is identical to the bound IP address ($new_ip_address), since the other alias parameters may be incorrect in this case. .SH RENEW When a binding has been renewed, the script is called as in BOUND, except that in addition to all the variables starting with $new_, and $requested_ there is another set of variables starting with $old_. Persistent settings that may have changed need to be deleted - for example, if a local route to the bound address is being configured, the old local route should be deleted. If the default route has changed, the old default route should be deleted. If the static routes have changed, the old ones should be deleted. Otherwise, processing can be done as with BOUND. .SH REBIND The DHCP client has rebound to a new DHCP server. This can be handled as with RENEW, except that if the IP address has changed, the ARP table should be cleared. .SH REBOOT The DHCP client has successfully reacquired its old address after a reboot. This can be processed as with BOUND. .SH EXPIRE The DHCP client has failed to renew its lease or acquire a new one, and the lease has expired. The IP address must be relinquished, and all related parameters should be deleted, as in RENEW and REBIND. .SH FAIL The DHCP client has been unable to contact any DHCP servers, and any leases that have been tested have not proved to be valid. The parameters from the last lease tested should be deconfigured. This can be handled in the same way as EXPIRE. .SH STOP The dhcpcanon has been informed to shut down gracefully, the dhcpcanon-script should unconfigure or shutdown the interface as appropriate. .SH RELEASE The dhcpcanon has been executed using the -r flag, indicating that the administrator wishes it to release its lease(s). dhcpcanon-script should unconfigure or shutdown the interface. .SH NBI No-Broadcast-Interfaces...dhcpcanon was unable to find any interfaces upon which it believed it should commence DHCP. What dhcpcanon-script should do in this situation is entirely up to the implementor. .SH TIMEOUT .PP The usual way to test a lease is to set up the network as with REBIND (since this may be called to test more than one lease) and then ping the first router defined in $routers. If a response is received, the lease must be valid for the network to which the interface is currently connected. It would be more complete to try to ping all of the routers listed in $new_routers, as well as those listed in $new_static_routes, but current scripts do not do this. .SH FILES Each operating system should generally have its own script file, although the script files for similar operating systems may be similar or even identical. The script files included in Internet Systems Consortium DHCP distribution appear in the distribution tree under client/scripts, and bear the names of the operating systems on which they are intended to work. .SH BUGS If more than one interface is being used, there's no obvious way to avoid clashes between server-supplied configuration parameters - for example, the stock dhcpcanon-script rewrites /etc/resolv.conf. If more than one interface is being configured, /etc/resolv.conf will be repeatedly initialized to the values provided by one server, and then the other. Assuming the information provided by both servers is valid, this shouldn't cause any real problems, but it could be confusing. .SH SEE ALSO dhcpcanon(8). .SH AUTHOR .B dhcpcanon-script(8) To learn more about Internet Systems Consortium, see .B https://www.isc.org. dhcpcanon-0.8.5/man/dhcpcanon.8000066400000000000000000000011611326371602700162700ustar00rootroot00000000000000.\" Manpage for dhcpcanon. .\" Contact ju@riseup.net to correct errors or typos. .TH man 8 "25 Nov 2016" "1.0" "dhcpcanon man page" .SH NAME dhcpcanon \- DHCP Client Anonymity profile .SH SYNOPSIS dhcpcanon [-h] [-d] [-l LEASE] [-v] [interface] .SH DESCRIPTION dhcpcanon is a DCHP client implementation of the DHCP Anonymity Profiles (RFC7844). using Scapy Automaton. .SH OPTIONS The options which apply to the dhcpcanon command are: -h, --help Show help message and exit. -d, --debug Show debug messages. -l, --lease LEASE Custom lease time. -v, --version Show version. .SH AUTHOR ju xor (ju@riseup.net) dhcpcanon-0.8.5/requirements.txt000066400000000000000000000001371326371602700167550ustar00rootroot00000000000000# to do not maintain this file, use setup.py --index-url https://pypi.python.org/simple/ -e . dhcpcanon-0.8.5/requirements_dev.txt000066400000000000000000000002541326371602700176130ustar00rootroot00000000000000# to do not maintain this file, use setup.py --index-url https://pypi.python.org/simple/ -e . # from setup.py extras_require dhcpcanon[test] dhcpcanon[dev] dhcpcanon[doc] dhcpcanon-0.8.5/requirements_docs.txt000066400000000000000000000000561326371602700177650ustar00rootroot00000000000000sphinxcontrib-plantuml sphinx-bootstrap-theme dhcpcanon-0.8.5/sbin/000077500000000000000000000000001326371602700144235ustar00rootroot00000000000000dhcpcanon-0.8.5/sbin/dhcpcanon-script000077500000000000000000000336201326371602700176140ustar00rootroot00000000000000#!/bin/sh # dhclient-script for Linux. Dan Halbert, March, 1997. # Updated for Linux 2.[12] by Brian J. Murrell, January 1999. # Modified for Debian. Matt Zimmerman and Eloy Paris, December 2003 # Modified to remove useless tests for antiquated kernel versions that # this doesn't even work with anyway, and introduces a dependency on /usr # being mounted, which causes cosmetic errors on hosts that NFS mount /usr # Andrew Pollock, February 2005 # Modified to work on point-to-point links. Andrew Pollock, June 2005 # Modified to support passing the parameters called with to the hooks. Andrew Pollock, November 2005 # The alias handling in here probably still sucks. -mdz # Modified for dhcpcanon by juga, 2017. # Explicitly set the PATH to that of ENV_SUPATH in /etc/login.defs and unset # various other variables. We need to do this so /sbin/dhcpcanon cannot abuse # the environment to escape AppArmor confinement via this script # (LP: #1045986). This can be removed once AppArmor supports environment # filtering (LP: #1045985) +export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +export ENV= +export BASH_ENV= +export CDPATH= +export GLOBIGNORE= +export BASH_XTRACEFD= # wait for given file to be writable wait_for_rw() { local file=$1 # Find out whether we are going to mount / rw exec 9>&0 &9 9>&- # Wait for $file to become writable if [ "$rootmode" = "rw" ]; then while ! { : >> "$file"; } 2>/dev/null; do sleep 0.1 done fi } # update /etc/resolv.conf based on received values make_resolv_conf() { local new_resolv_conf # DHCPv4 if [ -n "$new_domain_search" ] || [ -n "$new_domain_name" ] || [ -n "$new_domain_name_servers" ]; then resolv_conf=$(readlink -f "/etc/resolv.conf" 2>/dev/null) || resolv_conf="/etc/resolv.conf" new_resolv_conf="${resolv_conf}.dhcpcanon-new.$$" wait_for_rw "$new_resolv_conf" rm -f $new_resolv_conf if [ -n "$new_domain_name" ]; then echo domain ${new_domain_name%% *} >>$new_resolv_conf fi if [ -n "$new_domain_search" ]; then if [ -n "$new_domain_name" ]; then domain_in_search_list="" for domain in $new_domain_search; do if [ "$domain" = "${new_domain_name}" ] || [ "$domain" = "${new_domain_name}." ]; then domain_in_search_list="Yes" fi done if [ -z "$domain_in_search_list" ]; then new_domain_search="$new_domain_name $new_domain_search" fi fi echo "search ${new_domain_search}" >> $new_resolv_conf elif [ -n "$new_domain_name" ]; then echo "search ${new_domain_name}" >> $new_resolv_conf fi if [ -n "$new_domain_name_servers" ]; then for nameserver in $new_domain_name_servers; do echo nameserver $nameserver >>$new_resolv_conf done else # keep 'old' nameservers sed -n /^\w*[Nn][Aa][Mm][Ee][Ss][Ee][Rr][Vv][Ee][Rr]/p $resolv_conf >>$new_resolv_conf fi if [ -f $resolv_conf ]; then chown --reference=$resolv_conf $new_resolv_conf chmod --reference=$resolv_conf $new_resolv_conf fi mv -f $new_resolv_conf $resolv_conf # DHCPv6 elif [ -n "$new_dhcp6_domain_search" ] || [ -n "$new_dhcp6_name_servers" ]; then resolv_conf=$(readlink -f "/etc/resolv.conf" 2>/dev/null) || resolv_conf="/etc/resolv.conf" new_resolv_conf="${resolv_conf}.dhcpcanon-new.$$" wait_for_rw "$new_resolv_conf" rm -f $new_resolv_conf if [ -n "$new_dhcp6_domain_search" ]; then echo "search ${new_dhcp6_domain_search}" >> $new_resolv_conf fi if [ -n "$new_dhcp6_name_servers" ]; then for nameserver in $new_dhcp6_name_servers; do # append %interface to link-local-address nameservers if [ "${nameserver##fe80::}" != "$nameserver" ] || [ "${nameserver##FE80::}" != "$nameserver" ]; then nameserver="${nameserver}%${interface}" fi echo nameserver $nameserver >>$new_resolv_conf done else # keep 'old' nameservers sed -n /^\w*[Nn][Aa][Mm][Ee][Ss][Ee][Rr][Vv][Ee][Rr]/p $resolv_conf >>$new_resolv_conf fi if [ -f $resolv_conf ]; then chown --reference=$resolv_conf $new_resolv_conf chmod --reference=$resolv_conf $new_resolv_conf fi mv -f $new_resolv_conf $resolv_conf fi } # set host name set_hostname() { if [ -n "$new_host_name" ]; then local current_hostname=$(hostname) # current host name is empty, '(none)' or 'localhost' or differs from new one from DHCP if [ -z "$current_hostname" ] || [ "$current_hostname" = '(none)' ] || [ "$current_hostname" = 'localhost' ] || [ "$current_hostname" = "$old_host_name" ]; then if [ "$new_host_name" != "$current_host_name" ]; then hostname "$new_host_name" fi fi fi } # run given script run_hook() { local script="$1" local exit_status=0 if [ -f $script ]; then . $script exit_status=$? fi if [ -n "$exit_status" ] && [ "$exit_status" -ne 0 ]; then logger -p daemon.err "$script returned non-zero exit status $exit_status" fi return $exit_status } # run scripts in given directory run_hookdir() { local dir="$1" local exit_status=0 if [ -d "$dir" ]; then for script in $(run-parts --list $dir); do run_hook $script exit_status=$((exit_status|$?)) done fi return $exit_status } # Must be used on exit. Invokes the local dhcp client exit hooks, if any. exit_with_hooks() { local exit_status=$1 # Source the documented exit-hook script, if it exists if ! run_hook /etc/dhcp/dhcpcanon-exit-hooks; then exit_status=$? fi # Now run scripts in the Debian-specific directory. if ! run_hookdir /etc/dhcp/dhcpcanon-exit-hooks.d; then exit_status=$? fi exit $exit_status } # The 576 MTU is only used for X.25 and dialup connections # where the admin wants low latency. Such a low MTU can cause # problems with UDP traffic, among other things. As such, # disallow MTUs from 576 and below by default, so that broken # MTUs are ignored, but higher stuff is allowed (1492, 1500, etc). if [ -z "$new_interface_mtu" ] || [ "$new_interface_mtu" -le 576 ]; then new_interface_mtu='' fi # The action starts here # Invoke the local dhcp client enter hooks, if they exist. run_hook /etc/dhcp/dhcpcanon-enter-hooks run_hookdir /etc/dhcp/dhcpcanon-enter-hooks.d # Execute the operation case "$reason" in ### DHCPv4 Handlers MEDIUM|ARPCHECK|ARPSEND) # Do nothing ;; PREINIT) # The DHCP client is requesting that an interface be # configured as required in order to send packets prior to # receiving an actual address. - dhcpcanon-script(8) # ensure interface is up ip link set dev ${interface} up if [ -n "$alias_ip_address" ]; then # flush alias IP from interface ip -4 addr flush dev ${interface} label ${interface}:0 fi ;; BOUND|RENEW|REBIND|REBOOT) set_hostname if [ -n "$old_ip_address" ] && [ -n "$alias_ip_address" ] && [ "$alias_ip_address" != "$old_ip_address" ]; then # alias IP may have changed => flush it ip -4 addr flush dev ${interface} label ${interface}:0 fi if [ -n "$old_ip_address" ] && [ "$old_ip_address" != "$new_ip_address" ]; then # leased IP has changed => flush it ip -4 addr flush dev ${interface} label ${interface} fi if [ -z "$old_ip_address" ] || [ "$old_ip_address" != "$new_ip_address" ] || [ "$reason" = "BOUND" ] || [ "$reason" = "REBOOT" ]; then # new IP has been leased or leased IP changed => set it ip -4 addr add ${new_ip_address}${new_subnet_mask:+/$new_subnet_mask} \ ${new_broadcast_address:+broadcast $new_broadcast_address} \ dev ${interface} label ${interface} if [ -n "$new_interface_mtu" ]; then # set MTU ip link set dev ${interface} mtu ${new_interface_mtu} fi # if we have $new_rfc3442_classless_static_routes then we have to # ignore $new_routers entirely if [ ! "$new_rfc3442_classless_static_routes" ]; then # set if_metric if IF_METRIC is set or there's more than one router if_metric="$IF_METRIC" if [ "${new_routers%% *}" != "${new_routers}" ]; then if_metric=${if_metric:-1} fi for router in $new_routers; do if [ "$new_subnet_mask" = "255.255.255.255" ]; then # point-to-point connection => set explicit route ip -4 route add ${router} dev $interface >/dev/null 2>&1 fi # set default route ip -4 route add default via ${router} dev ${interface} \ ${if_metric:+metric $if_metric} >/dev/null 2>&1 if [ -n "$if_metric" ]; then if_metric=$((if_metric+1)) fi done fi fi if [ -n "$alias_ip_address" ] && [ "$new_ip_address" != "$alias_ip_address" ]; then # separate alias IP given, which may have changed # => flush it, set it & add host route to it ip -4 addr flush dev ${interface} label ${interface}:0 ip -4 addr add ${alias_ip_address}${alias_subnet_mask:+/$alias_subnet_mask} \ dev ${interface} label ${interface}:0 ip -4 route add ${alias_ip_address} dev ${interface} >/dev/null 2>&1 fi # update /etc/resolv.conf make_resolv_conf ;; EXPIRE|FAIL|RELEASE|STOP) if [ -n "$alias_ip_address" ]; then # flush alias IP ip -4 addr flush dev ${interface} label ${interface}:0 fi if [ -n "$old_ip_address" ]; then # flush leased IP ip -4 addr flush dev ${interface} label ${interface} fi if [ -n "$alias_ip_address" ]; then # alias IP given => set it & add host route to it ip -4 addr add ${alias_ip_address}${alias_subnet_mask:+/$alias_subnet_mask} \ dev ${interface} label ${interface}:0 ip -4 route add ${alias_ip_address} dev ${interface} >/dev/null 2>&1 fi ;; TIMEOUT) if [ -n "$alias_ip_address" ]; then # flush alias IP ip -4 addr flush dev ${interface} label ${interface}:0 fi # set IP from recorded lease ip -4 addr add ${new_ip_address}${new_subnet_mask:+/$new_subnet_mask} \ ${new_broadcast_address:+broadcast $new_broadcast_address} \ dev ${interface} label ${interface} if [ -n "$new_interface_mtu" ]; then # set MTU ip link set dev ${interface} mtu ${new_interface_mtu} fi # if there is no router recorded in the lease or the 1st router answers pings if [ -z "$new_routers" ] || ping -q -c 1 "${new_routers%% *}"; then # if we have $new_rfc3442_classless_static_routes then we have to # ignore $new_routers entirely if [ ! "$new_rfc3442_classless_static_routes" ]; then if [ -n "$alias_ip_address" ] && [ "$new_ip_address" != "$alias_ip_address" ]; then # separate alias IP given => set up the alias IP & add host route to it ip -4 addr add ${alias_ip_address}${alias_subnet_mask:+/$alias_subnet_mask} \ dev ${interface} label ${interface}:0 ip -4 route add ${alias_ip_address} dev ${interface} >/dev/null 2>&1 fi # set if_metric if IF_METRIC is set or there's more than one router if_metric="$IF_METRIC" if [ "${new_routers%% *}" != "${new_routers}" ]; then if_metric=${if_metric:-1} fi # set default route for router in $new_routers; do ip -4 route add default via ${router} dev ${interface} \ ${if_metric:+metric $if_metric} >/dev/null 2>&1 if [ -n "$if_metric" ]; then if_metric=$((if_metric+1)) fi done fi # update /etc/resolv.conf make_resolv_conf else # flush all IPs from interface ip -4 addr flush dev ${interface} exit_with_hooks 2 fi ;; ### DHCPv6 Handlers # TODO handle prefix change: ?based on ${old_ip6_prefix} and ${new_ip6_prefix}? PREINIT6) # ensure interface is up ip link set ${interface} up # flush any stale global permanent IPs from interface ip -6 addr flush dev ${interface} scope global permanent ;; BOUND6|RENEW6|REBIND6) if [ "${new_ip6_address}" ]; then # set leased IP ip -6 addr add ${new_ip6_address} \ dev ${interface} scope global fi # update /etc/resolv.conf if [ "${reason}" = BOUND6 ] || [ "${new_dhcp6_name_servers}" != "${old_dhcp6_name_servers}" ] || [ "${new_dhcp6_domain_search}" != "${old_dhcp6_domain_search}" ]; then make_resolv_conf fi ;; DEPREF6) # set preferred lifetime of leased IP to 0 ip -6 addr change ${cur_ip6_address} \ dev ${interface} scope global preferred_lft 0 ;; EXPIRE6|RELEASE6|STOP6) if [ -z "${old_ip6_address}" ]; then exit_with_hooks 2 fi # delete leased IP ip -6 addr del ${old_ip6_address} \ dev ${interface} ;; esac exit_with_hooks 0 dhcpcanon-0.8.5/setup.cfg000066400000000000000000000001021326371602700153020ustar00rootroot00000000000000[metadata] description-file = README.rst [aliases] # test=pytest dhcpcanon-0.8.5/setup.py000066400000000000000000000057421326371602700152120ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # vim:ts=4:sw=4:expandtab # Copyright 2016, 2017 juga (juga at riseup dot net), MIT license. """Setup.""" import subprocess from setuptools import find_packages, setup import dhcpcanon def systemd_unit_dir(): cmd = ['pkg-config', '--variable', 'systemdsystemunitdir', 'systemd'] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = proc.communicate() if proc.returncode != 0 or stdout is None: return None # systemd not found if isinstance(stdout.strip(), bytes): return stdout.strip().decode('utf-8') return stdout.strip() def systemd_tmpfiles_dir(): # There doesn't seem to be a specific pkg-config variable for this cmd = ['pkg-config', '--variable', 'prefix', 'systemd'] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = proc.communicate() if proc.returncode != 0 or stdout is None: return None # systemd not found d = stdout.strip() if isinstance(d, bytes): d = d.decode('utf-8') return d + '/lib/tmpfiles.d' if systemd_unit_dir(): data_files = [ (systemd_unit_dir(), ['systemd/dhcpcanon.service']), (systemd_tmpfiles_dir(), ['tmpfiles.d/dhcpcanon.conf']), ('sbin', ["sbin/dhcpcanon-script"]) ] else: data_files = [] test_requirements = ['coverage', 'tox', 'pytest'] setup( name='dhcpcanon', version=dhcpcanon.__version__, description=dhcpcanon.__description__, long_description=dhcpcanon.__long_description__, author=dhcpcanon.__author__, author_email=dhcpcanon.__author_mail__, license='MIT', url=dhcpcanon.__website__, packages=find_packages(exclude=['contrib', 'docs', 'tests*']), install_requires=[ "attrs>=16.3", "dbus-python>=1.2", "netaddr>=0.7", "lockfile>=0.12", "pip>=8.1", "pyroute2>=0.3", 'scapy-python3>=0.20', ], python_requires=">=3.5", extras_require={ 'dev': ['flake8'], 'test': test_requirements, 'doc': ['sphinx', 'sphinx-bootstrap-theme', 'pylint'] }, tests_require=test_requirements, entry_points={ 'console_scripts': [ 'dhcpcanon = dhcpcanon.dhcpcanon:main', ] }, # NOTE: not installing system files as the user might want to install them # in a custom prefix or without systemd # data_files=data_files, zip_safe=False, include_package_data=True, keywords='python scapy dhcp RFC7844 RFC2131 anonymity', classifiers=[ 'Development Status :: 3 - Alpha', "Environment :: Console", 'Intended Audience :: End Users/Desktop', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python :: 3', 'Topic :: System :: Networking', ], ) dhcpcanon-0.8.5/systemd/000077500000000000000000000000001326371602700151605ustar00rootroot00000000000000dhcpcanon-0.8.5/systemd/dhcpcanon.service000066400000000000000000000004631326371602700205020ustar00rootroot00000000000000[Unit] Description=dhcpcanon DHCP client Anonymity Profile Documentation=man:dhcpcanon(8) [Service] RuntimeDirectory=dhcpcanon RuntimeDirectoryMode=0775 ExecStart=/sbin/dhcpcanon $ARGS AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW User=dhcpcanon [Install] WantedBy=multi-user.target dhcpcanon-0.8.5/systemd/network/000077500000000000000000000000001326371602700166515ustar00rootroot00000000000000dhcpcanon-0.8.5/systemd/network/90-dhcpcanon.link000066400000000000000000000000371326371602700217130ustar00rootroot00000000000000[Link] MacAddressPolicy=random dhcpcanon-0.8.5/tests/000077500000000000000000000000001326371602700146325ustar00rootroot00000000000000dhcpcanon-0.8.5/tests/conftest.py000066400000000000000000000011221326371602700170250ustar00rootroot00000000000000# -*- coding: utf-8 -*- # vim:ts=4:sw=4:expandtab # Copyright 2016, 2017 juga (juga at riseup dot net), MIT license. """.""" import pytest from dhcpcanon.dhcpcap import DHCPCAP @pytest.fixture def dhcpcap_maker(request): """ return a function which creates initialized dhcpcap instances. """ def maker(): dhcpcap = DHCPCAP(client_mac="00:01:02:03:04:05", iface='eth0', xid=900000000) return dhcpcap return maker @pytest.fixture def dhcpcap(dhcpcap_maker): """ return an initialized dhcpcap instance. """ return dhcpcap_maker() dhcpcanon-0.8.5/tests/dhcpcap_leases.py000066400000000000000000000036601326371602700201470ustar00rootroot00000000000000# -*- coding: utf-8 -*- # vim:ts=4:sw=4:expandtab # Copyright 2016, 2017 juga (juga at riseup dot net), MIT license. """.""" from dhcpcanon.dhcpcaplease import DHCPCAPLease LEASE_INIT = DHCPCAPLease(interface='enp0s25', address='', server_id='', next_server='', router='', subnet_mask='', broadcast_address='', domain='', name_server='', lease_time='', renewal_time='', rebinding_time='', subnet_mask_cidr='', subnet='', expiry='', renew='', rebind='') LEASE_REQUEST = DHCPCAPLease(interface='eth0', address='192.168.1.23', server_id='192.168.1.1', next_server='192.168.1.1', router='192.168.1.1', subnet_mask='255.255.255.0', broadcast_address='192.168.1.255', domain='localdomain', name_server='192.168.1.1 8.8.8.8', lease_time='43200', renewal_time='21600', rebinding_time='37800', subnet_mask_cidr='24', subnet='192.168.1.0', expiry='', renew='', rebind='') LEASE_ACK = DHCPCAPLease(interface='eth0', address='192.168.1.23', server_id='192.168.1.1', next_server='192.168.1.1', router='192.168.1.1', subnet_mask='255.255.255.0', broadcast_address='192.168.1.255', domain='localdomain', name_server='192.168.1.1 8.8.8.8', lease_time='43200', renewal_time='21600', rebinding_time='37800', subnet_mask_cidr='24', subnet='192.168.1.0', expiry='17-06-23 12:00:00', renew='17-06-23 06:00:00', rebind='17-06-23 10:30:00') dhcpcanon-0.8.5/tests/dhcpcap_objs.py000066400000000000000000000101231326371602700176200ustar00rootroot00000000000000# -*- coding: utf-8 -*- # vim:ts=4:sw=4:expandtab # Copyright 2016, 2017 juga (juga at riseup dot net), MIT license. """.""" from dhcpcanon.dhcpcaplease import DHCPCAPLease from dhcpcanon.dhcpcap import DHCPCAP # init, before send discover # init after sent discover client_init = DHCPCAP(iface='enp0s25', client_mac='f0:de:f1:b8:9b:db', client_ip='0.0.0.0', client_port=68, server_mac='ff:ff:ff:ff:ff:ff', server_ip='255.255.255.255', server_port=67, lease=DHCPCAPLease(address='', server_id='', next_server='', router='', subnet_mask='', broadcast_address='', domain='', name_server='', subnet='', lease_time='', renewal_time='', rebinding_time='', interface='enp0s25', subnet_mask_cidr='', network='', expiry='', renew='', rebind=''), event=None) # selecting after received offer client_select = DHCPCAP(iface='enp0s25', client_mac='f0:de:f1:b8:9b:db', client_ip='0.0.0.0', client_port=68, server_mac='ff:ff:ff:ff:ff:ff', server_ip='255.255.255.255', server_port=67, lease=DHCPCAPLease(address='192.168.2.113', server_id='192.168.2.1', next_server='192.168.2.1', router='192.168.2.1', subnet_mask='255.255.255.0', broadcast_address='192.168.2.255', domain='localdomain', name_server='192.168.2.1 8.8.8.8', subnet='192.168.2.0', lease_time='43200', renewal_time='21600', rebinding_time='37800', interface='enp0s25', subnet_mask_cidr='24', network='', expiry='', renew='', rebind=''), event=None) # in requesting state after received ack client_request = DHCPCAP(iface='enp0s25', client_mac='f0:de:f1:b8:9b:db', client_ip='0.0.0.0', client_port=68, server_mac='1c:74:0d:b2:e5:10', server_ip='192.168.2.1', server_port=67, lease=DHCPCAPLease(address='192.168.2.113', server_id='192.168.2.1', next_server='192.168.2.1', router='192.168.2.1', subnet_mask='255.255.255.0', broadcast_address='192.168.2.255', domain='localdomain', name_server='192.168.2.1 8.8.8.8', subnet='192.168.2.0', lease_time='43200', renewal_time='21600', rebinding_time='37800', interface='enp0s25', subnet_mask_cidr='24', network='', expiry='17-06-15 23:00:32', renew='17-06-15 17:00:32', rebind='17-06-15 21:30:32'), event=4) dhcpcanon-0.8.5/tests/dhcpcap_pkts.py000066400000000000000000000077671326371602700176700ustar00rootroot00000000000000# -*- coding: utf-8 -*- # vim:ts=4:sw=4:expandtab # Copyright 2016, 2017 juga (juga at riseup dot net), MIT license. """.""" from scapy.all import BOOTP, DHCP, IP, UDP, Ether # client packets dhcp_discover = ( Ether(src="00:01:02:03:04:05", dst="ff:ff:ff:ff:ff:ff") / IP(src="0.0.0.0", dst="255.255.255.255") / UDP(sport=68, dport=67) / BOOTP(chaddr=[b'\x00\x01\x02\x03\x04\x05'], xid=900000000) / DHCP(options=[ ('message-type', 'discover'), ("client_id", b'\x00\x01\x02\x03\x04\x05'), ("param_req_list", b"\x01\x03\x06\x0f\x1f\x21\x2b\x2c\x2e\x2f\x79\xf9\xfc"), 'end']) ) dhcp_request = ( Ether(src="00:01:02:03:04:05", dst="ff:ff:ff:ff:ff:ff") / IP(src="0.0.0.0", dst="255.255.255.255") / UDP(sport=68, dport=67) / BOOTP(chaddr=[b'\x00\x01\x02\x03\x04\x05'], xid=900000000) / DHCP(options=[ ('message-type', 'request'), ("client_id", b'\x00\x01\x02\x03\x04\x05'), ("param_req_list", b"\x01\x03\x06\x0f\x1f\x21\x2b\x2c\x2e\x2f\x79\xf9\xfc"), ("requested_addr", "192.168.1.23"), ("server_id", "192.168.1.1"), 'end']) ) dhcp_request_unicast = ( Ether(src="00:01:02:03:04:05", dst="00:0a:0b:0c:0d:0f") / IP(src="192.168.1.23", dst="192.168.1.1") / UDP(sport=68, dport=67) / BOOTP(chaddr=[b'\x00\x01\x02\x03\x04\x05'], xid=900000000, ciaddr="192.168.1.23") / DHCP(options=[ ('message-type', 'request'), ("client_id", b'\x00\x01\x02\x03\x04\x05'), ("param_req_list", b"\x01\x03\x06\x0f\x1f\x21\x2b\x2c\x2e\x2f\x79\xf9\xfc"), 'end']) ) dhcp_decline = ( Ether(src="00:01:02:03:04:05", dst="ff:ff:ff:ff:ff:ff") / IP(src="0.0.0.0", dst="255.255.255.255") / UDP(sport=68, dport=67) / BOOTP(chaddr=['\x00\x01\x02\x03\x04\x05'], options='c\x82Sc') / DHCP(options=[ ('message-type', 'decline'), ("requested_addr", "192.168.1.23"), ("server_id", "192.168.1.1"), 'end']) ) dhcp_inform = ( Ether(src="00:01:02:03:04:05", dst="ff:ff:ff:ff:ff:ff") / IP(src="192.168.1.23", dst="255.255.255.255") / UDP(sport=68, dport=67) / BOOTP(chaddr=['\x00\x01\x02\x03\x04\x05'], ciaddr="192.168.1.23", options='c\x82Sc') / DHCP(options=[ ('message-type', 'inform'), 'end']) ) # server packets ################# dhcp_offer = ( Ether(src="00:0a:0b:0c:0d:0f", dst="00:01:02:03:04:05") / IP(src="192.168.1.1", dst="192.168.1.23") / UDP(sport=67, dport=68) / BOOTP(op=2, yiaddr="192.168.1.23", siaddr="192.168.1.1", giaddr='0.0.0.0') / DHCP(options=[ ('message-type', 'offer'), ('server_id', "192.168.1.1"), ('lease_time', 43200), ('renewal_time', 21600), ('rebinding_time', 37800), ('subnet_mask', "255.255.255.0"), ('broadcast_address', "192.168.1.255"), ('router', "192.168.1.1"), ('name_server', "192.168.1.1", "8.8.8.8"), ('domain', b'localdomain'), 'end'] ) ) dhcp_ack = ( Ether(src="00:0a:0b:0c:0d:0f", dst="00:01:02:03:04:05") / IP(src="192.168.1.1", dst="192.168.1.23") / UDP(sport=67, dport=68) / BOOTP(op=2, yiaddr="192.168.1.23", siaddr="192.168.1.1", giaddr='0.0.0.0') / DHCP(options=[ ('message-type', 'ack'), ('server_id', "192.168.1.1"), ('lease_time', 43200), ('renewal_time', 21600), ('rebinding_time', 37800), ('subnet_mask', "255.255.255.0"), ('broadcast_address', "192.168.1.255"), ('router', "192.168.1.1"), ('name_server', "192.168.1.1", "8.8.8.8"), ('domain', b'localdomain'), 'end']) ) dhcp_nak = ( Ether(src="00:0a:0b:0c:0d:0f", dst="00:01:02:03:04:05") / IP(src="192.168.1.1", dst="192.168.1.23") / UDP(sport=67, dport=68) / BOOTP(op=2, yiaddr="192.168.1.23", siaddr="192.168.1.1", giaddr='0.0.0.0') / DHCP(options=[ ('message-type', 'nak'), ('server_id', "192.168.1.1"), 'end']) ) dhcpcanon-0.8.5/tests/dhcpcapfsm_objs.py000066400000000000000000000473001326371602700203350ustar00rootroot00000000000000# -*- coding: utf-8 -*- # vim:ts=4:sw=4:expandtab 2 # Copyright 2016, 2017 juga (juga at riseup dot net), MIT license. """.""" from datetime import datetime from dhcpcanon.clientscript import ClientScript from dhcpcanon.dhcpcap import DHCPCAP from dhcpcanon.dhcpcaplease import DHCPCAPLease fsm_preinit = { 'current_state': 0, 'request_attempts': 0, 'discover_attempts': 0, 'time_sent_request': None, 'client': DHCPCAP(iface='eth0', client_mac='00:01:02:03:04:05', client_ip='0.0.0.0', client_port=68, xid=900000000, server_mac='ff:ff:ff:ff:ff:ff', server_ip='255.255.255.255', server_port=67, event=None, lease=DHCPCAPLease(address='', server_id='', next_server='', router='', subnet_mask='', broadcast_address='', domain='', name_server='', subnet='', lease_time='', renewal_time='', rebinding_time='', interface='eth0', subnet_mask_cidr='', network='', expiry='', renew='', rebind=''),), 'script': ClientScript(scriptname='/sbin/dhcpcanon-script', env={'rebind': '', 'medium': '', 'broadcast_address': '', 'renew': '', 'domain_name_servers': '', 'dhcp_server_identifier': '', 'pid': '25318', 'domain_name': '', 'network_number': '', 'subnet_mask': '', 'reason': 'INIT', 'client': 'dhcpcanon', 'next_server': '', 'expiry': '', 'routers': '', 'interface': 'eth0', 'dhcp_lease_time': '', 'ip_address': '', 'dhcp_renewal_time': '', 'dhcp_rebinding_time': ''}), } # client after send discover in state 1 fsm_init = { 'current_state': 1, 'request_attempts': 0, 'discover_attempts': 1, 'time_sent_request': None, 'client': DHCPCAP(iface='eth0', client_mac='00:01:02:03:04:05', client_ip='0.0.0.0', client_port=68, xid=900000000, server_mac='ff:ff:ff:ff:ff:ff', server_ip='255.255.255.255', server_port=67, event=None, lease=DHCPCAPLease(address='', server_id='', next_server='', router='', subnet_mask='', broadcast_address='', domain='', name_server='', subnet='', lease_time='', renewal_time='', rebinding_time='', interface='eth0', subnet_mask_cidr='', network='', expiry='', renew='', rebind=''),), 'script': ClientScript(scriptname='/sbin/dhcpcanon-script', env={'rebind': '', 'medium': '', 'broadcast_address': '', 'renew': '', 'domain_name_servers': '', 'dhcp_server_identifier': '', 'pid': '25318', 'domain_name': '', 'network_number': '', 'subnet_mask': '', 'reason': 'PREINIT', 'client': '', 'next_server': '', 'expiry': '', 'routers': '', 'interface': '', 'dhcp_lease_time': '', 'ip_address': '', 'dhcp_renewal_time': '', 'dhcp_rebinding_time': ''}), } fsm_selecting = { 'current_state': 2, 'request_attempts': 0, 'discover_attempts': 1, 'time_sent_request': datetime(2017, 6, 23), 'client': DHCPCAP(iface='eth0', client_mac='00:01:02:03:04:05', client_ip='0.0.0.0', client_port=68, xid=900000000, server_mac='ff:ff:ff:ff:ff:ff', server_ip='255.255.255.255', server_port=67, event=None, lease=DHCPCAPLease(address='192.168.1.23', server_id='192.168.1.1', next_server='192.168.1.1', router='192.168.1.1', subnet_mask='255.255.255.0', broadcast_address='192.168.1.255', domain='localdomain', name_server='192.168.1.1', subnet='192.168.1.0', lease_time='43200', renewal_time='21600', rebinding_time='37800', interface='eth0', subnet_mask_cidr='24', network='', expiry='', renew='', rebind=''),), 'script': ClientScript(scriptname='/sbin/dhcpcanon-script', env={'rebind': '', 'medium': '', 'broadcast_address': '', 'renew': '', 'domain_name_servers': '', 'dhcp_server_identifier': '', 'pid': '25318', 'domain_name': '', 'network_number': '', 'subnet_mask': '', 'reason': 'PREINIT', 'client': 'dhcpcanon', 'next_server': '', 'expiry': '', 'routers': '', 'interface': 'eth0', 'dhcp_lease_time': '', 'ip_address': '192.168.1.23', 'dhcp_renewal_time': '21600', 'dhcp_rebinding_time': '37800'}), } fsm_requesting = { 'current_state': 3, 'request_attempts': 0, 'discover_attempts': 1, 'time_sent_request': datetime(2017, 6, 23), 'client': DHCPCAP(iface='eth0', client_mac='00:01:02:03:04:05', client_ip='192.168.1.23', client_port=68, xid=900000000, server_mac='00:0a:0b:0c:0d:0f', server_ip='192.168.1.1', server_port=67, event=4, lease=DHCPCAPLease(address='192.168.1.23', server_id='192.168.1.1', next_server='192.168.1.1', router='192.168.1.1', subnet_mask='255.255.255.0', broadcast_address='192.168.1.255', domain='localdomain', name_server='192.168.1.1', subnet='192.168.1.0', lease_time='43200', renewal_time='21600', rebinding_time='37800', interface='eth0', subnet_mask_cidr='24', network='', expiry='17-06-23 12:00:00', renew='17-06-23 06:00:00', rebind='17-06-23 10:30:00'),), 'script': ClientScript(scriptname='/sbin/dhcpcanon-script', env={'rebind': '', 'medium': '', 'broadcast_address': '', 'renew': '', 'domain_name_servers': '', 'dhcp_server_identifier': '', 'pid': '25318', 'domain_name': '', 'network_number': '', 'subnet_mask': '', 'reason': 'PREINIT', 'client': 'dhcpcanon', 'next_server': '', 'expiry': '', 'routers': '', 'interface': 'eth0', 'dhcp_lease_time': '', 'ip_address': '', 'dhcp_renewal_time': '', 'dhcp_rebinding_time': ''}), } fsm_bound = { 'current_state': 4, 'request_attempts': 0, 'discover_attempts': 1, 'time_sent_request': datetime(2017, 6, 23), 'client': DHCPCAP(iface='eth0', client_mac='00:01:02:03:04:05', client_ip='192.168.1.23', client_port=68, xid=900000000, server_mac='00:0a:0b:0c:0d:0f', server_ip='192.168.1.1', server_port=67, event=4, lease=DHCPCAPLease(address='192.168.1.23', server_id='192.168.1.1', next_server='192.168.1.1', router='192.168.1.1', subnet_mask='255.255.255.0', broadcast_address='192.168.1.255', domain='localdomain', name_server='192.168.1.1', subnet='192.168.1.0', lease_time='43200', renewal_time='21600', rebinding_time='37800', interface='eth0', subnet_mask_cidr='24', network='', expiry='17-06-23 12:00:00', renew='17-06-23 06:00:00', rebind='17-06-23 10:30:00'),), 'script': ClientScript(scriptname='/sbin/dhcpcanon-script', env={'rebind': '17-06-23 10:30:00', 'medium': '', 'broadcast_address': '192.168.1.255', 'renew': '17-06-23 06:00:00', 'domain_name_servers': '192.168.1.1', 'dhcp_server_identifier': '192.168.1.1', 'pid': '25318', 'domain_name': 'localdomain', 'network_number': '24', 'subnet_mask': '255.255.255.0', 'reason': 'PREINIT', 'client': 'dhcpcanon', 'next_server': '192.168.1.1', 'expiry': '17-06-23 12:00:00', 'routers': '192.168.1.1', 'interface': 'eth0', 'dhcp_lease_time': '43200', 'ip_address': '', 'dhcp_renewal_time': '21600', 'dhcp_rebinding_time': '37800'}), } # without script ############################################################################### # -*- coding: utf-8 -*- # vim:ts=4:sw=4:expandtab 2 # Copyright 2016, 2017 juga (juga at riseup dot net), MIT license. """.""" from datetime import datetime from dhcpcanon.clientscript import ClientScript from dhcpcanon.dhcpcap import DHCPCAP from dhcpcanon.dhcpcaplease import DHCPCAPLease fsm_preinit = { 'current_state': 0, 'request_attempts': 0, 'discover_attempts': 0, 'time_sent_request': None, 'client': DHCPCAP(iface='eth0', client_mac='00:01:02:03:04:05', client_ip='0.0.0.0', client_port=68, xid=900000000, server_mac='ff:ff:ff:ff:ff:ff', server_ip='255.255.255.255', server_port=67, event=None, lease=DHCPCAPLease(address='', server_id='', next_server='', router='', subnet_mask='', broadcast_address='', domain='', name_server='', subnet='', lease_time='', renewal_time='', rebinding_time='', interface='eth0', subnet_mask_cidr='', network='', expiry='', renew='', rebind=''),), 'script': None, } # client after send discover in state 1 fsm_init = { 'current_state': 1, 'request_attempts': 0, 'discover_attempts': 1, 'time_sent_request': None, 'client': DHCPCAP(iface='eth0', client_mac='00:01:02:03:04:05', client_ip='0.0.0.0', client_port=68, xid=900000000, server_mac='ff:ff:ff:ff:ff:ff', server_ip='255.255.255.255', server_port=67, event=None, lease=DHCPCAPLease(address='', server_id='', next_server='', router='', subnet_mask='', broadcast_address='', domain='', name_server='', subnet='', lease_time='', renewal_time='', rebinding_time='', interface='eth0', subnet_mask_cidr='', network='', expiry='', renew='', rebind=''),), 'script': None, } fsm_selecting = { 'current_state': 2, 'request_attempts': 0, 'discover_attempts': 1, 'time_sent_request': datetime(2017, 6, 23), 'client': DHCPCAP(iface='eth0', client_mac='00:01:02:03:04:05', client_ip='0.0.0.0', client_port=68, xid=900000000, server_mac='ff:ff:ff:ff:ff:ff', server_ip='255.255.255.255', server_port=67, event=None, lease=DHCPCAPLease(address='192.168.1.23', server_id='192.168.1.1', next_server='192.168.1.1', router='192.168.1.1', subnet_mask='255.255.255.0', broadcast_address='192.168.1.255', domain='localdomain', name_server='192.168.1.1 8.8.8.8', subnet='192.168.1.0', lease_time='43200', renewal_time='21600', rebinding_time='37800', interface='eth0', subnet_mask_cidr='24', network='', expiry='', renew='', rebind=''),), 'script': None, } fsm_requesting = { 'current_state': 3, 'request_attempts': 0, 'discover_attempts': 1, 'time_sent_request': datetime(2017, 6, 23), 'client': DHCPCAP(iface='eth0', client_mac='00:01:02:03:04:05', client_ip='192.168.1.23', client_port=68, xid=900000000, server_mac='00:0a:0b:0c:0d:0f', server_ip='192.168.1.1', server_port=67, event=4, lease=DHCPCAPLease(address='192.168.1.23', server_id='192.168.1.1', next_server='192.168.1.1', router='192.168.1.1', subnet_mask='255.255.255.0', broadcast_address='192.168.1.255', domain='localdomain', name_server='192.168.1.1 8.8.8.8', subnet='192.168.1.0', lease_time='43200', renewal_time='21600', rebinding_time='37800', interface='eth0', subnet_mask_cidr='24', network='', expiry='17-06-23 12:00:00', renew='17-06-23 06:00:00', rebind='17-06-23 10:30:00'),), 'script': None, } fsm_bound = { 'current_state': 4, 'request_attempts': 0, 'discover_attempts': 1, 'time_sent_request': datetime(2017, 6, 23), 'client': DHCPCAP(iface='eth0', client_mac='00:01:02:03:04:05', client_ip='192.168.1.23', client_port=68, xid=900000000, server_mac='00:0a:0b:0c:0d:0f', server_ip='192.168.1.1', server_port=67, event=4, lease=DHCPCAPLease(address='192.168.1.23', server_id='192.168.1.1', next_server='192.168.1.1', router='192.168.1.1', subnet_mask='255.255.255.0', broadcast_address='192.168.1.255', domain='localdomain', name_server='192.168.1.1 8.8.8.8', subnet='192.168.1.0', lease_time='43200', renewal_time='21600', rebinding_time='37800', interface='eth0', subnet_mask_cidr='24', network='', expiry='17-06-23 12:00:00', renew='17-06-23 06:00:00', rebind='17-06-23 10:30:00'),), 'script': None, } dhcpcanon-0.8.5/tests/test_dhcpcap.py000066400000000000000000000034361326371602700176530ustar00rootroot00000000000000# -*- coding: utf-8 -*- # vim:ts=4:sw=4:expandtab # Copyright 2016, 2017 juga (juga at riseup dot net), MIT license. """""" import logging from datetime import datetime from dhcpcap_leases import LEASE_ACK, LEASE_REQUEST from dhcpcap_pkts import dhcp_ack, dhcp_discover, dhcp_offer, dhcp_request FORMAT = "%(levelname)s: %(filename)s:%(lineno)s - %(funcName)s - " + \ "%(message)s" logging.basicConfig(format=FORMAT, level=logging.DEBUG) logger = logging.getLogger(__name__) class TestDHCPCAP: def test_intialize(self, dhcpcap): assert dhcpcap.client_mac == "00:01:02:03:04:05" assert dhcpcap.iface == "eth0" # assert dhcpcap.xid == 900000000 # assert dhcpcap.prl == \ # b"\x01\x03\x06\x0f\x1f\x21\x2b\x2c\x2e\x2f\x79\xf9\xfc" # assert dhcpcap.client_ip == "0.0.0.0" # assert dhcpcap.client_port == 68 # assert dhcpcap.server_mac == "ff:ff:ff:ff:ff:ff" # assert dhcpcap.server_ip == "255.255.255.255" # assert dhcpcap.server_port == 67 # assert dhcpcap.lease is None # assert dhcpcap.event is None def test_gen_discover(self, dhcpcap): discover = dhcpcap.gen_discover() logger.debug(discover) logger.debug(dhcp_discover) assert discover == dhcp_discover def test_handle_offer(self, dhcpcap): dhcpcap.handle_offer(dhcp_offer) lease = dhcpcap.lease assert lease == LEASE_REQUEST def test_gen_request(self, dhcpcap): dhcpcap.lease = LEASE_REQUEST request = dhcpcap.gen_request() assert request == dhcp_request def test_handle_ack(self, dhcpcap): dhcpcap.lease = LEASE_REQUEST dhcpcap.handle_ack(dhcp_ack, datetime(2017, 6, 23)) lease = dhcpcap.lease assert lease == LEASE_ACK dhcpcanon-0.8.5/tests/test_dhcpcapfsm.py000066400000000000000000000142541326371602700203610ustar00rootroot00000000000000# -*- coding: utf-8 -*- # vim:ts=4:sw=4:expandtab 2 # Copyright 2016, 2017 juga (juga at riseup dot net), MIT license. """Tests for the FSM of the DHCP client implementation of the Anonymity Profile ([:rfc:`7844`]). .. todo:: Test for more cases: - delays getting OFFERs - delays getting ACK - RENEWING state - REBINDING state - lease expires """ import logging import logging.config import pytest from datetime import datetime from scapy.automaton import Automaton from scapy.config import conf from dhcpcanon.conflog import LOGGING from dhcpcanon.constants import STATES2NAMES from dhcpcanon.dhcpcapfsm import DHCPCAPFSM from dhcpcap_pkts import dhcp_ack, dhcp_offer from dhcpcapfsm_objs import (fsm_bound, fsm_init, fsm_preinit, fsm_requesting, fsm_selecting) logging.config.dictConfig(LOGGING) logger = logging.getLogger('dhcpcanon') logger_scapy_interactive = logging.getLogger('scapy.interactive') logger.setLevel(logging.DEBUG) logger_scapy_interactive.setLevel(logging.DEBUG) class DummySocket(object): def __init__(self, *args, **kargs): pass def send(self, pkt): pass def fileno(self): return 0 def recv(self, n=0): return dhcp_offer def close(self): pass class DummySocketAck(DummySocket): def recv(self, n=0): return dhcp_ack @pytest.mark.skip(reason="DummySocket not working.") class TestDHCPCAPFSM: """.""" def test_preinit_bound(self): """Test FSM from PREINIT to BOUND. No delays. No script""" logger.debug('Test PREINIT') # fsm_preinit['script'].script_init(fsm_preinit['client'].lease, # fsm_preinit['current_state']) # recvsock=DummySocket) will fail with python3 conf.L2listen = DummySocket # for sendp: conf.L2socket = DummySocket dhcpcanon = DHCPCAPFSM(client_mac='00:01:02:03:04:05', iface='eth0', xid=900000000, # scriptfile='/sbin/dhcpcanon-script', delay_selecting=1, timeout_select=1, ll=DummySocket) assert dhcpcanon.dict_self() == fsm_preinit logger.debug('Test INIT') logger.debug('============') logger.debug('state %s', STATES2NAMES[dhcpcanon.current_state]) # fsm_init['script'].script_init(fsm_init['client'].lease, # fsm_init['current_state'] - 1) logger.debug('Test start running, INIT') try: dhcpcanon.next() except Automaton.Singlestep as err: logger.debug('Singlestep %s in state %s', err, dhcpcanon.current_state) assert dhcpcanon.dict_self() == fsm_init logger.debug('Test SELECTING') logger.debug('===============') logger.debug('State %s', STATES2NAMES[dhcpcanon.current_state]) logger.debug('Num offers %s', len(dhcpcanon.offers)) # fsm_selecting['script'].script_init(fsm_init['client'].lease, # 'PREINIT') logger.debug('Test timeout selecting %s', dhcpcanon.get_timeout(dhcpcanon.current_state, dhcpcanon.timeout_selecting)) # FIXME:110 why is needed here to press enter to don't retransmit? try: dhcpcanon.next() except Automaton.Singlestep as err: logger.debug('Singlestep %s in state %s', err, dhcpcanon.current_state) # os.kill(os.getpid(), signal.SIGCONT) # logger.debug(dhcpcanon.dict_self()['script']) # logger.debug(fsm_selecting['script']) # TODO: case when offer is not received, # and next step is selecting again if len(dhcpcanon.offers) < 1: logger.debug('Offer not received, tests are not complete yet.') return # assert dhcpcanon.dict_self()['script'] == fsm_selecting['script'] assert dhcpcanon.dict_self()['client'].lease == \ fsm_selecting['client'].lease assert dhcpcanon.dict_self()['client'] == \ fsm_selecting['client'] # with mock.patch('dhcpcanon.timers.nowutc', # return_value=datetime(2017, 6, 23)): dhcpcanon.time_sent_request = datetime(2017, 6, 23) assert dhcpcanon.dict_self() == fsm_selecting logger.debug('Test REQUESTING') logger.debug('=================') logger.debug('State %s', STATES2NAMES[dhcpcanon.current_state]) # dummy socket that will receive an ACK dhcpcanon.listen_sock = DummySocketAck() # fsm_requesting['script'].script_init(fsm_init['client'].lease, # 'PREINIT') try: dhcpcanon.next() except Automaton.Singlestep as err: logger.debug('Singlestep %s in state %s', err, dhcpcanon.current_state) # assert dhcpcanon.dict_self()['script'] == fsm_requesting['script'] # set the timers accourding to the time pkt sent # dhcpcanon.client.lease.set_times(datetime(2017, 6, 23)) dhcpcanon.set_timers() assert dhcpcanon.dict_self()['client'].lease == \ fsm_requesting['client'].lease assert dhcpcanon.dict_self()['client'] == \ fsm_requesting['client'] assert dhcpcanon.dict_self() == fsm_requesting logger.debug('Test BOUND') logger.debug('============') logger.debug('State %s', STATES2NAMES[dhcpcanon.current_state]) # fsm_bound['script'].script_init(fsm_bound['client'].lease, # 'BOUND') try: dhcpcanon.next() except Automaton.Singlestep as err: logger.debug('Singlestep %s in state %s', err, dhcpcanon.current_state) # assert dhcpcanon.dict_self()['script'] == fsm_bound['script'] assert dhcpcanon.dict_self()['client'].lease == \ fsm_bound['client'].lease assert dhcpcanon.dict_self()['client'] == \ fsm_bound['client'] assert dhcpcanon.dict_self() == fsm_bound dhcpcanon-0.8.5/tmpfiles.d/000077500000000000000000000000001326371602700155355ustar00rootroot00000000000000dhcpcanon-0.8.5/tmpfiles.d/dhcpcanon.conf000066400000000000000000000001531326371602700203400ustar00rootroot00000000000000# see tmpfiles.d(5) #Type Path Mode UID GID Age Argument d /run/dhcpcanon 0755 root root - - dhcpcanon-0.8.5/tox.ini000066400000000000000000000015331326371602700150050ustar00rootroot00000000000000[tox] envlist = clean, lint, py35, stats #doc{py27,py35}-{pinned,unpinned}, stats [testenv:clean] skip_install = True changedir={toxinidir} deps = coverage commands= coverage erase [testenv:lint] skip_install = True deps = .[dev] commands = flake8 --max-line-length 79 --ignore=E402,E123 dhcpcanon scripts tests [testenv:stats] skip_install = True changedir={toxinidir} deps = coverage commands= coverage combine coverage report coverage html [testenv] passenv = TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH deps = .[test] commands = coverage run --rcfile={toxinidir}/.coveragerc --source=dhcpcanon -m pytest -svx {toxinidir}/tests [testenv:doc] deps = .[doc] whitelist_externals = make changedir = docs commands = make html # this requires build the pdf images # make latexpdf # this requires network # make linkcheck