[
  {
    "path": ".gitignore",
    "content": ".DS_Store\n\n*.pyc\n*.egg-info\nbuild/\ndist/\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2016 Vince Cali\n\nPermission 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:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE 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."
  },
  {
    "path": "README.md",
    "content": "# AirPyrt Tools\n\n### License\n\nSee LICENSE\n\n\n### Requirements\n\n- python 2.7\n- pycrypto\n\n\n### Installation\n\n`python setup.py install [--user]`\n\n\n### Usage\n\n`python [-B] -m acp`\n\n    usage: __main__.py [-h] [-t address] [-p password] [--listprop]\n                       [--helpprop property] [--getprop property]\n                       [--setprop property value] [--dumpprop] [--acpprop]\n                       [--dump-syslog] [--reboot] [--factory-reset]\n                       [--flash-primary firmware_path] [--do-feat-command]\n                       [--decrypt inpath outpath] [--extract inpath outpath]\n                       [--srp-test]\n\n    optional arguments:\n      -h, --help            show this help message and exit\n\n    AirPort client parameters:\n      -t address, --target address\n                            IP address or hostname of the target router\n      -p password, --password password\n                            router admin password\n\n    AirPort client commands:\n      --listprop            list supported properties\n      --helpprop property   print the description of the specified property\n      --getprop property    get the value of the specified property\n      --setprop property value\n                            set the value of the specified property\n      --dumpprop            dump values of all supported properties\n      --acpprop             get acp acpprop list\n      --dump-syslog         dump the router system log\n      --reboot              reboot device\n      --factory-reset       RESET EVERYTHING and reboot; you have been warned!\n      --flash-primary firmware_path\n                            flash primary partition firmware\n      --do-feat-command     send 0x1b (feat) command\n\n    Basebinary commands:\n      --decrypt inpath outpath\n                            decrypt the basebinary\n      --extract inpath outpath\n                            extract the gzimg contents\n\n    Test arguments:\n      --srp-test            SRP (requires OS X)\n\n\n### Notes\n\n**IMPORTANT**\n\nThis still uses the old ACP protocol implementation, which puts the admin password\nof the device over the wire in a trivially recoverable format. This was fixed by \nin the new protocol which uses SRP authentication and better encryption of requests\nto/from the device. Until this is implemented this tool is entirely unsafe to use,\nespecially for remote administration (which you should have disabled anyway...).\n\nThis project grew organically out of my understanding of various pieces of the ACP \nprotocol. I've restructured the code a few times as it has improved, but there are \nstill many gaps in the implementation, and a lot of code smell. Between sitting on\nthis indefinitely making incremental improvements (and probably never releasing a \n\"finished\" product) and releasing it in a rougher state for others to explore, the\nlatter made far more sense.\n\nReturn value of 0xfffffff6 when using --getprop means the property is not avaliable/readable\n\n\n## TODO (very incomplete list in no particular order)\n\n- add IP address type for properties, make sure it supports IPv4 and IPv6\n- specify RO/WO/RW attribute for properties\n- exception handling:\n  - invalid struct fields aren't handled well in many cases\n  - finish adding custom exception classes and make sure we're using them\n- logging (mostly done, still looks horrible) with verbosity controls\n- review and update docstrings\n- SRP support (fix pysrp because ctypes hax, while fun, are horrible and non-portable)\n- ACP protocol version 2 (full session encryption)\n- handle encrypted property elements\n- basebinary repacking/reencryption\n- basebinary rootfs mounting\n- threaded server\n- handle protocol v1 (for old firmwares/devices)\n- bonjour announcement/discovery\n- options to specify no encryption, old method, and new (SRP) method\n- ACPMonitorSession support\n- ACPRPC support\n"
  },
  {
    "path": "acp/__init__.py",
    "content": ""
  },
  {
    "path": "acp/__main__.py",
    "content": "import cli\ncli.main()\n"
  },
  {
    "path": "acp/basebinary.py",
    "content": "import logging\nimport os.path\nimport struct\nimport zlib\n\nfrom Crypto.Cipher import AES\n\n#XXX: ugh...\nfrom .misc import cast_u32\n\n# hardcoded 128 bit values\n# 107: 52 49 C3 51 02 8B F1 FD 2B D1 84 9E 28 B2 3F 24\n# 108: BB 7D EB 09 70 D8 EE 2E 00 FA 46 CB 1C 3C 09 8E\n# 115: 10 75 E8 06 F4 77 0C D4 76 3B D2 85 A6 4E 91 74\n# 120: 68 8C DD 3B 1B 6B DD A2 07 B6 CE C2 73 52 92 D2\n\n_basebinary_keys = {\n\t#3   : \"\",\n\t#102 : \"\",\n\t#104 : \"\",\n\t#105 : \"\",\n\t#106 : \"\",\n\t107 : \"5249c351028bf1fd2bd1849e28b23f24\",\n\t108 : \"bb7deb0970d8ee2e00fa46cb1c3c098e\",\n\t#109 : \"\",\n\t#113 : \"\",\n\t#114 : \"\",\n\t115 : \"1075e806f4770cd4763bd285a64e9174\",\n\t#116 : \"\",\n\t#117 : \"\",\n\t#119 : \"\",\n\t120 : \"688cdd3b1b6bdda207b6cec2735292d2\",\n\t}\n\ndef _derive_key(model):\n\tif model not in _basebinary_keys:\n\t\treturn None\n\tkey = _basebinary_keys[model].decode(\"hex\")\n\tderived_key = \"\"\n\tfor i in range(len(key)):\n\t\tderived_key += chr(ord(key[i]) ^ (i + 0x19))\n\tlogging.debug(\"derived key {0}\".format(derived_key.encode(\"hex\")))\n\treturn derived_key\n\n\nclass BasebinaryError(Exception):\n\tpass\n\nclass Basebinary(object):\n\t_header_magic = \"APPLE-FIRMWARE\\x00\"\n\t#XXX: do we need to fix shitty Python struct member signdness things here too?\n\t_header_format = struct.Struct(\">15sB2I4BI\")\n\t\n\theader_size = _header_format.size\n\t\n\t\n\t@classmethod\n\tdef parse(cls, data):\n\t\tif len(data) < (cls.header_size + 4):\n\t\t\traise BasebinaryError(\"not enough data to parse\")\n\t\t\n\t\theader_data = data[:cls.header_size]\n\t\tinner_data = data[cls.header_size:-4]\n\t\t\n\t\t#XXX: and here??\n\t\tstored_checksum, = struct.unpack(\">I\", data[-4:])\n\t\t\n\t\t(byte_0x0F, model, version, byte_0x18, byte_0x19, byte_0x1A, flags, unk_0x1C) = cls.parse_header(header_data)\n\t\t\n\t\tif flags & 2:\n\t\t\tinner_data = cls.decrypt(inner_data, model, byte_0x0F)\n\t\t\n\t\t#XXX: why is Python so shitty about this comparison <.<\n\t\tchecksum = cast_u32(zlib.adler32(header_data+inner_data))\n\t\tlogging.debug(\"stored checksum     {0:#x}\".format(stored_checksum))\n\t\tlogging.debug(\"calculated checksum {0:#x}\".format(checksum))\n\t\tlogging.debug(\"data length         {0:#x}\".format(len(header_data+inner_data)))\n\t\tif stored_checksum != checksum:\n\t\t\traise BasebinaryError(\"bad checksum\")\n\t\t\t\n\t\treturn inner_data\n\t\n\t\n\t@classmethod\n\tdef compose(cls, data):\n\t\t#TODO\n\t\tpass\n\t\n\t\n\t@classmethod\n\tdef parse_header(cls, data):\n\t\tmagic, byte_0x0F, model, version, byte_0x18, byte_0x19, byte_0x1A, flags, unk_0x1C = cls._header_format.unpack(data)\n\t\t\n\t\tif magic != cls._header_magic:\n\t\t\traise BasebinaryError(\"bad header magic\")\n\t\t\n\t\treturn (byte_0x0F, model, version, byte_0x18, byte_0x19, byte_0x1A, flags, unk_0x1C)\n\t\n\t\n\t@classmethod\n\tdef compose_header(cls, byte_0x0F, model, version, byte_0x18, byte_0x19, byte_0x1A, flags, unk_0x1C):\n\t\t#TODO\n\t\tpass\n\t\n\t\n\t@classmethod\n\tdef decrypt(cls, data, model, byte_0x0F):\n\t\tiv = cls._header_magic+chr(byte_0x0F)\n\t\tkey = _derive_key(model)\n\t\tif key is None:\n\t\t\traise BasebinaryError(\"key missing for model {0}\".format(model))\n\t\t\n\t\tdecrypted_data = \"\"\n\t\tremaining_length = len(data)\n\t\tchunk_length = 0x8000\n\t\twhile remaining_length:\n\t\t\tif remaining_length > chunk_length:\n\t\t\t\tdecrypted_data   += cls.decrypt_chunk(data[-remaining_length:-(remaining_length-chunk_length)], key, iv)\n\t\t\t\tremaining_length -= chunk_length\n\t\t\telse:\n\t\t\t\tdecrypted_data   += cls.decrypt_chunk(data[-remaining_length:], key, iv)\n\t\t\t\tremaining_length = 0\n\t\t\n\t\treturn decrypted_data\n\t\n\t\n\t@classmethod\n\tdef decrypt_chunk(cls, encrypted_data, key, iv):\n\t\tcipher = AES.new(key, AES.MODE_CBC, iv)\n\t\tdecrypted_data = \"\"\n\t\tbytes_left = len(encrypted_data)\n\t\twhile bytes_left:\n\t\t\t#logging.debug(\"bytes left: {0:#x}\".format(bytes_left))\n\t\t\tif bytes_left > 0x10:\n\t\t\t\tdecrypted_data += cipher.decrypt(encrypted_data[-bytes_left:-(bytes_left-0x10)])\n\t\t\t\tbytes_left -= 0x10\n\t\t\telif bytes_left == 0x10:\n\t\t\t\tdecrypted_data += cipher.decrypt(encrypted_data[-bytes_left:])\n\t\t\t\tbytes_left = 0\n\t\t\telse: # bytes_left < 0x10\n\t\t\t\t#LOL: odd-sized chunk at the end is left unencrypted\n\t\t\t\tdecrypted_data += encrypted_data[-bytes_left:]\n\t\t\t\tbytes_left = 0\n\t\t\n\t\treturn decrypted_data\n\t\n\t\n\t@classmethod\n\tdef encrypt(cls, data):\n\t\t#TODO: not really necessary; router doesn't require an encrypted firmware if flag is unset\n\t\tpass\n\t\n\t\n\t@classmethod\n\tdef extract(cls, data):\n\t\t#TODO: proper gzip header validation?\n\t\tgzip_offset = data.index(\"\\x1f\\x8b\\x08\")\n\t\tgzdata = data[gzip_offset:]\n\t\t\n\t\treturn zlib.decompress(gzdata, 16+zlib.MAX_WBITS)\n"
  },
  {
    "path": "acp/cflbinary.py",
    "content": "import logging\nimport struct\nfrom collections import OrderedDict\nfrom math  import log\nfrom types import *\n\n\n_header_magic = \"CFB0\"\n_footer_magic = \"END!\"\n_header_size = len(_header_magic)\n_footer_size = len(_footer_magic)\n\ndef _lslice(data, length):\n\t\"\"\" Slice object into two counting from the left\n\t\n\tReturns:\n\t\t(left_slice, right_slice)\n\t\"\"\"\n\treturn data[:length], data[length:]\n\n\nclass CFLBinaryPListComposeError(Exception):\n\t\"\"\"Exception raised for errors composing a cflbinary format property list\"\"\"\n\tpass\n\nclass CFLBinaryPListParseError(Exception):\n\t\"\"\"Exception raised for errors parsing a cflbinary format property list\"\"\"\n\tpass\n\n\nclass CFLBinaryPListComposer(object):\n\t\"\"\"Write cflbinary format property list\"\"\"\n\t\n\t@classmethod\n\tdef _pack_object(cls, obj):\n\t\t\"\"\" Pack a supported Python built-in object\n\t\t\n\t\tNote:\n\t\t\tthis function is super hacky and needs to be fixed\n\t\t\n\t\tReturns:\n\t\t\tdata\n\t\t\n\t\tRaises:\n\t\t\tCFLBinaryPListComposeError\n\t\t\"\"\"\n\t\tdata = \"\"\n\t\t\n\t\tobject_type = type(obj)\n\t\t\n\t\tif\t object_type == NoneType:\n\t\t\tdata += \"\\x00\"\n\t\t\n\t\telif object_type == BooleanType:\n\t\t\tif not obj:\n\t\t\t\tdata += \"\\x08\"\n\t\t\telse:\n\t\t\t\tdata += \"\\x09\"\n\t\t\n\t\telif object_type == IntType:\n\t\t\tobject_marker = 0x10\n\t\t\tbuf = \"\"\n\t\t\t#XXX: need to actually catch unsupported packed sizes\n\t\t\tfor fmt in [\">B\", \">H\", \">I\", \">Q\"]:\n\t\t\t\ttry:\n\t\t\t\t\tbuf = struct.pack(fmt, obj)\n\t\t\t\texcept struct.error:\n\t\t\t\t\tlogging.debug(\"XXX: skipping {0}\".format(fmt))\n\t\t\t\t\tpass\n\t\t\t\telse:\n\t\t\t\t\tbreak\n\t\t\t\n\t\t\tobject_marker += int(log(len(buf), 2))\n\t\t\t\n\t\t\tdata += chr(object_marker)\n\t\t\tdata += buf\n\t\t\n\t\telif object_type == FloatType:\n\t\t\tobject_marker = 0x20\n\t\t\tbuf = \"\"\n\t\t\t#XXX: need to actually catch unsupported packed sizes\n\t\t\tfor fmt in [\"!f\", \"!d\"]:\n\t\t\t\ttry:\n\t\t\t\t\tbuf = struct.pack(fmt, obj)\n\t\t\t\texcept struct.error:\n\t\t\t\t\tlogging.debug(\"XXX: skipping {0}\".format(fmt))\n\t\t\t\t\tpass\n\t\t\t\telse:\n\t\t\t\t\tbreak\n\t\t\t\n\t\t\tobject_marker += int(log(len(buf), 2))\n\t\t\t\n\t\t\tdata += chr(object_marker)\n\t\t\tdata += buf\n\t\t\n\t\t#XXX: DateType?\n\t\t\n\t\telif object_type == StringType:\n\t\t\tobject_marker = 0x40\n\t\t\tdata_len = len(obj)\n\t\t\tif data_len < 0xF:\n\t\t\t\tobject_marker += data_len\n\t\t\t\tdata += chr(object_marker)\n\t\t\telse:\n\t\t\t\tobject_marker += 0xF\n\t\t\t\tdata += chr(object_marker)\n\t\t\t\tdata += cls._pack_object(data_len)\n\t\t\tdata += obj\n\t\t\n\t\telif object_type == UnicodeType:\n\t\t\tdata += \"\\x70\"\n\t\t\tdata += obj.encode(\"utf-8\")\n\t\t\tdata += \"\\x00\"\n\t\t\n\t\telif object_type == ListType:\n\t\t\tdata += \"\\xA0\"\n\t\t\tfor element in obj:\n\t\t\t\tdata += cls._pack_object(element)\n\t\t\tdata += \"\\x00\"\n\t\t\n\t\telif object_type in [DictType, OrderedDict]:\n\t\t\tdata += \"\\xD0\"\n\t\t\tfor k, v in obj.iteritems():\n\t\t\t\tdata += cls._pack_object(k)\n\t\t\t\tdata += cls._pack_object(v)\n\t\t\tdata += \"\\x00\"\n\t\t\n\t\telse:\n\t\t\traise CFLBinaryPListComposeError(\"unsupported Python built-in type: {0}\".format(type(obj)))\n\t\t\n\t\treturn data\n\t\n\t@classmethod\n\tdef compose(cls, object):\n\t\t\"\"\" Compose Python object into equivalent plist\n\t\t\n\t\tReturns:\n\t\t\tplist_data\n\t\t\n\t\tRaises:\n\t\t\tCFLBinaryPListComposeError\n\t\t\"\"\"\n\t\tdata =  _header_magic\n\t\t# assume one root object\n\t\tdata += cls._pack_object(object)\n\t\tdata += _footer_magic\n\t\treturn data\n\n\nclass CFLBinaryPListParser(object):\n\t\"\"\"Read cflbinary format property list\"\"\"\n\t\n\t@classmethod\n\tdef _unpack_int(cls, size_exponent, data):\n\t\t\"\"\" Unpack an int object as a Python int from the provided data\n\t\t\n\t\tReturns:\n\t\t\t(int, remaining_data)\n\t\t\n\t\tRaises:\n\t\t\tCFLBinaryPListParseError\n\t\t\"\"\"\n\t\tint_size = 2**size_exponent\n\t\tint_bytes, data = _lslice(data, int_size)\n\t\t#XXX: are these supposed to be signed or unsigned?\n\t\tif int_size == 1:\n\t\t\tint_fmt = \">B\"\n\t\telif int_size == 2:\n\t\t\tint_fmt = \">H\"\n\t\telif int_size == 4:\n\t\t\tint_fmt = \">I\"\n\t\telif int_size == 8:\n\t\t\tint_fmt = \">Q\"\n\t\telse:\n\t\t\traise CFLBinaryPListParseError(\"unsupported int packed object size of {0} bytes\")\n\t\t\n\t\ttry:\n\t\t\t(int_val, ) = struct.unpack(int_fmt, int_bytes)\n\t\texcept struct.error:\n\t\t\traise CFLBinaryPListParseError(\"failed to unpack int value\")\n\t\t\n\t\treturn int_val, data\n\t\n\t@classmethod\n\tdef _unpack_real(cls, size_exponent, data):\n\t\t\"\"\" Unpack a real object as a Python float from the provided data\n\t\t\n\t\tReturns:\n\t\t\t(float, remaining_data)\n\t\t\n\t\tRaises:\n\t\t\tCFLBinaryPListParseError\n\t\t\"\"\"\n\t\treal_size = 2**size_exponent\n\t\treal_bytes, data = _lslice(data, real_size)\n\t\tif   real_size == 4:\n\t\t\treal_fmt = \">f\"\n\t\telif real_size == 8:\n\t\t\treal_fmt = \">d\"\n\t\telse:\n\t\t\traise CFLBinaryPListParseError(\"unsupported real packed object size of {0} bytes\")\n\t\t\n\t\ttry:\n\t\t\t(float_val, ) = struct.unpack(real_fmt, real_bytes)\n\t\texcept struct.error:\n\t\t\traise CFLBinaryPListParseError(\"failed to unpack float value\")\n\t\t\n\t\treturn float_val, data\n\t\n\t@classmethod\n\tdef _unpack_count(cls, object_info, data):\n\t\t\"\"\" Unpack count from object info nibble and/or packed int value\n\t\t\n\t\tReturns:\n\t\t\t(count, remaining_data)\n\t\t\n\t\tRaises:\n\t\t\tCFLBinaryPListParseError\n\t\t\"\"\"\n\t\tif object_info == 0x0F:\n\t\t\t# count is the following packed int object\n\t\t\tmarker, data = cls._unpack_object_marker(data)\n\t\t\tcount_object_type = marker & 0xF0\n\t\t\tcount_object_info = marker & 0x0F\n\t\t\tif count_object_type != 0x10:\n\t\t\t\traise CFLBinaryPListParseError(\"expected count to be a packed int object\")\n\t\t\tcount, data = cls._unpack_int(count_object_info, data)\n\t\telse:\n\t\t\tcount = object_info\n\t\t\n\t\treturn count, data\n\t\n\t@classmethod\n\tdef _unpack_object_marker(cls, data):\n\t\t\"\"\" Unpack an object marker from the provided data\n\t\t\n\t\tReturns:\n\t\t\t(marker, remaining_data)\n\t\t\n\t\tRaises:\n\t\t\tCFLBinaryPListParseError\n\t\t\"\"\"\n\t\tmarker_byte, data = _lslice(data, 1)\n\t\ttry:\n\t\t\t(marker, ) = struct.unpack(\">B\", marker_byte)\n\t\texcept struct.error:\n\t\t\traise CFLBinaryPListParseError(\"failed to unpack object marker\")\n\t\t\n\t\treturn marker, data\n\t\n\t@classmethod\n\tdef _unpack_object(cls, data):\n\t\t\"\"\" Unpack an object from the provided data\n\t\t\n\t\tReturns:\n\t\t\t(obj, remaining_data)\n\t\t\n\t\tRaises:\n\t\t\tCFLBinaryPListParseError\n\t\t\"\"\"\n\t\tobj = None\n\t\t\n\t\tmarker, data = cls._unpack_object_marker(data)\n\t\tobject_type = marker & 0xF0\n\t\tobject_info = marker & 0x0F\n\t\t\n\t\tif object_type == 0x00:\n\t\t\tif object_info == 0x00:   # null, null object\n\t\t\t\treturn None, data\n\t\t\telif object_info == 0x08: # bool, false\n\t\t\t\treturn False, data\n\t\t\telif object_info == 0x09: # bool, true\n\t\t\t\treturn True, data\n\t\t\telse:\n\t\t\t\traise CFLBinaryPListParseError(\"unsupported object info value for object type 0x00: {0:#x}\".format(object_info))\n\t\t\n\t\telif object_type == 0x10:     # int, big-endian\n\t\t\treturn cls._unpack_int(object_info, data)\n\t\t\n\t\telif object_type == 0x20:     # real, big-endian\n\t\t\treturn cls._unpack_real(object_info, data)\n\t\t\n\t\telif object_type == 0x30:     # date\n\t\t\t#XXX: not sure if this is actually used\n\t\t\traise CFLBinaryPListParseError(\"date support not implemented\")\n\t\t\n\t\telif object_type == 0x40:     # data\n\t\t\tsize, data = cls._unpack_count(object_info, data)\n\t\t\t#XXX: we return data as str type, is this ok?\n\t\t\treturn _lslice(data, size)\n\t\t\n\t\telif object_type == 0x50:     # string, ASCII\n\t\t\traise CFLBinaryPListParseError(\"ASCII string support not implemented\")\n\t\t\n\t\telif object_type == 0x60:     # string, Unicode\n\t\t\traise CFLBinaryPListParseError(\"Unicode string support not implemented\")\n\t\t\n\t\telif object_type == 0x70:     # string, UTF8, NULL terminated\n\t\t\traw = \"\"\n\t\t\twhile True:\n\t\t\t\tbyte, data = _lslice(data, 1)\n\t\t\t\tif byte == \"\\x00\":\n\t\t\t\t\tbreak\n\t\t\t\traw += byte\n\t\t\t#XXX: what exceptions could we get here?\n\t\t\tobj = raw.decode(\"utf-8\")\n\t\t\treturn obj, data\n\t\t\n\t\telif object_type == 0x80:      # uid\n\t\t\traise CFLBinaryPListParseError(\"uid support not implemented\")\n\t\t\n\t\telif object_type == 0xA0:      # array\n\t\t\tobj = []\n\t\t\twhile True:\n\t\t\t\telement, data = cls._unpack_object(data)\n\t\t\t\tif element == None:\n\t\t\t\t\tbreak\n\t\t\t\tobj.append(element)\n\t\t\treturn obj, data\n\t\t\n\t\telif object_type == 0xB0:      # ordset\n\t\t\traise CFLBinaryPListParseError(\"ordset support not implemented\")\n\t\t\n\t\telif object_type == 0xC0:      # set\n\t\t\traise CFLBinaryPListParseError(\"set support not implemented\")\n\t\t\n\t\telif object_type == 0xD0:      # dict\n\t\t\tkeys = []\n\t\t\tvalues = []\n\t\t\twhile True:\n\t\t\t\tkey, data = cls._unpack_object(data)\n\t\t\t\tif key == None:\n\t\t\t\t\tbreak\n\t\t\t\tkeys.append(key)\n\t\t\t\t\n\t\t\t\tvalue, data = cls._unpack_object(data)\n\t\t\t\tvalues.append(value)\n\t\t\t\n\t\t\tobj = OrderedDict()\n\t\t\tfor i in range(len(keys)):\n\t\t\t\tobj[keys[i]] = values[i]\n\t\t\t\n\t\t\treturn obj, data\n\t\t\n\t\telse:\n\t\t\traise CFLBinaryPListParseError(\"unsupported object type: {0:#x}\".format(object_type))\n\t\n\t@classmethod\n\tdef parse(cls, data):\n\t\t\"\"\" Parse plist data into equivalent Python built-in object type\n\t\t\n\t\tReturns:\n\t\t\tobj\n\t\t\n\t\tRaises:\n\t\t\tCFLBinaryPListParseError\n\t\t\"\"\"\n\t\t# bail now if there isn't enough data for header, footer, and at least one object\n\t\tif len(data) < (_header_size + _footer_size + 1):\n\t\t\traise CFLBinaryPListParseError(\"not enough data to parse\")\n\t\t\n\t\theader_data, data = _lslice(data, _header_size)\n\t\tif header_data != _header_magic:\n\t\t\traise CFLBinaryPListParseError(\"bad header magic\")\n\t\t\n\t\t# read object stream (assume one root object)\n\t\tobj, remaining_data = cls._unpack_object(data)\n\t\tif len(remaining_data) > _footer_size:\n\t\t\traise CFLBinaryPListParseError(\"extra data found after unpacking root object\")\n\t\t\n\t\tif remaining_data != _footer_magic:\n\t\t\traise CFLBinaryPListParseError(\"bad footer magic\")\n\t\t\n\t\treturn obj\n"
  },
  {
    "path": "acp/cli.py",
    "content": "import argparse\nimport logging\nimport os.path\nimport sys\nimport time\n\nfrom collections import OrderedDict\n\nfrom .basebinary import *\nfrom .client import ACPClient\nfrom .exception import *\nfrom .property import ACPProperty\n\n\nclass _ArgParser(argparse.ArgumentParser):\n\tdef error(self, message):\n\t\tsys.stderr.write(\"error: {0}\\n\".format(message))\n\t\t#self.print_help()\n\t\tsys.exit(2)\n\n\ndef _cmd_not_implemented(*unused):\n\traise ACPCommandLineError(\"command handler not implemented\")\n\ndef _cmd_listprop(unused):\n\tprint \"\\nSupported properties:\\n\"\n\tprop_names = ACPProperty.get_supported_property_names()\n\tfor name in prop_names:\n\t\tprint \"{0}: {1}\".format(name, ACPProperty.get_property_info_string(name, \"description\"))\n\tprint\n\ndef _cmd_helpprop(args):\n\tprop_name = args.pop()\n\tdescription = ACPProperty.get_property_info_string(prop_name, \"description\")\n\tprop_type = ACPProperty.get_property_info_string(prop_name, \"type\")\n\tvalidation = ACPProperty.get_property_info_string(prop_name, \"validation\")\n\ts = \"{0} ({1}\".format(description, prop_type)\n\tif validation:\n\t\ts += \", {0})\".format(validation)\n\telse:\n\t\ts += \")\"\n\tprint s\n\ndef _cmd_getprop(client, args):\n\tprop_name = args.pop()\n\tprop = client.get_properties([prop_name])\n\tif len(prop):\n\t\tprint ACPProperty(prop_name, prop[0].value)\n\ndef _cmd_setprop(client, args):\n\tprop_name, prop_value = args\n\tprop_type = ACPProperty.get_property_info_string(prop_name, \"type\")\n\tprop = ACPProperty()\n\tif prop_type == \"dec\":\n\t\ttry:\n\t\t\tprop = ACPProperty(prop_name, int(prop_value))\n\t\texcept ValueError:\n\t\t\tlogging.error(\"value for \\\"{0}\\\" has the wrong type, should be {0}\".format(prop_name, prop_type))\n\telif prop_type == \"hex\":\n\t\ttry:\n\t\t\t#XXX: this is not the right way to do exceptions\n\t\t\tprop = ACPProperty(prop_name, int(prop_value, 16))\n\t\texcept ValueError:\n\t\t\tlogging.error(\"value for \\\"{0}\\\" has the wrong type, should be {0}\".format(prop_name, prop_type))\n\telif prop_type == \"mac\":\n\t\t#XXX: not catching our exception\n\t\tprop = ACPProperty(prop_name, prop_value)\n\telif prop_type == \"bin\":\n\t\tprop = ACPProperty(prop_name, prop_value.decode(\"hex\"))\n\telif prop_type == \"str\":\n\t\tprop = ACPProperty(prop_name, prop_value)\n\telif prop_type in [\"cfb\", \"log\"]:\n\t\tlogging.error(\"unsupported prop type: {0}\".format(prop_type))\n\tclient.set_properties({prop_name : prop})\n\ndef _cmd_dumpprop(client, unused):\n\tprop_names = ACPProperty.get_supported_property_names()\n\tproperties = client.get_properties(prop_names)\n\tfor prop in properties:\n\t\tpadded_description = ACPProperty.get_property_info_string(prop.name, \"description\").ljust(32, \" \")\n\t\tprint \"{0}: {1}\".format(padded_description, prop)\n\ndef _cmd_acpprop(client, unused):\n\tprops_reply = client.get_properties([\"prop\"])\n\tprops_raw = props_reply[0].value\n\tprops = \"\"\n\tfor i in range(len(props_raw) / 4):\n\t\tprops += \"{0}\\n\".format(props_raw[i*4:i*4+4])\n\tprint props\n\ndef _cmd_dump_syslog(client, unused):\n\tprint \"{0}\".format(client.get_properties([\"logm\"])[0])\n\ndef _cmd_reboot(client, unused):\n\tprint \"Rebooting device\"\t\n\tclient.set_properties({\"acRB\" : ACPProperty(\"acRB\", 0)})\n\ndef _cmd_factory_reset(client, unused):\n\tprint \"Performing factory reset\"\t\n\tclient.set_properties(OrderedDict([(\"acRF\",ACPProperty(\"acRF\", 0)), (\"acRB\",ACPProperty(\"acRB\", 0))]))\n\ndef _cmd_flash_primary(client, args):\n\tfw_path = args.pop()\n\tif os.path.exists(fw_path):\n\t\twith open(fw_path, \"rb\") as fw_file:\n\t\t\tfw_data = fw_file.read()\n\t\tprint \"Flashing primary firmware partition\"\n\t\tclient.flash_primary(fw_data)\n\telse:\n\t\tlogging.error(\"Basebinary not readable at path: {0}\".format(fw_path))\n\ndef _cmd_do_feat_command(client, unused):\n\tprint client.get_features()\n\ndef _cmd_decrypt(args):\n\t(inpath, outpath) = args\n\twith open(inpath, \"rb\") as infile:\n\t\tindata = infile.read()\n\t\n\t#XXX: lazy, fixme\n\ttry:\n\t\toutdata = Basebinary.parse(indata)\n\texcept BasebinaryError:\n\t\traise\n\telse:\n\t\twith open(outpath, \"wb\") as outfile:\n\t\t\toutfile.write(outdata)\n\ndef _cmd_extract(args):\n\t(inpath, outpath) = args\n\twith open(inpath, \"rb\") as infile:\n\t\tindata = infile.read()\n\t\n\t#XXX: lazy, fixme\n\ttry:\n\t\toutdata = Basebinary.extract(indata)\n\texcept BasebinaryError:\n\t\traise\n\telse:\n\t\twith open(outpath, \"wb\") as outfile:\n\t\t\toutfile.write(outdata)\n\ndef _cmd_srp_test(client, unused):\n\tprint \"SRP testing\"\n\tclient.authenticate_AppleSRP()\n\tclient.close()\n\n\ndef main():\n\t#TODO: add CLI arg for verbosity\n\tlogging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)\n\t\n\tparser = _ArgParser()\n\t\n\tparameters_group = parser.add_argument_group(\"AirPort client parameters\")\n\tparameters_group.add_argument(\"-t\", \"--target\", metavar=\"address\", help=\"IP address or hostname of the target router\")\n\tparameters_group.add_argument(\"-p\", \"--password\", metavar=\"password\", help=\"router admin password\")\n\t\n\tairport_client_group = parser.add_argument_group(\"AirPort client commands\")\n\tairport_client_group.add_argument(\"--listprop\", action=\"store_const\", const=True, help=\"list supported properties\")\n\tairport_client_group.add_argument(\"--helpprop\", metavar=\"property\", nargs=1, help=\"print the description of the specified property\")\n\tairport_client_group.add_argument(\"--getprop\", metavar=\"property\", nargs=1, help=\"get the value of the specified property\")\n\tairport_client_group.add_argument(\"--setprop\", metavar=(\"property\", \"value\"), nargs=2, help=\"set the value of the specified property\")\n\tairport_client_group.add_argument(\"--dumpprop\", action=\"store_const\", const=True, help=\"dump values of all supported properties\")\n\tairport_client_group.add_argument(\"--acpprop\", action=\"store_const\", const=True, help=\"get acp acpprop list\")\n\tairport_client_group.add_argument(\"--dump-syslog\", action=\"store_const\", const=True, help=\"dump the router system log\")\n\tairport_client_group.add_argument(\"--reboot\", action=\"store_const\", const=True, help=\"reboot device\")\n\tairport_client_group.add_argument(\"--factory-reset\", action=\"store_const\", const=True, help=\"RESET EVERYTHING and reboot; you have been warned!\")\n\tairport_client_group.add_argument(\"--flash-primary\", metavar=\"firmware_path\", nargs=1, help=\"flash primary partition firmware\")\n\tairport_client_group.add_argument(\"--do-feat-command\", action=\"store_const\", const=True, help=\"send 0x1b (feat) command\")\n\t\n\tbasebinary_group = parser.add_argument_group(\"Basebinary commands\")\n\tbasebinary_group.add_argument(\"--decrypt\", metavar=(\"inpath\", \"outpath\"), nargs=2, help=\"decrypt the basebinary\")\n\tbasebinary_group.add_argument(\"--extract\", metavar=(\"inpath\", \"outpath\"), nargs=2, help=\"extract the gzimg contents\")\n\t\n\ttest_group = parser.add_argument_group(\"Test arguments\")\n\ttest_group.add_argument(\"--srp-test\", action=\"store_const\", const=True, help=\"SRP (requires OS X)\")\n\t\n\targs_dict = vars(parser.parse_args())\n\t\n\t#TODO: give each element a dict containing parameter requirements/argparse infos, then generate parser based on this\n\tcommands = {\n\t\t\"listprop\": \"local\",\n\t\t\"helpprop\": \"local\",\n\t\t\"getprop\": \"remote_admin\",\n\t\t\"setprop\": \"remote_admin\",\n\t\t\"dumpprop\": \"remote_admin\",\n\t\t\"acpprop\": \"remote_admin\",\n\t\t\"dump_syslog\": \"remote_admin\",\n\t\t\"reboot\": \"remote_admin\",\n\t\t\"factory_reset\": \"remote_admin\",\n\t\t\"flash_primary\": \"remote_admin\",\n\t\t\"do_feat_command\": \"remote_noauth\",\n\t\t\"decrypt\": \"local\",\n\t\t\"extract\": \"local\",\n\t\t\"srp_test\": \"remote_admin\",\n\t\t}\n\t\n\ttarget = args_dict[\"target\"]\n\tpassword = args_dict[\"password\"]\n\tcommand_args = {k: v for k, v in args_dict.items() if k in commands and v is not None}\n\t\n\tif len(command_args) == 0:\n\t\tlogging.error(\"must specify a command\")\n\t\t\n\telif len(command_args) == 1:\n\t\t#TODO: clean this up a bit\n\t\tcmd, arg = command_args.popitem()\n\t\tassert commands[cmd] in [\"local\", \"remote_noauth\", \"remote_admin\"], \"unknown command type \\\"{0}\\\"\".format(commands[cmd])\n\t\tcmd_handler_name = \"_cmd_{0}\".format(cmd)\n\t\tcmd_handler = globals().get(cmd_handler_name, _cmd_not_implemented)\n\t\t\n\t\tif commands[cmd] == \"local\":\n\t\t\tcmd_handler(arg)\n\t\t\n\t\tif commands[cmd] == \"remote_noauth\":\n\t\t\tif target is not None:\n\t\t\t\tc = ACPClient(target)\n\t\t\t\tc.connect()\n\t\t\t\tcmd_handler(c, arg)\n\t\t\t\tc.close()\n\t\t\telse:\n\t\t\t\tlogging.error(\"must specify a target\")\n\t\t\n\t\tif commands[cmd] == \"remote_admin\":\n\t\t\tif target is not None and password is not None:\n\t\t\t\tc = ACPClient(target, password)\n\t\t\t\tc.connect()\n\t\t\t\tcmd_handler(c, arg)\n\t\t\t\tc.close()\n\t\t\telse:\n\t\t\t\tlogging.error(\"must specify a target and administrator password\")\n\t\t\t\t\n\telse:\n\t\tlogging.error(\"multiple commands not supported, choose only one\")\n"
  },
  {
    "path": "acp/clibs/AppleSRP.py",
    "content": "from ctypes import *\n\n\n#XXX: hax to display NULL pointer thingies\ndef _fmt_void_ptr(value):\n\tif value is None:\n\t\treturn 0\n\treturn value\n\ndef _fmt_cstr(value):\n\tif cast(value, c_void_p).value is None:\n\t\treturn \"<null cstr>\"\n\treturn value.contents.get_data_buffer().encode(\"hex\")\n\ndef _fmt_ccz_class(value):\n\tif cast(value, c_void_p).value is None:\n\t\treturn \"<null ccz_class>\"\n\treturn value.contents\n\ndef _fmt_ccz(value):\n\tif cast(value, c_void_p).value is None:\n\t\treturn \"<null ccz>\"\n\treturn value.contents\n\n\nclass cstr(Structure):\n\t_fields_ = [(\"data\", c_void_p),\n\t            (\"length\", c_long),\n\t            (\"cap\", c_long),\n\t            (\"ref\", c_int),\n\t            (\"allocator\", c_void_p)]\n\t\n\tdef __str__(self):\n\t\ts =  \"cstr:  {0!r}\\n\".format(self)\n\t\ts += \"data:  {0}\\n\".format(self.get_data_buffer().encode(\"hex\"))\n\t\ts += \"len:   {0}\\n\".format(self.length)\n\t\ts += \"cap:   {0}\\n\".format(self.cap)\n\t\ts += \"ref:   {0}\\n\".format(self.ref)\n\t\ts += \"alloc: {0:#x}\".format(self.allocator)\n\t\treturn s\n\t\n\tdef get_data_buffer(self):\n\t\treturn string_at(self.data, self.length)\n\n\n# define SHA_LBLOCK      16\n'''\ntypedef struct SHAstate_st {\n    SHA_LONG h0, h1, h2, h3, h4;\n    SHA_LONG Nl, Nh;\n    SHA_LONG data[SHA_LBLOCK];\n    unsigned int num;\n} SHA_CTX;\n'''\nclass SHA1_CTX(Structure):\n\t_fields_ = [(\"h0\", c_uint),\n\t            (\"h1\", c_uint),\n\t            (\"h2\", c_uint),\n\t            (\"h3\", c_uint),\n\t            (\"h4\", c_uint),\n\t            (\"Nl\", c_uint),\n\t            (\"Nh\", c_uint),\n\t            (\"data\", c_uint * 16), # uninitialized data???\n\t            (\"num\", c_uint)]\n\t\n\tdef __str__(self):\n\t\ts =  \"SHA1_CTX: {0!r}\\n\".format(self)\n\t\ts += \"h0:       {0:#x}\\n\".format(self.h0)\n\t\ts += \"h1:       {0:#x}\\n\".format(self.h1)\n\t\ts += \"h2:       {0:#x}\\n\".format(self.h2)\n\t\ts += \"h3:       {0:#x}\\n\".format(self.h3)\n\t\ts += \"h4:       {0:#x}\\n\".format(self.h4)\n\t\ts += \"Nl:       {0}\\n\".format(self.Nl)\n\t\ts += \"Nh:       {0}\\n\".format(self.Nh)\n\t\ts += \"data:     {0}\\n\".format(\"\".join([\"{0:08x}\".format(self.data[i]) for i in range(16)])) # uninitialized data???\n\t\ts += \"num:      {0}\".format(self.num)\n\t\treturn s\n\n\n#define RFC2945_KEY_LEN 40\t/* length of session key (bytes) */\n#define RFC2945_RESP_LEN 20\t/* length of proof hashes (bytes) */\n'''\nstruct client_meth_st {\n  SHA1_CTX hash;\n  SHA1_CTX ckhash;\n  unsigned char k[RFC2945_KEY_LEN];\n};\n'''\nclass client_meth_st(Structure):\n\t_fields_ = [(\"hash\", SHA1_CTX),\n\t            (\"ckhash\", SHA1_CTX),\n\t            (\"k\", c_ubyte * 40)]\n\t\n\tdef __str__(self):\n\t\ts =  \"client_meth_st: {0!r}\\n\".format(self)\n\t\ts += \"hash:           {0}\\n\".format(self.hash)\n\t\ts += \"ckhash:         {0}\\n\".format(self.ckhash)\n\t\ts += \"k:              {0}\".format(self.k)\n\t\treturn s\n\n\n'''\nstruct server_meth_st {\n  SHA1_CTX hash;\n  SHA1_CTX ckhash;\n  SHA1_CTX oldhash;\n  SHA1_CTX oldckhash;\n  unsigned char k[RFC2945_KEY_LEN];\n  unsigned char r[RFC2945_RESP_LEN];\n};\n'''\nclass server_meth_st(Structure):\n\t_fields_ = [(\"hash\", SHA1_CTX),\n\t            (\"ckhash\", SHA1_CTX),\n\t            (\"oldhash\", SHA1_CTX),\n\t            (\"oldckhash\", SHA1_CTX),\n\t            (\"k\", c_ubyte * 40),\n\t            (\"r\", c_ubyte * 20)]\n\t\n\tdef __str__(self):\n\t\ts =  \"server_meth_st: {0!r}\\n\".format(self)\n\t\ts += \"hash:           {0}\\n\".format(self.hash)\n\t\ts += \"ckhash:         {0}\\n\".format(self.ckhash)\n\t\ts += \"oldhash:        {0}\\n\".format(self.oldhash)\n\t\ts += \"oldckhash:      {0}\\n\".format(self.oldckhash)\n\t\ts += \"k:              {0}\\n\".format(self.k)\n\t\ts += \"r:              {0}\".format(self.r)\n\t\treturn s\n\n\n'''\nstruct ccz_class {\n\tvoid *ctx;\n\tvoid *(*ccz_alloc)(void *, size_t);\n\tvoid *(*ccz_realloc)(void *, size_t, void *, size_t);\n\tvoid (*ccz_free)(void *, size_t, void *);\n};\n'''\nclass ccz_class(Structure):\n\t_fields_ = [(\"ctx\", c_void_p),\n\t            (\"ccz_alloc\", c_void_p),\n\t            (\"ccz_realloc\", c_void_p),\n\t            (\"ccz_free\", c_void_p)]\n\t\n\tdef __str__(self):\n\t\ts =  \"ccz_class:   {0!r}\\n\".format(self)\n\t\ts += \"ctx:         {0:#x}\\n\".format(_fmt_void_ptr(self.ctx))\n\t\ts += \"ccz_alloc:   {0:#x}\\n\".format(self.ccz_alloc)\n\t\ts += \"ccz_realloc: {0:#x}\\n\".format(self.ccz_realloc)\n\t\ts += \"ccz_free:    {0:#x}\".format(self.ccz_free)\n\t\treturn s\n\n\n'''\nstruct ccz {\n    size_t n;\n    struct ccz_class *isa;\n    int sac;\n    cc_unit *u;\n};\ntypedef struct ccz ccz;\n'''\nclass ccz(Structure):\n\t_fields_ = [(\"n\", c_size_t),\n\t            (\"isa\", POINTER(ccz_class)),\n\t            (\"sac\", c_int),\n\t            (\"u\", c_void_p)]\n\t\n\tdef __str__(self):\n\t\ts =  \"ccz: {0!r}\\n\".format(self)\n\t\ts += \"n:   {0}\\n\".format(self.n)\n\t\ts += \"isa: {0}\\n\".format(_fmt_ccz_class(self.isa))\n\t\ts += \"sac: {0}\\n\".format(self.sac)\n\t\ts += \"u:   {0:#x}\".format(_fmt_void_ptr(self.u))\n\t\treturn s\n\n\n'''\nstruct srp_st {\n  int magic;\t/* To distinguish client from server (and for sanity) */\n\n  int flags;\n\n  cstr * username;\n\n  BigInteger modulus;\n  BigInteger generator;\n  cstr * salt;\n\n  BigInteger verifier;\n  BigInteger password;\n\n  BigInteger pubkey;\n  BigInteger secret;\n  BigInteger u;\n\n  BigInteger key;\n\n  cstr * ex_data;\n\n  SRP_METHOD * meth;\n  void * meth_data;\n\n  BigIntegerCtx bctx;\t     /* to cache temporaries if available */\n  BigIntegerModAccel accel;  /* to accelerate modexp if available */\n\n  SRP_CLIENT_PARAM_VERIFY_CB param_cb;\t/* to verify params */\n  SRP_SERVER_LOOKUP * slu;   /* to look up users */\n};\n'''\nclass srp_st(Structure):\n\t_fields_ = [(\"magic\", c_int),\n\t            (\"flags\", c_int),\n\t            (\"username\", POINTER(cstr)),\n\t            (\"modulus\", POINTER(ccz)),\n\t            (\"generator\", POINTER(ccz)),\n\t            (\"salt\", POINTER(cstr)),\n\t            (\"verifier\", POINTER(ccz)),\n\t            (\"password\", POINTER(ccz)),\n\t            (\"pubkey\", POINTER(ccz)),\n\t            (\"secret\", POINTER(ccz)),\n\t            (\"u\", POINTER(ccz)),\n\t            (\"key\", POINTER(ccz)),\n\t            (\"ex_data\", POINTER(cstr)),\n\t            (\"meth\", c_void_p),\n\t            #XXXXXXXXXXXXXXXX\n\t            (\"meth_data\", POINTER(client_meth_st)),\n\t            #(\"meth_data\", POINTER(server_meth_st)),\n\t            #XXXXXXXXXXXXXXXX\n\t            (\"bctx\", c_void_p),\n\t            (\"accel\", c_void_p),\n\t            (\"param_cb\", c_void_p),\n\t            (\"slu\", c_void_p)]\n\t\n\tdef __str__(self):\n\t\ts =  \"*** START ***\\n\"\n\t\ts += \"srp_st:    {0!r}\\n\".format(self)\n\t\ts += \"magic:     {0}\\n\".format(self.magic)\n\t\ts += \"flags:     {0}\\n\".format(self.flags)\n\t\ts += \"username:  {0}\\n\".format(_fmt_cstr(self.username))\n\t\ts += \"modulus:   {0}\\n\".format(_fmt_ccz(self.modulus))\n\t\ts += \"generator: {0}\\n\".format(_fmt_ccz(self.generator))\n\t\ts += \"salt:      {0}\\n\".format(_fmt_cstr(self.salt))\n\t\ts += \"verifier:  {0}\\n\".format(_fmt_ccz(self.verifier))\n\t\ts += \"password:  {0}\\n\".format(_fmt_ccz(self.password))\n\t\ts += \"pubkey:    {0}\\n\".format(_fmt_ccz(self.pubkey))\n\t\ts += \"secret:    {0}\\n\".format(_fmt_ccz(self.secret))\n\t\ts += \"u:         {0}\\n\".format(_fmt_ccz(self.u))\n\t\ts += \"key:       {0}\\n\".format(_fmt_ccz(self.key))\n\t\ts += \"ex_data:   {0}\\n\".format(_fmt_cstr(self.ex_data))\n\t\ts += \"meth:      {0:#x}\\n\".format(_fmt_void_ptr(self.meth))\n\t\ts += \"meth_data: {0}\\n\".format(self.meth_data.contents) #XXXXXXXXXXXXXXXX\n\t\ts += \"bctx:      {0:#x}\\n\".format(_fmt_void_ptr(self.bctx))\n\t\ts += \"accel:     {0:#x}\\n\".format(_fmt_void_ptr(self.accel))\n\t\ts += \"param_cb:  {0:#x}\\n\".format(_fmt_void_ptr(self.param_cb))\n\t\ts += \"slu:       {0:#x}\\n\".format(_fmt_void_ptr(self.slu))\n\t\ts += \"**** END ****\"\n\t\treturn s\n\n\n__AppleSRP = cdll.LoadLibrary(\"/System/Library/PrivateFrameworks/AppleSRP.framework/Versions/A/AppleSRP\")\n#print \"AppleSRP:\", __AppleSRP\n\n# SRP_METHOD *SRP6a_client_method(void)\nSRP6a_client_method = __AppleSRP.SRP6a_client_method\nSRP6a_client_method.restype = c_void_p\n\n# SRP_METHOD *SRP6a_server_method(void)\nSRP6a_server_method = __AppleSRP.SRP6a_server_method\nSRP6a_server_method.restype = c_void_p\n\n# SRP *SRP_new(SRP_METHOD *meth)\nSRP_new = __AppleSRP.SRP_new\n#SRP_new.restype = c_void_p\nSRP_new.restype = POINTER(srp_st)\nSRP_new.argtypes = [ c_void_p ]\n\n# SRP_RESULT SRP_set_username(SRP *srp, const char *username)\nSRP_set_username = __AppleSRP.SRP_set_username\nSRP_set_username.argtypes = [ c_void_p, c_char_p ]\n\n# SRP_RESULT SRP_set_params(SRP *srp, const unsigned char *modulus, int modlen,\n#                           const unsigned char *generator, int genlen,\n#                           const unsigned char *salt, int saltlen)\nSRP_set_params = __AppleSRP.SRP_set_params\nSRP_set_params.argtypes = [ c_void_p, c_char_p, c_int, c_char_p, c_int, c_char_p, c_int ]\n\n# SRP_RESULT SRP_gen_pub(SRP *srp, cstr **result)\nSRP_gen_pub = __AppleSRP.SRP_gen_pub\nSRP_gen_pub.argtypes = [ c_void_p, POINTER(POINTER(cstr)) ]\n\n# SRP_RESULT SRP_set_auth_password(SRP *srp, const unsigned char *password, int passlen)\nSRP_set_auth_password = __AppleSRP.SRP_set_auth_password\nSRP_set_auth_password.argtypes = [ c_void_p, c_char_p, c_int ]\n\n# SRP_RESULT SRP_compute_key(SRP *srp, cstr **result, const unsigned char *pubkey, int pubkeylen)\nSRP_compute_key = __AppleSRP.SRP_compute_key\nSRP_compute_key.argtypes = [ c_void_p, POINTER(POINTER(cstr)), c_char_p, c_int ]\n\n# SRP_RESULT SRP_respond(SRP *srp, cstr **proof)\nSRP_respond = __AppleSRP.SRP_respond\nSRP_respond.argtypes = [ c_void_p, POINTER(POINTER(cstr)) ]\n\n# SRP_RESULT SRP_verify(SRP *srp, const unsigned char *proof, int prooflen)\nSRP_verify = __AppleSRP.SRP_verify\nSRP_verify.argtypes = [ c_void_p, c_char_p, c_int ]\n\n# SRP_RESULT SRP_free(SRP *srp)\nSRP_free = __AppleSRP.SRP_free\nSRP_free.argtypes = [ c_void_p ]\n\n# cstr *cstr_new(void)\ncstr_new = __AppleSRP.cstr_new\ncstr_new.restype = POINTER(cstr)\n\n# void cstr_free(cstr *str)\ncstr_free = __AppleSRP.cstr_free\ncstr_free.restype = None\ncstr_free.argtypes = [ POINTER(cstr) ]\n"
  },
  {
    "path": "acp/clibs/__init__.py",
    "content": ""
  },
  {
    "path": "acp/client.py",
    "content": "import logging\nimport os\nimport struct\nimport time\n\nfrom .cflbinary import CFLBinaryPListComposer, CFLBinaryPListParser\nfrom .message import ACPMessage\nfrom .property import ACPProperty\nfrom .session import ACPClientSession\n\n\nclass ACPClient(object):\n\tdef __init__(self, target, password=\"\"):\n\t\tself.target = target\n\t\tself.password = password\n\t\t\n\t\tself.session = ACPClientSession(target, password)\n\t\n\t\n\tdef connect(self):\n\t\tself.session.connect()\n\t\n\t\n\tdef close(self):\n\t\tself.session.close()\n\t\n\t\n\tdef send(self, data):\n\t\tself.session.send(data)\n\t\n\t\n\tdef recv(self, size):\n\t\treturn self.session.recv(size)\n\t\n\t\n\tdef recv_message_header(self):\n\t\treturn self.recv(ACPMessage.header_size)\n\t\n\t\n\tdef recv_property_element_header(self):\n\t\treturn self.recv(ACPProperty.element_header_size)\n\t\n\t\n\tdef get_properties(self, prop_names=[]):\n\t\t# request property by sending name and \"null\" value\n\t\tpayload = \"\"\n\t\tfor name in prop_names:\n\t\t\tpayload += ACPProperty.compose_raw_element(0, ACPProperty(name))\n\t\t\n\t\trequest = ACPMessage.compose_getprop_command(4, self.password, payload)\n\t\tself.send(request)\n\t\t\n\t\traw_reply = self.recv_message_header()\n\t\treply_header = ACPMessage.parse_raw(raw_reply)\n\t\t\n\t\tif reply_header.error_code != 0:\n\t\t\tprint \"get_properties error code: {0:#x}\".format(reply_header.error_code)\n\t\t\t#XXX: blah, what to do...\n\t\t\treturn []\n\t\t\n\t\tprops = []\n\t\twhile True:\n\t\t\tprop_header = self.recv_property_element_header()\n\t\t\tname, flags, size = ACPProperty.parse_raw_element_header(prop_header)\n\t\t\tlogging.debug(\"name  \".format(name))\n\t\t\tlogging.debug(\"flags \".format(flags))\n\t\t\tlogging.debug(\"size  \".format(size))\n\t\t\t\n\t\t\tprop_data = self.recv(size)\n\t\t\tlogging.debug(\"prop_data {0!r}\".format(prop_data))\n\t\t\t\n\t\t\tif flags & 1:\n\t\t\t\t(error_code, ) = struct.unpack(\">I\", prop_data)\n\t\t\t\tprint \"error requesting value for property \\\"{0}\\\": {1:#x}\".format(name, error_code)\n\t\t\t\tcontinue\n\t\t\t\n\t\t\tprop = ACPProperty(name, prop_data)\n\t\t\tlogging.debug(\"prop {0!r}\".format(prop))\n\t\t\t\n\t\t\t#XXX: this is still a bit ugly\n\t\t\tif prop.name is None and prop.value is None:\n\t\t\t\tlogging.debug(\"found empty prop end marker\")\n\t\t\t\tbreak\n\t\t\t\n\t\t\t#XXX: should we should return dict(name=name, prop=ACPProperty(name, value)) instead?\n\t\t\tprops.append(prop)\n\t\t\t\n\t\treturn props\n\t\n\t\n\tdef set_properties(self, props_dict={}):\n\t\tpayload = \"\"\n\t\tfor name, prop in props_dict.iteritems():\n\t\t\tlogging.debug(\"prop: {0!r}\".format(prop))\n\t\t\tpayload += ACPProperty.compose_raw_element(0, prop)\n\t\trequest = ACPMessage.compose_setprop_command(0, self.password, payload)\n\t\tself.send(request)\n\t\t\n\t\traw_reply = self.recv_message_header()\n\t\treply_header = ACPMessage.parse_raw(raw_reply)\n\t\t\n\t\tif reply_header.error_code != 0:\n\t\t\tprint \"set_properties error code: {0:#x}\".format(reply_header.error_code)\n\t\t\t#XXX: blah, what to do...\n\t\t\treturn\n\t\t\n\t\tprop_header = self.recv_property_element_header()\n\t\tname, flags, size = ACPProperty.parse_raw_element_header(prop_header)\n\t\tlogging.debug(\"name  {0!r}\".format(name))\n\t\tlogging.debug(\"flags {0!r}\".format(flags))\n\t\tlogging.debug(\"size  {0!r}\".format(size))\n\t\t\n\t\tprop_data = self.recv(size)\n\t\tlogging.debug(\"prop_data {0!r}\".format(prop_data))\n\t\t\n\t\tif flags & 1:\n\t\t\t(error_code, ) = struct.unpack(\">I\", prop_data)\n\t\t\tprint \"error setting value for property \\\"{0}\\\": {1:#x}\".format(name, error_code)\n\t\t\treturn\n\t\t\t\n\t\tprop = ACPProperty(name, prop_data)\n\t\tlogging.debug(\"prop {0!r}\".format(prop))\n\t\t\n\t\t#XXX: this is still a bit ugly\n\t\tif prop.name is None and prop.value is None:\n\t\t\tlogging.debug(\"found empty prop end marker\")\n\t\n\t\n\tdef get_features(self):\n\t\tself.send(ACPMessage.compose_feat_command(0))\n\t\t\n\t\treply_header = ACPMessage.parse_raw(self.recv_message_header())\n\t\t\n\t\treply = self.recv(reply_header.body_size)\n\t\t\n\t\treturn CFLBinaryPListParser.parse(reply)\n\t\n\t\n\tdef flash_primary(self, payload):\n\t\tself.send(ACPMessage.compose_flash_primary_command(0, self.password, payload))\n\t\t\n\t\treply_header = ACPMessage.parse_raw(self.recv_message_header())\n\t\t\n\t\treturn self.recv(reply_header.body_size)\n\t\n\t\n\tdef authenticate_AppleSRP(self):\n\t\t#XXX: STILL TESTING SHIT\n\t\timport ctypes\n\t\tfrom .clibs import AppleSRP\n\t\tfrom collections import OrderedDict\t\t\n\t\t\n\t\tusername = u\"admin\"\n\t\t\n\t\tdic = OrderedDict([(u\"state\", 1), (u\"username\", username)])\n\t\tpayload = CFLBinaryPListComposer.compose(dic)\n\t\traw_message = ACPMessage.compose_auth_command(4, payload)\n\t\tself.send(raw_message)\n\t\t\n\t\traw_reply_header = self.recv_message_header()\n\t\treply_header = ACPMessage.parse_raw(raw_reply_header)\n\t\t\n\t\tif reply_header.error_code != 0:\n\t\t\tlogging.error(\"authenticate error code: {0:#x}\".format(reply_header.error_code))\n\t\t\t#XXX: blah, what to do...\n\t\t\treturn\n\t\t\n\t\tlogging.debug(\"recv_size: {0}\".format(reply_header.body_size))\n\t\traw_message = self.recv(reply_header.body_size)\n\t\tlogging.debug(\"raw_message: {0!r}\".format(raw_message))\n\t\tparams1 = CFLBinaryPListParser.parse(raw_message)\n\t\tlogging.debug(params1)\n\t\t\n\t\tn = params1[u\"modulus\"]\n\t\tg = params1[u\"generator\"]\n\t\tsalt = params1[u\"salt\"]\n\t\tserver_pkey = params1[u\"publicKey\"]\n\t\t\n\t\tnhex = n.encode(\"hex\")\n\t\tghex = g.encode(\"hex\")\n\t\t\n\t\tlogging.debug(\"nhex: {0}\".format(nhex))\n\t\tlogging.debug(\"ghex: {0}\".format(ghex))\n\t\tlogging.debug(\"salt: {0}\".format(salt.encode(\"hex\")))\n\t\tlogging.debug(\"server_pkey: {0}\".format(server_pkey.encode(\"hex\")))\n\t\t\n\t\t# create SRP context\n\t\tasrp = AppleSRP.SRP_new(AppleSRP.SRP6a_client_method())\n\t\t#logging.debug(asrp.contents)\n\t\t\n\t\t# set username\n\t\tlogging.debug(\"SRP_set_username: {0}\".format(AppleSRP.SRP_set_username(asrp, username)))\n\t\t#logging.debug(asrp.contents)\n\t\t\n\t\t# set parameters from server\n\t\tlogging.debug(\"SRP_set_params: {0}\".format(AppleSRP.SRP_set_params(asrp, n, len(n), g, len(g), salt, len(salt))))\n\t\t#logging.debug(asrp.contents)\n\t\t\n\t\t# generate public key\n\t\tclient_gen_pubkey_ptr = AppleSRP.cstr_new()\n\t\tlogging.debug(\"SRP_gen_pub: {0}\".format(AppleSRP.SRP_gen_pub(asrp, ctypes.byref(client_gen_pubkey_ptr))))\n\t\tclient_gen_pubkey = client_gen_pubkey_ptr.contents\n\t\tlogging.debug(client_gen_pubkey)\n\t\t#logging.debug(asrp.contents)\n\n\t\t# set password\n\t\tlogging.debug(\"SRP_set_auth_password: {0}\".format(AppleSRP.SRP_set_auth_password(asrp, self.password, len(self.password))))\n\t\t#logging.debug(asrp.contents)\n\t\t\n\t\t# compute key\n\t\tclient_computed_key_ptr = AppleSRP.cstr_new()\n\t\tlogging.debug(\"SRP_compute_key: {0}\".format(AppleSRP.SRP_compute_key(asrp, ctypes.byref(client_computed_key_ptr), server_pkey, len(server_pkey))))\n\t\tclient_computed_key = client_computed_key_ptr.contents\n\t\tlogging.debug(client_computed_key)\n\t\tclient_computed_key_buf = client_computed_key.get_data_buffer()\n\t\t#logging.debug(asrp.contents)\n\t\t\n\t\t# generate challenge response\n\t\tclient_proof_ptr = AppleSRP.cstr_new()\n\t\tlogging.debug(\"SRP_respond: {0}\".format(AppleSRP.SRP_respond(asrp, ctypes.byref(client_proof_ptr))))\n\t\tclient_proof = client_proof_ptr.contents\n\t\tlogging.debug(client_proof)\n\t\t#logging.debug(asrp.contents)\n\t\t\n\t\tclient_iv = os.urandom(0x10)\n\t\tclient_pkey = client_gen_pubkey.get_data_buffer()\n\t\tclient_proof = client_proof.get_data_buffer()\n\t\t\n\t\tdic = OrderedDict([(u\"iv\", client_iv), (u\"publicKey\", client_pkey), (u\"state\", 3), (u\"response\", client_proof)])\n\t\tpayload = CFLBinaryPListComposer.compose(dic)\n\t\traw_message = ACPMessage.compose_auth_command(4, payload)\n\t\tself.send(raw_message)\n\t\t\n\t\traw_reply_header = self.recv_message_header()\n\t\treply_header = ACPMessage.parse_raw(raw_reply_header)\n\t\t\n\t\tif reply_header.error_code != 0:\n\t\t\tlogging.debug(\"authenticate error code: {0:#x}\".format(reply_header.error_code))\n\t\t\t#XXX: blah, what to do...\n\t\t\treturn\n\t\t\n\t\tlogging.debug(\"recv_size: {0}\".format(reply_header.body_size))\n\t\traw_message = self.recv(reply_header.body_size)\n\t\tlogging.debug(\"raw_message: {0!r}\".format(raw_message))\n\t\tparams2 = CFLBinaryPListParser.parse(raw_message)\n\t\tlogging.debug(params2)\n\t\n\t\tserver_proof = params2[u\"response\"]\n\t\tserver_iv = params2[u\"iv\"]\n\t\t\n\t\t# verify server response\n\t\tlogging.debug(\"SRP_verify: {0}\".format(AppleSRP.SRP_verify(asrp, server_proof, len(server_proof))))\n\t\t#logging.debug(asrp.contents)\n\t\t\n\t\t# cleanup\n\t\tlogging.debug(\"Freeing cstr(s)\")\n\t\tAppleSRP.cstr_free(client_gen_pubkey_ptr)\n\t\tAppleSRP.cstr_free(client_computed_key_ptr)\n\t\tAppleSRP.cstr_free(client_proof_ptr)\n\t\tlogging.debug(\"SRP_free: {0}\".format(AppleSRP.SRP_free(asrp)))\n\t\t\n\t\t###self.session.enable_encryption(client_computed_key_buf, client_iv, server_iv)\n\n"
  },
  {
    "path": "acp/encryption.py",
    "content": "from Crypto.Cipher import AES\nfrom Crypto.Protocol import KDF\nfrom Crypto.Util import Counter\n\n\nPBKDF_salt0 = \"F072FA3F66B410A135FAE8E6D1D43D5F\".decode(\"hex\")\nPBKDF_salt1 = \"BD0682C9FE79325BC73655F4174B996C\".decode(\"hex\")\n\n\nclass _ACPEncryptionContext(object):\n\tdef __init__(self, key, iv):\n\t\tself.key = key\n\t\tself.iv = iv\n\t\t\n\t\tself.ctr = Counter.new(128, initial_value=int(iv.encode(\"hex\"), 16))\n\t\tself.cipher = AES.new(key, AES.MODE_CTR, counter=self.ctr)\n\n\nclass ACPEncryption(object):\n\tdef __init__(self, key, client_iv, server_iv):\n\t\tself._client_context = self._init_client_context(key, client_iv)\n\t\tself._server_context = self._init_server_context(key, server_iv)\n\t\n\tdef _init_client_context(cls, key, iv):\n\t\tderived_key = KDF.PBKDF2(key, PBKDF_salt0, 16, 5)\n\t\treturn _ACPEncryptionContext(derived_key, iv)\n\t\n\tdef _init_server_context(cls, key, iv):\n\t\tderived_key = KDF.PBKDF2(key, PBKDF_salt1, 16, 7)\n\t\treturn _ACPEncryptionContext(derived_key, iv)\n\t\n\t\n\tdef client_decrypt(self, data):\n\t\treturn self._client_context.cipher.decrypt(data)\n\t\n\t\n\tdef client_encrypt(self, data):\n\t\treturn self._client_context.cipher.encrypt(data)\n\t\n\t\n\tdef server_decrypt(self, data):\n\t\treturn self._server_context.cipher.decrypt(data)\n\t\n\t\n\tdef server_encrypt(self, data):\n\t\treturn self._server_context.cipher.encrypt(data)\n\n"
  },
  {
    "path": "acp/exception.py",
    "content": "#TODO: put other exceptions in here...\n\nclass ACPError(Exception):\n\t\"\"\"Base class for exceptions in this module.\"\"\"\n\tpass\n\n\nclass ACPClientError(ACPError):\n\t\"\"\"Exception raised for errors in the ACP client\"\"\"\n\tpass\n\n\nclass ACPCommandLineError(ACPError):\n\t\"\"\"Exception raised for command line invocation errors\"\"\"\n\tpass\n\n\nclass ACPMessageError(ACPError):\n\t\"\"\"Exception raised for errors processing ACP packets\"\"\"\n\tpass\n\n\nclass ACPPropertyError(ACPError):\n\t\"\"\"Exception raised for errors processing ACP properties\"\"\"\n\tpass\n\n"
  },
  {
    "path": "acp/keystream.py",
    "content": "\"\"\"Static key/seed for keystream generation\"\"\"\nACP_STATIC_KEY = \"5b6faf5d9d5b0e1351f2da1de7e8d673\".decode(\"hex\")\n\ndef generate_acp_keystream(length):\n\t\"\"\"Get key used to encrypt the header key (and some message data?)\n\t\n\tArgs:\n\t\tlength (int): length of keystream to generate\n\t\n\tReturns:\n\t\tString of requested length\n\t\n\tNote:\n\t\tKeystream repeats every 256 bytes\n\t\n\t\"\"\"\n\tkey = \"\"\n\tkey_idx = 0\n\t\n\twhile (key_idx < length):\n\t\tkey += chr((key_idx + 0x55 & 0xFF) ^ ord(ACP_STATIC_KEY[key_idx % len(ACP_STATIC_KEY)]))\n\t\tkey_idx += 1\n\t\n\treturn key\n"
  },
  {
    "path": "acp/message.py",
    "content": "import logging\nimport struct\nimport zlib\n\nfrom .exception import ACPMessageError\nfrom .keystream import *\n\n\ndef _generate_acp_header_key(password):\n\t\"\"\"\n\tEncrypt password for ACP message header key field\n\t\n\tNote:\n\t\tTruncates the password at 0x20 bytes, not sure if this is the right thing to use in all cases\n\t\n\tArgs:\n\t\tpassword (str): system password of the router (syAP)\n\t\n\tReturns:\n\t\tString containing encrypted password of proper length for the header field\n\t\n\t\"\"\"\n\tpw_len = 0x20\n\tpw_key = generate_acp_keystream(pw_len)\n\t\n\t# pad with NULLs\n\tpw_buf = password[:pw_len].ljust(pw_len, \"\\x00\")\n\tenc_pw_buf = \"\"\n\tfor i in range(pw_len):\n\t\tenc_pw_buf += chr(ord(pw_key[i]) ^ ord(pw_buf[i]))\t\n\t\n\treturn enc_pw_buf\n\n\nclass ACPMessage(object):\n\t\"\"\"ACP message composition and parsing\"\"\"\n\t\n\t#XXX: struct is stupid about unpacking unsigned ints > 0x7fffffff, so treat everything as signed and\n\t#     \"cast\" where necessary. Should we switch to using ctypes?\n\t_header_format = struct.Struct(\"!4s8i12x32s48x\")\n\t_header_magic  = \"acpp\"\n\t\n\theader_size = _header_format.size\n\t\n\t\n\tdef __init__(self, version, flags, unused, command, error_code, key, body=None, body_size=None):\n\t\tself.version = version\n\t\tself.flags = flags\n\t\tself.unused = unused\n\t\tself.command = command\n\t\tself.error_code = error_code\n\t\t\n\t\t# body is not specified, this is a stream header\n\t\tif body == None:\n\t\t\t# the body size is already specified, don't override it\n\t\t\tself.body_size = body_size if body_size != None else -1\n\t\t\tself.body_checksum = 1 # equivalent to zlib.adler32(\"\")\n\t\telse:\n\t\t\t# the body size is already specified, don't override it\n\t\t\tself.body_size = body_size if body_size != None else len(body)\n\t\t\tself.body_checksum = zlib.adler32(body)\n\t\t\n\t\tself.key = key\n\t\tself.body = body\n\t\n\t\n\tdef __str__(self):\n\t\ts =  \"ACPMessage:    {0!r}\\n\".format(self)\n\t\ts += \"body_checksum: {0:#x}\\n\".format(self.body_checksum)\n\t\ts += \"body_size:     {0:#x}\\n\".format(self.body_size)\n\t\ts += \"flags:         {0:#x}\\n\".format(self.flags)\n\t\ts += \"unused:        {0:#x}\\n\".format(self.unused)\n\t\ts += \"command:       {0:#x}\\n\".format(self.command)\n\t\ts += \"error_code:    {0:#x}\\n\".format(self.error_code)\n\t\ts += \"key:           {0!r}\".format(self.key)\n\t\treturn s\n\t\n\t\n\t@classmethod\n\tdef parse_raw(cls, data):\n\t\t# bail early if there is not enough data\n\t\tif len(data) < cls.header_size:\n\t\t\traise ACPMessageError(\"need to pass at least {0} bytes\".format(cls.header_size))\n\t\theader_data = data[:cls.header_size]\n\t\t# make sure there's data beyond the header before we try to access it\n\t\tbody_data = data[cls.header_size:] if len(data) > cls.header_size else None\n\t\t\n\t\t(magic, version, header_checksum, body_checksum, body_size, flags, unused, command, error_code, key) = cls._header_format.unpack(header_data)\n\t\tlogging.debug(\"ACP message header fields, parsed not validated\")\n\t\tlogging.debug(\"magic           {0!r}\".format(magic))\n\t\tlogging.debug(\"header_checksum {0:#x}\".format(header_checksum))\n\t\tlogging.debug(\"body_checksum   {0:#x}\".format(body_checksum))\n\t\tlogging.debug(\"body_size       {0:#x}\".format(body_size))\n\t\tlogging.debug(\"flags           {0:#x}\".format(flags))\n\t\tlogging.debug(\"unused          {0:#x}\".format(unused))\n\t\tlogging.debug(\"command         {0:#x}\".format(command))\n\t\tlogging.debug(\"error_code      {0:#x}\".format(error_code))\n\t\tlogging.debug(\"key             {0!r}\".format(key))\n\t\t\n\t\tif magic != cls._header_magic:\n\t\t\traise ACPMessageError(\"bad header magic\")\n\t\t\n\t\tif version not in [0x00000001, 0x00030001]:\n\t\t\traise ACPMessageError(\"invalid version\")\n\t\t\n\t\t#TODO: can we zero the header_checksum field without recreating the struct (how?)\n\t\ttmphdr = cls._header_format.pack(magic, version, 0, body_checksum, body_size, flags, unused, command, error_code, key)\n\t\tif header_checksum != zlib.adler32(tmphdr):\n\t\t\traise ACPMessageError(\"header checksum does not match\")\n\t\t\n\t\tif body_data and body_size == -1:\n\t\t\traise ACPMessageError(\"cannot handle stream header with data attached\")\n\t\t\n\t\tif body_data and body_size != len(body_data):\n\t\t\traise ACPMessageError(\"message body size does not match available data\")\n\t\t\n\t\tif body_data and body_checksum != zlib.adler32(body_data):\n\t\t\traise ACPMessageError(\"body checksum does not match\")\n\t\t\n\t\t#TODO: check flags\n\t\t\n\t\t#TODO: check status\n\t\t\n\t\tif command not in [1, 3, 4, 5, 6, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b]:\n\t\t\traise ACPMessageError(\"unknown command\")\n\t\t\n\t\t#TODO: check error code\n\t\t\n\t\treturn cls(version, flags, unused, command, error_code, key, body_data, body_size)\n\t\n\t\n\t@classmethod\n\tdef compose_echo_command(cls, flags, password, payload):\n\t\treturn cls(0x00030001, flags, 0, 1, 0, _generate_acp_header_key(password), payload)._compose_raw_packet()\n\t\n\t\n\t@classmethod\n\tdef compose_flash_primary_command(cls, flags, password, payload):\n\t\treturn cls(0x00030001, flags, 0, 3, 0, _generate_acp_header_key(password), payload)._compose_raw_packet()\n\t\n\t\n\t@classmethod\n\tdef compose_flash_secondary_command(cls, flags, password, payload):\n\t\treturn cls(0x00030001, flags, 0, 5, 0, _generate_acp_header_key(password), payload)._compose_raw_packet()\n\t\n\t\n\t@classmethod\n\tdef compose_flash_bootloader_command(cls, flags, password, payload):\n\t\treturn cls(0x00030001, flags, 0, 6, 0, _generate_acp_header_key(password), payload)._compose_raw_packet()\n\t\n\t\n\t@classmethod\n\tdef compose_getprop_command(cls, flags, password, payload):\n\t\treturn cls(0x00030001, flags, 0, 0x14, 0, _generate_acp_header_key(password), payload)._compose_raw_packet()\n\t\n\t\n\t@classmethod\n\tdef compose_setprop_command(cls, flags, password, payload):\n\t\treturn cls(0x00030001, flags, 0, 0x15, 0, _generate_acp_header_key(password), payload)._compose_raw_packet()\n\t\n\t\n\t@classmethod\n\tdef compose_perform_command(cls, flags, password, payload):\n\t\treturn cls(0x00030001, flags, 0, 0x16, 0, _generate_acp_header_key(password), payload)._compose_raw_packet()\n\t\n\t\n\t@classmethod\n\tdef compose_monitor_command(cls, flags, password, payload):\n\t\treturn cls(0x00030001, flags, 0, 0x18, 0, _generate_acp_header_key(password), payload)._compose_raw_packet()\n\t\n\t\n\t@classmethod\n\tdef compose_rpc_command(cls, flags, password, payload):\n\t\treturn cls(0x00030001, flags, 0, 0x19, 0, _generate_acp_header_key(password), payload)._compose_raw_packet()\n\t\n\t\n\t@classmethod\n\tdef compose_auth_command(cls, flags, payload):\n\t\treturn cls(0x00030001, flags, 0, 0x1a, 0, _generate_acp_header_key(\"\"), payload)._compose_raw_packet()\n\t\n\t\n\t@classmethod\n\tdef compose_feat_command(cls, flags):\n\t\treturn cls(0x00030001, flags, 0, 0x1b, 0, _generate_acp_header_key(\"\"))._compose_raw_packet()\n\t\n\t\n\t@classmethod\n\tdef compose_message_ex(cls, version, flags, unused, command, error_code, password, payload, payload_size):\n\t\treturn cls(version, flags, unused, command, error_code, _generate_acp_header_key(password), payload, payload_size)._compose_raw_packet()\n\t\n\t\n\tdef _compose_raw_packet(self):\n\t\t\"\"\"Compose a request from the client to ACP daemon\n\t\t\n\t\tReturns:\n\t\t\tString containing message to send\n\t\t\n\t\t\"\"\"\n\t\treply = self._compose_header()\n\t\tif self.body:\n\t\t\treply += self.body\n\t\t\n\t\treturn reply\n\t\n\t\n\tdef _compose_header(self):\n\t\t\"\"\"Compose the message header\n\t\t\n\t\tReturns:\n\t\t\tString containing header data\n\t\t\n\t\t\"\"\"\n\t\ttmphdr = self._header_format.pack(self._header_magic,\n\t\t                                  self.version,\n\t\t                                  0,\n\t\t                                  self.body_checksum,\n\t\t                                  self.body_size,\n\t\t                                  self.flags,\n\t\t                                  self.unused,\n\t\t                                  self.command,\n\t\t                                  self.error_code,\n\t\t                                  self.key)\n\t\t\n\t\theader = self._header_format.pack(self._header_magic,\n\t\t                                  self.version,\n\t\t                                  zlib.adler32(tmphdr),\n\t\t                                  self.body_checksum,\n\t\t                                  self.body_size,\n\t\t                                  self.flags,\n\t\t                                  self.unused,\n\t\t                                  self.command,\n\t\t                                  self.error_code,\n\t\t                                  self.key)\n\t\t\n\t\treturn header\n"
  },
  {
    "path": "acp/misc.py",
    "content": "#XXX: this file exists until I think of a better way to do this\n\ndef cast_u32(value):\n\t#XXX: lazy, how do we do this correctly?\n\tif value < -0x80000000 or value > 0x7FFFFFFF:\n\t\traise Exception(\"value outside u32 range\")\n\treturn value & 0xFFFFFFFF\n"
  },
  {
    "path": "acp/property.py",
    "content": "import logging\nimport pprint\nimport struct\n\nfrom .cflbinary import CFLBinaryPListParser\nfrom .exception import ACPPropertyError\n\n\n_acp_properties = [\n\t# Uncomment and fill out relevant fields to add support for a property\n\t# Properties must be in the following format:\n\t# (name, type, description, validation), where\n\t# name (required) is a 4 character string,\n\t# type (required) is a valid property type (str, dec, hex, log, mac, cfb, bin)\n\t# description (required) is a short, one-line description of the property\n\t# validation (optional) is eval()d to verify the input value for setting a property\n\t(\"buil\",\"str\",\"Build string?\",\"\"),\n\t(\"DynS\",\"cfb\",\"DNS\",\"\"),\n\t#(\"cfpf\",\"\",\"\",\"\"),\n\t#(\"cloC\",\"\",\"\",\"\"),\n\t#(\"cloD\",\"\",\"\",\"\"),\n\t#(\"conf\",\"\",\"\",\"\"),\n\t(\"fire\",\"cfb\",\"Firewall???\",\"\"),\n\t#(\"prob\",\"\",\"\",\"\"),\n\t(\"srcv\",\"str\",\"Source Version\",\"\"),\n\t(\"syNm\",\"str\",\"Device name\",\"\"),\n\t#(\"syDN\",\"\",\"\",\"\"),\n\t#(\"syPI\",\"\",\"\",\"\"),\n\t(\"syPW\",\"str\",\"Router administration password\",\"\"),\n\t(\"syPR\",\"str\",\"syPR string???\",\"\"),\n\t(\"syGP\",\"str\",\"Router guest password???\",\"\"),\n\t#(\"syCt\",\"\",\"\",\"\"),\n\t#(\"syLo\",\"\",\"\",\"\"),\n\t(\"syDs\",\"str\",\"System description\",\"\"),\n\t(\"syVs\",\"str\",\"System version\",\"\"),\n\t(\"syVr\",\"str\",\"System version???\",\"\"),\n\t(\"syIn\",\"str\",\"System information???\",\"\"),\n\t(\"syFl\",\"hex\",\"????\",\"\"),\n\t(\"syAM\",\"str\",\"Model Identifier\",\"\"),\n\t(\"syAP\",\"dec\",\"Product ID\",\"\"),\n\t(\"sySN\",\"str\",\"Apple Serial Number\",\"\"),\n\t#(\"ssSN\",\"\",\"\",\"\"),\n\t#(\"sySK\",\"\",\"\",\"\"),\n\t(\"ssSK\",\"str\",\"Apple SKU\",\"\"),\n\t#(\"syRe\",\"\",\"\",\"\"),\n\t(\"syLR\",\"cfb\",\"syLR Blob\",\"\"),\n\t(\"syAR\",\"cfb\",\"syAR Blob\",\"\"),\n\t(\"syUT\",\"dec\",\"System Uptime\",\"\"),\n\t#(\"minV\",\"\",\"\",\"\"),\n\t(\"minS\",\"str\",\"apple-minver\",\"\"),\n\t(\"chip\",\"str\",\"SoC Description\",\"\"),\n\t#(\"card\",\"\",\"\",\"\"),\n\t#(\"memF\",\"\",\"\",\"\"),\n\t#(\"pool\",\"\",\"\",\"\"),\n\t#(\"tmpC\",\"\",\"\",\"\"),\n\t#(\"RPMs\",\"\",\"\",\"\"),\n\t(\"sySI\",\"cfb\",\"System Info Blob?\",\"\"),\n\t#(\"fDCY\",\"\",\"\",\"\"),\n\t(\"TMEn\",\"hex\",\"TMEn???\",\"\"),\n\t(\"CLTM\",\"cfb\",\"CLTM???\",\"\"),\n\t#(\"sPLL\",\"\",\"\",\"\"),\n\t#(\"syTL\",\"\",\"\",\"\"),\n\t#(\"syST\",\"\",\"\",\"\"),\n\t(\"sySt\",\"cfb\",\"System Status\",\"\"),\n\t#(\"syIg\",\"\",\"\",\"\"),\n\t(\"syBL\",\"str\",\"Bootloader vesrsion string\",\"\"),\n\t(\"time\",\"dec\",\"System time\",\"\"),\n\t(\"timz\",\"cfb\",\"Timezone Config Blob\",\"\"),\n\t(\"usrd\",\"cfb\",\"usrd???\",\"\"),\n\t#(\"uuid\",\"\",\"\",\"\"),\n\t#(\"drTY\",\"\",\"\",\"\"),\n\t#(\"sttE\",\"\",\"\",\"\"),\n\t#(\"sttF\",\"\",\"\",\"\"),\n\t#(\"stat\",\"\",\"\",\"\"),\n\t#(\"sRnd\",\"\",\"\",\"\"),\n\t#(\"Accl\",\"\",\"\",\"\"),\n\t(\"dSpn\",\"cfb\",\"Disk spin status?\",\"\"),\n\t(\"syMS\",\"str\",\"MLB Serial Number\",\"\"),\n\t#(\"IGMP\",\"\",\"\",\"\"),\n\t(\"diag\",\"bin\",\"diag???\",\"\"),\n\t#(\"paFR\",\"\",\"\",\"\"),\n\t(\"raNm\",\"str\",\"Radio Name\",\"\"),\n\t#(\"raCl\",\"\",\"\",\"\"),\n\t#(\"raSk\",\"\",\"\",\"\"),\n\t#(\"raWM\",\"\",\"\",\"\"),\n\t#(\"raEA\",\"\",\"\",\"\"),\n\t#(\"raWE\",\"\",\"\",\"\"),\n\t#(\"raCr\",\"\",\"\",\"\"),\n\t#(\"raKT\",\"\",\"\",\"\"),\n\t#(\"raNN\",\"\",\"\",\"\"),\n\t#(\"raGK\",\"\",\"\",\"\"),\n\t#(\"raHW\",\"\",\"\",\"\"),\n\t#(\"raCM\",\"\",\"\",\"\"),\n\t#(\"raRo\",\"\",\"\",\"\"),\n\t#(\"raCA\",\"\",\"\",\"\"),\n\t#(\"raCh\",\"\",\"\",\"\"),\n\t#(\"rCh2\",\"\",\"\",\"\"),\n\t#(\"raWC\",\"\",\"\",\"\"),\n\t#(\"raDe\",\"\",\"\",\"\"),\n\t#(\"raMu\",\"\",\"\",\"\"),\n\t#(\"raLC\",\"\",\"\",\"\"),\n\t#(\"raLF\",\"\",\"\",\"\"),\n\t#(\"ra1C\",\"\",\"\",\"\"),\n\t#(\"raVs\",\"\",\"\",\"\"),\n\t(\"raMA\",\"mac\",\"Radio MAC Address\",\"\"),\n\t#(\"raM2\",\"\",\"\",\"\"),\n\t#(\"raMO\",\"\",\"\",\"\"),\n\t#(\"raLO\",\"\",\"\",\"\"),\n\t#(\"raDS\",\"\",\"\",\"\"),\n\t#(\"raNA\",\"\",\"\",\"\"),\n\t#(\"raWB\",\"\",\"\",\"\"),\n\t#(\"raIS\",\"\",\"\",\"\"),\n\t#(\"raMd\",\"\",\"\",\"\"),\n\t#(\"raPo\",\"\",\"\",\"\"),\n\t#(\"raPx\",\"\",\"\",\"\"),\n\t#(\"raTr\",\"\",\"\",\"\"),\n\t#(\"raDt\",\"\",\"\",\"\"),\n\t#(\"raFC\",\"\",\"\",\"\"),\n\t#(\"raEC\",\"\",\"\",\"\"),\n\t#(\"raMX\",\"\",\"\",\"\"),\n\t#(\"raIE\",\"\",\"\",\"\"),\n\t#(\"raII\",\"\",\"\",\"\"),\n\t#(\"raB0\",\"\",\"\",\"\"),\n\t#(\"raB1\",\"\",\"\",\"\"),\n\t#(\"raB2\",\"\",\"\",\"\"),\n\t#(\"raSt\",\"\",\"\",\"\"),\n\t#(\"APSR\",\"\",\"\",\"\"),\n\t#(\"raTX\",\"\",\"\",\"\"),\n\t#(\"raRX\",\"\",\"\",\"\"),\n\t#(\"raAC\",\"\",\"\",\"\"),\n\t(\"raSL\",\"cfb\",\"Radios list?\",\"\"),\n\t#(\"raMI\",\"\",\"\",\"\"),\n\t#(\"raST\",\"\",\"\",\"\"),\n\t#(\"raDy\",\"\",\"\",\"\"),\n\t#(\"raEV\",\"\",\"\",\"\"),\n\t#(\"rTSN\",\"\",\"\",\"\"),\n\t(\"raSR\",\"cfb\",\"Radio scan results?\",\"\"),\n\t#(\"eaRA\",\"\",\"\",\"\"),\n\t(\"WiFi\",\"cfb\",\"Wifi configuration?\",\"\"),\n\t(\"rCAL\",\"cfb\",\"Radio calibration data?\",\"\"),\n\t#(\"moPN\",\"\",\"\",\"\"),\n\t#(\"moAP\",\"\",\"\",\"\"),\n\t#(\"moUN\",\"\",\"\",\"\"),\n\t#(\"moPW\",\"\",\"\",\"\"),\n\t#(\"moIS\",\"\",\"\",\"\"),\n\t#(\"moLS\",\"\",\"\",\"\"),\n\t#(\"moLI\",\"\",\"\",\"\"),\n\t#(\"moID\",\"\",\"\",\"\"),\n\t#(\"moDT\",\"\",\"\",\"\"),\n\t#(\"moPD\",\"\",\"\",\"\"),\n\t#(\"moAD\",\"\",\"\",\"\"),\n\t#(\"moCC\",\"\",\"\",\"\"),\n\t#(\"moCR\",\"\",\"\",\"\"),\n\t#(\"moCI\",\"\",\"\",\"\"),\n\t#(\"^moM\",\"\",\"\",\"\"),\n\t#(\"moVs\",\"\",\"\",\"\"),\n\t#(\"moMP\",\"\",\"\",\"\"),\n\t#(\"moMF\",\"\",\"\",\"\"),\n\t#(\"moFV\",\"\",\"\",\"\"),\n\t#(\"pdFl\",\"\",\"\",\"\"),\n\t#(\"pdUN\",\"\",\"\",\"\"),\n\t#(\"pdPW\",\"\",\"\",\"\"),\n\t#(\"pdAR\",\"\",\"\",\"\"),\n\t#(\"pdID\",\"\",\"\",\"\"),\n\t#(\"pdMC\",\"\",\"\",\"\"),\n\t#(\"peSN\",\"\",\"\",\"\"),\n\t#(\"peUN\",\"\",\"\",\"\"),\n\t#(\"pePW\",\"\",\"\",\"\"),\n\t#(\"peSC\",\"\",\"\",\"\"),\n\t#(\"peAC\",\"\",\"\",\"\"),\n\t#(\"peID\",\"\",\"\",\"\"),\n\t#(\"peAO\",\"\",\"\",\"\"),\n\t(\"waCV\",\"bin\",\"WAN Config Mode?\",\"\"),\n\t(\"waIn\",\"bin\",\"WAN Interface Mode?\",\"\"),\n\t#(\"waD1\",\"\",\"\",\"\"),\n\t#(\"waD2\",\"\",\"\",\"\"),\n\t#(\"waD3\",\"\",\"\",\"\"),\n\t#(\"waC1\",\"\",\"\",\"\"),\n\t#(\"waC2\",\"\",\"\",\"\"),\n\t#(\"waC3\",\"\",\"\",\"\"),\n\t(\"waIP\",\"bin\",\"WAN IP\",\"\"),\n\t#(\"waSM\",\"\",\"\",\"\"),\n\t(\"waRA\",\"bin\",\"WAN Upstream Gateway IP\",\"\"),\n\t#(\"waDC\",\"\",\"\",\"\"),\n\t#(\"waDS\",\"\",\"\",\"\"),\n\t(\"waMA\",\"mac\",\"WAN MAC Address\",\"\"),\n\t#(\"waMO\",\"\",\"\",\"\"),\n\t#(\"waDN\",\"\",\"\",\"\"),\n\t#(\"waCD\",\"\",\"\",\"\"),\n\t#(\"waIS\",\"\",\"\",\"\"),\n\t#(\"waNM\",\"\",\"\",\"\"),\n\t#(\"waSD\",\"\",\"\",\"\"),\n\t#(\"waFF\",\"\",\"\",\"\"),\n\t#(\"waRO\",\"\",\"\",\"\"),\n\t#(\"waW1\",\"\",\"\",\"\"),\n\t#(\"waW2\",\"\",\"\",\"\"),\n\t#(\"waW3\",\"\",\"\",\"\"),\n\t#(\"waLL\",\"\",\"\",\"\"),\n\t#(\"waUB\",\"\",\"\",\"\"),\n\t(\"waDI\",\"cfb\",\"WAN DHCP Info?\",\"\"),\n\t#(\"laCV\",\"\",\"\",\"\"),\n\t#(\"laIP\",\"\",\"\",\"\"),\n\t#(\"laSM\",\"\",\"\",\"\"),\n\t#(\"laRA\",\"\",\"\",\"\"),\n\t#(\"laDC\",\"\",\"\",\"\"),\n\t#(\"laDS\",\"\",\"\",\"\"),\n\t#(\"laNA\",\"\",\"\",\"\"),\n\t(\"laMA\",\"mac\",\"LAN MAC Address\",\"\"),\n\t#(\"laIS\",\"\",\"\",\"\"),\n\t#(\"laSD\",\"\",\"\",\"\"),\n\t#(\"laIA\",\"\",\"\",\"\"),\n\t#(\"gn6?\",\"\",\"\",\"\"),\n\t#(\"gn6A\",\"\",\"\",\"\"),\n\t#(\"gn6P\",\"\",\"\",\"\"),\n\t#(\"dhFl\",\"\",\"\",\"\"),\n\t#(\"dhBg\",\"\",\"\",\"\"),\n\t#(\"dhEn\",\"\",\"\",\"\"),\n\t#(\"dhSN\",\"\",\"\",\"\"),\n\t#(\"dhRo\",\"\",\"\",\"\"),\n\t#(\"dhLe\",\"\",\"\",\"\"),\n\t#(\"dhMg\",\"\",\"\",\"\"),\n\t#(\"dh95\",\"\",\"\",\"\"),\n\t(\"DRes\",\"cfb\",\"DHCP Reservations\",\"\"),\n\t#(\"dhWA\",\"\",\"\",\"\"),\n\t#(\"dhDS\",\"\",\"\",\"\"),\n\t#(\"dhDB\",\"\",\"\",\"\"),\n\t#(\"dhDE\",\"\",\"\",\"\"),\n\t#(\"dhDL\",\"\",\"\",\"\"),\n\t(\"dhSL\",\"cfb\",\"DHCP Server leases?\",\"\"),\n\t#(\"gnFl\",\"\",\"\",\"\"),\n\t#(\"gnBg\",\"\",\"\",\"\"),\n\t#(\"gnEn\",\"\",\"\",\"\"),\n\t#(\"gnSN\",\"\",\"\",\"\"),\n\t#(\"gnRo\",\"\",\"\",\"\"),\n\t#(\"gnLe\",\"\",\"\",\"\"),\n\t#(\"gnMg\",\"\",\"\",\"\"),\n\t#(\"gn95\",\"\",\"\",\"\"),\n\t#(\"gnDi\",\"\",\"\",\"\"),\n\t#(\"naFl\",\"\",\"\",\"\"),\n\t#(\"naBg\",\"\",\"\",\"\"),\n\t#(\"naEn\",\"\",\"\",\"\"),\n\t#(\"naSN\",\"\",\"\",\"\"),\n\t#(\"naRo\",\"\",\"\",\"\"),\n\t#(\"naAF\",\"\",\"\",\"\"),\n\t#(\"nDMZ\",\"\",\"\",\"\"),\n\t#(\"pmPI\",\"\",\"\",\"\"),\n\t#(\"pmPS\",\"\",\"\",\"\"),\n\t#(\"pmPR\",\"\",\"\",\"\"),\n\t#(\"pmTa\",\"\",\"\",\"\"),\n\t#(\"acEn\",\"\",\"\",\"\"),\n\t#(\"acTa\",\"\",\"\",\"\"),\n\t(\"tACL\",\"cfb\",\"Timed Access Control\",\"\"),\n\t#(\"wdFl\",\"\",\"\",\"\"),\n\t#(\"wdLs\",\"\",\"\",\"\"),\n\t#(\"dWDS\",\"\",\"\",\"\"),\n\t#(\"cWDS\",\"\",\"\",\"\"),\n\t#(\"dwFl\",\"\",\"\",\"\"),\n\t#(\"raFl\",\"\",\"\",\"\"),\n\t#(\"raI1\",\"\",\"\",\"\"),\n\t#(\"raTm\",\"\",\"\",\"\"),\n\t#(\"raAu\",\"\",\"\",\"\"),\n\t#(\"raAc\",\"\",\"\",\"\"),\n\t#(\"raSe\",\"\",\"\",\"\"),\n\t#(\"raRe\",\"\",\"\",\"\"),\n\t#(\"raF2\",\"\",\"\",\"\"),\n\t#(\"raI2\",\"\",\"\",\"\"),\n\t#(\"raT2\",\"\",\"\",\"\"),\n\t#(\"raU2\",\"\",\"\",\"\"),\n\t#(\"raC2\",\"\",\"\",\"\"),\n\t#(\"raS2\",\"\",\"\",\"\"),\n\t#(\"raR2\",\"\",\"\",\"\"),\n\t#(\"raCi\",\"\",\"\",\"\"),\n\t(\"ntSV\",\"str\",\"NTP Server Hostname\",\"\"),\n\t#(\"ntpC\",\"\",\"\",\"\"),\n\t#(\"smtp\",\"\",\"\",\"\"),\n\t#(\"slog\",\"\",\"\",\"\"),\n\t#(\"slgC\",\"\",\"\",\"\"),\n\t#(\"slCl\",\"\",\"\",\"\"),\n\t(\"slvl\",\"dec\",\"System log severity level?\",\"\"),\n\t#(\"slfl\",\"\",\"\",\"\"),\n\t(\"logm\",\"log\",\"System log data\",\"\"),\n\t#(\"snAF\",\"\",\"\",\"\"),\n\t#(\"snLW\",\"\",\"\",\"\"),\n\t#(\"snLL\",\"\",\"\",\"\"),\n\t#(\"snRW\",\"\",\"\",\"\"),\n\t#(\"snWW\",\"\",\"\",\"\"),\n\t#(\"snRL\",\"\",\"\",\"\"),\n\t#(\"snWL\",\"\",\"\",\"\"),\n\t#(\"snCS\",\"\",\"\",\"\"),\n\t#(\"srtA\",\"\",\"\",\"\"),\n\t#(\"srtF\",\"\",\"\",\"\"),\n\t#(\"upsF\",\"\",\"\",\"\"),\n\t#(\"usbF\",\"\",\"\",\"\"),\n\t(\"USBi\",\"cfb\",\"USB Info\",\"\"),\n\t#(\"USBL\",\"\",\"\",\"\"),\n\t#(\"USBR\",\"\",\"\",\"\"),\n\t#(\"USBO\",\"\",\"\",\"\"),\n\t#(\"USBs\",\"\",\"\",\"\"),\n\t#(\"USBo\",\"\",\"\",\"\"),\n\t#(\"USBh\",\"\",\"\",\"\"),\n\t#(\"USBb\",\"\",\"\",\"\"),\n\t#(\"USBn\",\"\",\"\",\"\"),\n\t(\"prni\",\"cfb\",\"Printer Info?\",\"\"),\n\t#(\"prnM\",\"\",\"\",\"\"),\n\t#(\"prnI\",\"\",\"\",\"\"),\n\t#(\"prnR\",\"\",\"\",\"\"),\n\t#(\"RUdv\",\"\",\"\",\"\"),\n\t#(\"RUfl\",\"\",\"\",\"\"),\n\t(\"MaSt\",\"cfb\",\"USB Mass Storage Info\",\"\"),\n\t#(\"SMBw\",\"\",\"\",\"\"),\n\t#(\"SMBs\",\"\",\"\",\"\"),\n\t#(\"fssp\",\"\",\"\",\"\"),\n\t#(\"diSD\",\"\",\"\",\"\"),\n\t#(\"diCS\",\"\",\"\",\"\"),\n\t#(\"deSt\",\"\",\"\",\"\"),\n\t#(\"daSt\",\"\",\"\",\"\"),\n\t#(\"dmSt\",\"\",\"\",\"\"),\n\t#(\"adNm\",\"\",\"\",\"\"),\n\t#(\"adBD\",\"\",\"\",\"\"),\n\t#(\"adAD\",\"\",\"\",\"\"),\n\t#(\"adHU\",\"\",\"\",\"\"),\n\t#(\"IDNm\",\"\",\"\",\"\"),\n\t(\"seFl\",\"bin\",\"????\",\"\"), #????\n\t#(\"nvVs\",\"\",\"\",\"\"),\n\t#(\"dbRC\",\"\",\"\",\"\"),\n\t(\"dbug\",\"hex\",\"Debug flags\",\"0 <= value <= 0xFFFFFFFF\"),\n\t#(\"dlvl\",\"\",\"\",\"\"),\n\t#(\"dcmd\",\"\",\"\",\"\"),\n\t#(\"dsps\",\"\",\"\",\"\"),\n\t#(\"logC\",\"\",\"\",\"\"),\n\t#(\"cver\",\"\",\"\",\"\"),\n\t(\"ctim\",\"hex\",\"ctim???\",\"\"),\n\t#(\"svMd\",\"\",\"\",\"\"),\n\t#(\"serM\",\"\",\"\",\"\"),\n\t#(\"serT\",\"\",\"\",\"\"),\n\t#(\"emNo\",\"\",\"\",\"\"),\n\t#(\"effF\",\"\",\"\",\"\"),\n\t#(\"LLnk\",\"\",\"\",\"\"),\n\t#(\"WLnk\",\"\",\"\",\"\"),\n\t#(\"PHYS\",\"\",\"\",\"\"),\n\t#(\"PHYN\",\"\",\"\",\"\"),\n\t#(\"Rnfo\",\"\",\"\",\"\"),\n\t#(\"evtL\",\"\",\"\",\"\"),\n\t#(\"isAC\",\"\",\"\",\"\"),\n\t#(\"Adet\",\"\",\"\",\"\"),\n\t(\"Prof\",\"cfb\",\"Restore Profile Blob\",\"\"),\n\t#(\"maAl\",\"\",\"\",\"\"),\n\t#(\"maPr\",\"\",\"\",\"\"),\n\t#(\"leAc\",\"\",\"\",\"\"),\n\t#(\"APID\",\"\",\"\",\"\"),\n\t#(\"AAU \",\"\",\"\",\"\"),\n\t(\"lcVs\",\"str\",\"lcVs Version String?\",\"\"),\n\t#(\"lcVr\",\"\",\"\",\"\"),\n\t#(\"lcmV\",\"\",\"\",\"\"),\n\t#(\"lcMV\",\"\",\"\",\"\"),\n\t#(\"iMTU\",\"\",\"\",\"\"),\n\t(\"wsci\",\"cfb\",\"wsci Blob\",\"\"),\n\t#(\"FlSu\",\"\",\"\",\"\"),\n\t(\"OTPR\",\"hex\",\"machdep.otpval\",\"\"),\n\t(\"acRB\",\"dec\",\"Reboot device flag\",\"value == 0\"),\n\t(\"acRI\",\"dec\",\"Reload services??\",\"value == 0\"),\n\t#(\"acPC\",\"\",\"\",\"\"),\n\t#(\"acDD\",\"\",\"\",\"\"),\n\t#(\"acPD\",\"\",\"\",\"\"),\n\t#(\"acPG\",\"\",\"\",\"\"),\n\t#(\"acDS\",\"\",\"\",\"\"),\n\t#(\"acFN\",\"\",\"\",\"\"),\n\t#(\"acRP\",\"\",\"\",\"\"),\n\t(\"acRN\",\"dec\",\"Resets something... (?)\",\"value == 0\"),\n\t(\"acRF\",\"dec\",\"Reset to factory defaults\",\"value == 0\"),\n\t#(\"MdmH\",\"\",\"\",\"\"),\n\t#(\"dirf\",\"\",\"\",\"\"),\n\t#(\"Afrc\",\"\",\"\",\"\"),\n\t#(\"lebl\",\"\",\"\",\"\"),\n\t#(\"lebs\",\"\",\"\",\"\"),\n\t(\"LEDc\",\"dec\",\"LED color/pattern\",\"0 <= value <= 3\"),\n\t#(\"acEf\",\"\",\"\",\"\"),\n\t#(\"invr\",\"\",\"\",\"\"),\n\t#(\"FLSH\",\"\",\"\",\"\"),\n\t#(\"acPL\",\"\",\"\",\"\"),\n\t#(\"rReg\",\"\",\"\",\"\"),\n\t#(\"dReg\",\"\",\"\",\"\"),\n\t(\"GPIs\",\"bin\",\"GPIOs values\",\"len(value) == 8\"),\n\t#(\"play\",\"\",\"\",\"\"),\n\t#(\"paus\",\"\",\"\",\"\"),\n\t#(\"ffwd\",\"\",\"\",\"\"),\n\t#(\"rwnd\",\"\",\"\",\"\"),\n\t#(\"itun\",\"\",\"\",\"\"),\n\t#(\"plls\",\"\",\"\",\"\"),\n\t#(\"User\",\"\",\"\",\"\"),\n\t#(\"Pass\",\"\",\"\",\"\"),\n\t#(\"itIP\",\"\",\"\",\"\"),\n\t#(\"itpt\",\"\",\"\",\"\"),\n\t#(\"daap\",\"\",\"\",\"\"),\n\t#(\"song\",\"\",\"\",\"\"),\n\t#(\"arti\",\"\",\"\",\"\"),\n\t#(\"albm\",\"\",\"\",\"\"),\n\t#(\"volm\",\"\",\"\",\"\"),\n\t#(\"rvol\",\"\",\"\",\"\"),\n\t#(\"Tcnt\",\"\",\"\",\"\"),\n\t#(\"Bcnt\",\"\",\"\",\"\"),\n\t#(\"shfl\",\"\",\"\",\"\"),\n\t#(\"rept\",\"\",\"\",\"\"),\n\t#(\"auPr\",\"\",\"\",\"\"),\n\t#(\"auJD\",\"\",\"\",\"\"),\n\t#(\"auNN\",\"\",\"\",\"\"),\n\t#(\"auNP\",\"\",\"\",\"\"),\n\t#(\"aFrq\",\"\",\"\",\"\"),\n\t#(\"aChn\",\"\",\"\",\"\"),\n\t#(\"aLvl\",\"\",\"\",\"\"),\n\t#(\"aPat\",\"\",\"\",\"\"),\n\t#(\"aSta\",\"\",\"\",\"\"),\n\t#(\"aStp\",\"\",\"\",\"\"),\n\t#(\"auCC\",\"\",\"\",\"\"),\n\t#(\"acmp\",\"\",\"\",\"\"),\n\t#(\"aenc\",\"\",\"\",\"\"),\n\t#(\"anBf\",\"\",\"\",\"\"),\n\t#(\"aWan\",\"\",\"\",\"\"),\n\t#(\"auRR\",\"\",\"\",\"\"),\n\t#(\"auMt\",\"\",\"\",\"\"),\n\t#(\"aDCP\",\"\",\"\",\"\"),\n\t#(\"DCPc\",\"\",\"\",\"\"),\n\t#(\"DACP\",\"\",\"\",\"\"),\n\t#(\"DCPi\",\"\",\"\",\"\"),\n\t#(\"auSl\",\"\",\"\",\"\"),\n\t#(\"auFl\",\"\",\"\",\"\"),\n\t(\"fe01\",\"hex\",\"????\",\"\"),\n\t(\"feat\",\"str\",\"Supported features?\",\"\"),\n\t(\"prop\",\"str\",\"Valid acp properties\",\"\"),\n\t(\"hw01\",\"hex\",\"????\",\"\"),\n\t#(\"fltr\",\"\",\"\",\"\"),\n\t#(\"wdel\",\"\",\"\",\"\"),\n\t#(\"plEB\",\"\",\"\",\"\"),\n\t#(\"rWSC\",\"\",\"\",\"\"),\n\t#(\"uDFS\",\"\",\"\",\"\"),\n\t#(\"dWPA\",\"\",\"\",\"\"),\n\t#(\"dpFF\",\"\",\"\",\"\"),\n\t#(\"duLF\",\"\",\"\",\"\"),\n\t#(\"ieHT\",\"\",\"\",\"\"),\n\t#(\"dwlX\",\"\",\"\",\"\"),\n\t#(\"dd11\",\"\",\"\",\"\"),\n\t#(\"dRdr\",\"\",\"\",\"\"),\n\t#(\"dotD\",\"\",\"\",\"\"),\n\t#(\"dotH\",\"\",\"\",\"\"),\n\t#(\"dPwr\",\"\",\"\",\"\"),\n\t#(\"wlBR\",\"\",\"\",\"\"),\n\t#(\"iTIM\",\"\",\"\",\"\"),\n\t#(\"idAG\",\"\",\"\",\"\"),\n\t#(\"mvFL\",\"\",\"\",\"\"),\n\t#(\"mvFM\",\"\",\"\",\"\"),\n\t#(\"dPPP\",\"\",\"\",\"\"),\n\t#(\"!mta\",\"\",\"\",\"\"),\n\t#(\"minR\",\"\",\"\",\"\"),\n\t#(\"SpTr\",\"\",\"\",\"\"),\n\t#(\"dRBT\",\"\",\"\",\"\"),\n\t#(\"dRIR\",\"\",\"\",\"\"),\n\t(\"pECC\",\"cfb\",\"PCIe ECC Blob?\",\"\"),\n\t#(\"fxEB\",\"\",\"\",\"\"),\n\t#(\"fxID\",\"\",\"\",\"\"),\n\t#(\"fuup\",\"\",\"\",\"\"),\n\t#(\"fust\",\"\",\"\",\"\"),\n\t#(\"fuca\",\"\",\"\",\"\"),\n\t(\"fugp\",\"str\",\"Firmware upgrade progress\",\"\"),\n\t(\"cks0\",\"hex\",\"Bootloader Flash Checksum\",\"\"),\n\t(\"cks1\",\"hex\",\"Primary Flash Checksum\",\"\"),\n\t(\"cks2\",\"hex\",\"Secondary Flash Checksum\",\"\"),\n\t#(\"ddBg\",\"\",\"\",\"\"),\n\t#(\"ddEn\",\"\",\"\",\"\"),\n\t#(\"ddIn\",\"\",\"\",\"\"),\n\t#(\"ddSm\",\"\",\"\",\"\"),\n\t#(\"ddEC\",\"\",\"\",\"\"),\n\t#(\"ddFE\",\"\",\"\",\"\"),\n\t#(\"ddSR\",\"\",\"\",\"\"),\n\t#(\"6cfg\",\"\",\"\",\"\"),\n\t#(\"6aut\",\"\",\"\",\"\"),\n\t#(\"6Qpd\",\"\",\"\",\"\"),\n\t#(\"6Wad\",\"\",\"\",\"\"),\n\t#(\"6Wfx\",\"\",\"\",\"\"),\n\t#(\"6Wgw\",\"\",\"\",\"\"),\n\t#(\"6Wte\",\"\",\"\",\"\"),\n\t#(\"6Lfw\",\"\",\"\",\"\"),\n\t#(\"6Lad\",\"\",\"\",\"\"),\n\t#(\"6Lfx\",\"\",\"\",\"\"),\n\t#(\"6sfw\",\"\",\"\",\"\"),\n\t#(\"6pmp\",\"\",\"\",\"\"),\n\t#(\"6trd\",\"\",\"\",\"\"),\n\t#(\"6sec\",\"\",\"\",\"\"),\n\t#(\"6fwl\",\"\",\"\",\"\"),\n\t#(\"6NS1\",\"\",\"\",\"\"),\n\t#(\"6NS2\",\"\",\"\",\"\"),\n\t#(\"6NS3\",\"\",\"\",\"\"),\n\t#(\"6ahr\",\"\",\"\",\"\"),\n\t#(\"6dhs\",\"\",\"\",\"\"),\n\t#(\"6dso\",\"\",\"\",\"\"),\n\t#(\"6PDa\",\"\",\"\",\"\"),\n\t#(\"6PDl\",\"\",\"\",\"\"),\n\t#(\"6vlt\",\"\",\"\",\"\"),\n\t#(\"6plt\",\"\",\"\",\"\"),\n\t#(\"6CWa\",\"\",\"\",\"\"),\n\t#(\"6CWp\",\"\",\"\",\"\"),\n\t#(\"6CWg\",\"\",\"\",\"\"),\n\t#(\"6CLa\",\"\",\"\",\"\"),\n\t#(\"6NSa\",\"\",\"\",\"\"),\n\t#(\"6NSb\",\"\",\"\",\"\"),\n\t#(\"6NSc\",\"\",\"\",\"\"),\n\t#(\"6CPa\",\"\",\"\",\"\"),\n\t#(\"6CPl\",\"\",\"\",\"\"),\n\t#(\"6!at\",\"\",\"\",\"\"),\n\t(\"rteI\",\"cfb\",\"rteI Blob\",\"\"),\n\t#(\"PCLI\",\"\",\"\",\"\"),\n\t#(\"dxEM\",\"\",\"\",\"\"),\n\t#(\"dxID\",\"\",\"\",\"\"),\n\t#(\"dxAI\",\"\",\"\",\"\"),\n\t#(\"dxIP\",\"\",\"\",\"\"),\n\t#(\"dxOA\",\"\",\"\",\"\"),\n\t#(\"dxIA\",\"\",\"\",\"\"),\n\t#(\"dxC1\",\"\",\"\",\"\"),\n\t#(\"dxP1\",\"\",\"\",\"\"),\n\t#(\"dxC2\",\"\",\"\",\"\"),\n\t#(\"dxP2\",\"\",\"\",\"\"),\n\t#(\"bjFl\",\"\",\"\",\"\"),\n\t#(\"bjSd\",\"\",\"\",\"\"),\n\t#(\"bjSM\",\"\",\"\",\"\"),\n\t#(\"wbEn\",\"\",\"\",\"\"),\n\t#(\"wbHN\",\"\",\"\",\"\"),\n\t#(\"wbHU\",\"\",\"\",\"\"),\n\t#(\"wbHP\",\"\",\"\",\"\"),\n\t#(\"wbRD\",\"\",\"\",\"\"),\n\t#(\"wbRU\",\"\",\"\",\"\"),\n\t#(\"wbRP\",\"\",\"\",\"\"),\n\t#(\"wbAC\",\"\",\"\",\"\"),\n\t#(\"dMac\",\"\",\"\",\"\"),\n\t#(\"iCld\",\"\",\"\",\"\"),\n\t#(\"iCLH\",\"\",\"\",\"\"),\n\t#(\"iCLB\",\"\",\"\",\"\"),\n\t#(\"SUEn\",\"\",\"\",\"\"),\n\t#(\"SUAI\",\"\",\"\",\"\"),\n\t#(\"SUFq\",\"\",\"\",\"\"),\n\t#(\"SUSv\",\"\",\"\",\"\"),\n\t#(\"suPR\",\"\",\"\",\"\"),\n\t#(\"msEn\",\"\",\"\",\"\"),\n\t#(\"trCo\",\"\",\"\",\"\"),\n\t#(\"EZCF\",\"\",\"\",\"\"),\n\t#(\"ezcf\",\"\",\"\",\"\"),\n\t#(\"gVID\",\"\",\"\",\"\"),\n\t#(\"wcfg\",\"\",\"\",\"\"),\n\t#(\"awce\",\"\",\"\",\"\"),\n\t#(\"wcgu\",\"\",\"\",\"\"),\n\t#(\"wcgs\",\"\",\"\",\"\"),\n\t#(\"awcc\",\"\",\"\",\"\"),\n\t]\n\ndef _generate_acp_property_dict():\t\n\tprops = {}\n\tfor (name, type, description, validation) in _acp_properties:\n\t\t# basic validation of tuples\n\t\tassert len(name) == 4, \"bad name in _acp_properties list: {0}\".format(name)\n\t\tassert type in [\"str\", \"dec\", \"hex\", \"log\", \"mac\", \"cfb\", \"bin\"], \"bad type in _acp_properties list for name: {0}\".format(name)\n\t\tassert description, \"missing description in _acp_properties list for name: {0}\".format(name)\n\t\tprops[name] = dict(type=type, description=description, validation=validation)\n\treturn props\n\n\nclass ACPPropertyInitValueError(ACPPropertyError):\n\tpass\n\n\nclass ACPProperty(object):\n\t_acpprop = _generate_acp_property_dict()\n\t\n\t_element_header_format = struct.Struct(\"!4s2I\")\n\telement_header_size = _element_header_format.size\n\t\n\t\n\tdef __init__(self, name=None, value=None):\n\t\t# handle \"null\" property packed name and value first\n\t\tif name == \"\\x00\\x00\\x00\\x00\" and value == \"\\x00\\x00\\x00\\x00\":\n\t\t\tname = None\n\t\t\tvalue = None\n\t\t\n\t\tif name and name not in self.get_supported_property_names():\n\t\t\traise ACPPropertyError(\"invalid property name passed to initializer: {0}\".format(name))\n\t\t\n\t\tif value is not None:\n\t\t\t# accept value as packed binary string or Python type\n\t\t\tprop_type = self.get_property_info_string(name, \"type\")\n\t\t\t_init_handler_name = \"_init_{0}\".format(prop_type)\n\t\t\tassert hasattr(self, _init_handler_name), \"missing init handler for \\\"{0}\\\" property type\".format(prop_type)\n\t\t\t_init_handler = getattr(self, _init_handler_name)\n\t\t\t\n\t\t\tlogging.debug(\"old value: {0!r} type: {1}\".format(value, type(value)))\n\t\t\ttry:\n\t\t\t\tvalue = _init_handler(value)\n\t\t\texcept ACPPropertyInitValueError as e:\n\t\t\t\traise ACPPropertyError(\"{0!s} provided for \\\"{1}\\\" property type: {2!r}\".format(e, prop_type, value))\n\t\t\tlogging.debug(\"new value: {0!r} type: {1}\".format(value, type(value)))\n\t\t\t\n\t\t\t#XXX: this is still really hacky, should probably do something with anonymous functions or introspection\n\t\t\tvalidation_expr = self.get_property_info_string(name, \"validation\")\n\t\t\tif validation_expr and not eval(validation_expr):\n\t\t\t\traise ACPPropertyError(\"invalid value passed to initializer for property \\\"{0}\\\": {1}\".format(name, repr(value)))\n\t\t\n\t\tself.name = name\n\t\tself.value = value\n\t\n\tdef _init_dec(self, value):\n\t\tif   type(value) == int:\n\t\t\treturn value\n\t\telif type(value) == str:\n\t\t\ttry:\n\t\t\t\treturn struct.unpack(\"!I\", value)[0]\n\t\t\texcept:\n\t\t\t\traise ACPPropertyInitValueError(\"invalid packed binary string\")\n\t\telse:\n\t\t\traise ACPPropertyInitValueError(\"invalid built-in type\")\n\t\n\tdef _init_hex(self, value):\n\t\tif   type(value) == int:\n\t\t\treturn value\n\t\telif type(value) == str:\n\t\t\ttry:\n\t\t\t\treturn struct.unpack(\"!I\", value)[0]\n\t\t\texcept:\n\t\t\t\traise ACPPropertyInitValueError(\"invalid packed binary string\")\n\t\telse:\n\t\t\traise ACPPropertyInitValueError(\"invalid built-in type\")\n\t\n\tdef _init_mac(self, value):\n\t\tif type(value) == str:\n\t\t\t# first, try as packed binary value\n\t\t\tif len(value) == 6:\n\t\t\t\treturn value\n\t\t\t# second, attempt to unpack colon delimited value\n\t\t\tmac_bytes = value.split(\":\")\n\t\t\tif len(mac_bytes) == 6:\n\t\t\t\ttry:\n\t\t\t\t\treturn \"\".join(mac_bytes).decode(\"hex\")\n\t\t\t\texcept TypeError:\n\t\t\t\t\traise ACPPropertyInitValueError(\"non-hex digit in value\")\n\t\t\t# fallthrough\n\t\t\traise ACPPropertyInitValueError(\"invalid value\")\n\t\telse:\n\t\t\traise ACPPropertyInitValueError(\"invalid built-in type\")\n\t\n\tdef _init_bin(self, value):\n\t\tif type(value) == str:\n\t\t\treturn value\n\t\telse:\n\t\t\traise ACPPropertyInitValueError(\"invalid built-in type\")\n\t\n\tdef _init_cfb(self, value):\n\t\tif type(value) == str:\n\t\t\treturn value\n\t\telse:\n\t\t\traise ACPPropertyInitValueError(\"invalid built-in type\")\n\t\n\tdef _init_log(self, value):\n\t\tif type(value) == str:\n\t\t\treturn value\n\t\telse:\n\t\t\traise ACPPropertyInitValueError(\"invalid built-in type\")\n\t\n\tdef _init_str(self, value):\n\t\tif type(value) == str:\n\t\t\treturn value\n\t\telse:\n\t\t\traise ACPPropertyInitValueError(\"invalid built-in type\")\n\t\n\t\n\tdef __repr__(self):\n\t\t#XXX: return tuple or dict?\n\t\treturn repr((self.name, self.value))\n\t\n\t\n\t#TODO: make this function return something other than the formatted value of the property...I keep getting confused by its current shittiness\n\tdef __str__(self):\n\t\t#XXX: is this the correct thing to do?\n\t\tif self.name is None or self.value is None:\n\t\t\treturn \"\"\n\t\t\n\t\tprop_type = self.get_property_info_string(self.name, \"type\")\n\t\t_format_handler_name = \"_format_{0}\".format(prop_type)\n\t\tassert hasattr(self, _format_handler_name), \"missing format handler for \\\"{0}\\\" property type\".format(prop_type)\n\t\treturn getattr(self, _format_handler_name)(self.value)\n\t\n\tdef _format_dec(self, value):\n\t\treturn str(value)\n\t\n\tdef _format_hex(self, value):\n\t\treturn hex(value)\n\t\n\tdef _format_mac(self, value):\n\t\tmac_bytes = []\n\t\tfor i in range(6):\n\t\t\tmac_bytes.append(value[i].encode(\"hex\"))\n\t\treturn \"{0}:{1}:{2}:{3}:{4}:{5}\".format(*mac_bytes)\n\t\n\tdef _format_bin(self, value):\n\t\treturn value.encode(\"hex\")\n\t\n\tdef _format_cfb(self, value):\n\t\treturn pprint.pformat(CFLBinaryPListParser.parse(value))\n\t\n\tdef _format_log(self, value):\n\t\ts = \"\"\n\t\tfor line in value.strip(\"\\x00\").split(\"\\x00\"):\n\t\t\ts += \"{0}\\n\".format(line)\n\t\treturn s\n\t\n\tdef _format_str(self, value):\n\t\treturn value\n\t\n\t\n\t@classmethod\n\tdef get_supported_property_names(cls):\n\t\tprops = []\n\t\tfor name in cls._acpprop:\n\t\t\tprops.append(name)\n\t\treturn props\n\t\n\t\n\t@classmethod\n\tdef get_property_info_string(cls, prop_name, key):\n\t\t#XXX: should we do this differently?\n\t\tif prop_name is None:\n\t\t\treturn None\n\t\tif prop_name not in cls._acpprop:\n\t\t\tlogging.error(\"property \\\"{0}\\\" not supported\".format(prop_name))\n\t\t\treturn None\n\t\tprop_info = cls._acpprop[prop_name]\n\t\tif key not in prop_info:\n\t\t\tlogging.error(\"invalid property info key \\\"{0}\\\"\".format(key))\n\t\t\treturn None\n\t\treturn prop_info[key]\n\t\n\t\n\t@classmethod\n\tdef parse_raw_element(cls, data):\n\t\tname, flags, size = cls.parse_raw_element_header(data[:cls.element_header_size])\n\t\t#TODO: handle flags!???\n\t\treturn cls(name, data[cls.element_header_size:])\n\t\n\t\n\t@classmethod\n\tdef parse_raw_element_header(cls, data):\n\t\ttry:\n\t\t\treturn cls._element_header_format.unpack(data)\n\t\texcept struct.error:\n\t\t\traise ACPPropertyError(\"failed to parse property element header\")\n\t\n\t\n\t@classmethod\n\tdef compose_raw_element(cls, flags, property):\n\t\t#TODO: handle flags!???\n\t\t#XXX: handles \"null\" name or value first, but this is currently garbage\n\t\tname = property.name if property.name is not None else \"\\x00\\x00\\x00\\x00\"\n\t\tvalue = property.value if property.value is not None else \"\\x00\\x00\\x00\\x00\"\n\t\tif   type(value) == int:\n\t\t\tst = struct.Struct(\">I\")\n\t\t\t#XXX: this could throw an exception, we need to range check int/hex values to ensure they pack into 32 bits still\n\t\t\treturn cls.compose_raw_element_header(name, flags, st.size) + st.pack(value)\n\t\telif type(value) == str:\n\t\t\treturn cls.compose_raw_element_header(name, flags, len(value)) + value\n\t\telse:\n\t\t\traise ACPPropertyError(\"unhandled property type for raw element composition\")\n\t\n\t\n\t@classmethod\n\tdef compose_raw_element_header(cls, name, flags, size):\n\t\ttry:\n\t\t\treturn cls._element_header_format.pack(name, flags, size)\n\t\texcept struct.error:\n\t\t\traise ACPPropertyError(\"failed to compose property header\")\n"
  },
  {
    "path": "acp/session.py",
    "content": "import logging\nimport socket\n\nfrom .encryption import ACPEncryption\n\n\nACP_SERVER_PORT = 5009\n\nclass _ACPSession(object):\n\tdef __init__(self, target, password):\n\t\t#XXX: how should we make this abstract enough to cover client and server?\n\t\tself.target = target\n\t\tself.password = password\n\t\t\n\t\tself.sock = None\n\t\t\n\t\t#self.encryption_context = None\n\t\tself.encrypt_method = None\n\t\tself.decrypt_method = None\n\t\t\n\t\t#XXX: AppleSRP hax\n\t\t#self.SRP = None\n\t\t#self.state = 0\n\t\n\t\n\tdef connect(self, port=ACP_SERVER_PORT):\n\t\tself.port = port\n\t\tself.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n\t\tlogging.info(\"connecting to host {0}:{1}\".format(self.target, self.port))\n\t\tself.sock.connect((self.target, self.port))\n\t\n\t\n\tdef close(self):\n\t\tif self.sock:\n\t\t\tself.sock.close()\n\t\t\tself.sock = None\n\t\n\t\n\tdef send(self, data):\n\t\tif self.encrypt_method:\n\t\t\tdata = self.encrypt_method(data)\n\t\t\n\t\tif self.sock:\n\t\t\tself.sock.sendall(data)\n\t\t#TODO: else? what if sock is not None but not valid in some other way?\n\t\n\t\n\tdef _recv_size(self, size):\n\t\trecvd_chunks = []\n\t\trecvd_size = 0\n\t\twhile True:\n\t\t\tif recvd_size == size:\n\t\t\t\tbreak\n\t\t\t#XXX: this is broken for server receiving stream headers\n\t\t\tdata = self.sock.recv(size - recvd_size)\n\t\t\tif not data:\n\t\t\t\tbreak\n\t\t\trecvd_chunks.append(data)\n\t\t\trecvd_size += len(data)\n\t\t\n\t\treturn \"\".join(recvd_chunks)\n\t\n\tdef _recv_size_timeout(self, size, timeout):\n\t\t#XXX: blargh\n\t\tself.sock.setblocking(0)\n\t\trecvd_chunks = []\n\t\trecvd_size = 0\n\t\t\n\t\tbegin=time.time()\n\t\twhile True:\n\t\t\tif recvd_size == size:\n\t\t\t\tbreak\n\t\t\tif recvd_chunks and time.time()-begin > timeout:\n\t\t\t\tbreak\n\t\t\tif time.time()-begin > timeout*2:\n\t\t\t\tbreak\n\t\t\t\n\t\t\ttry:\n\t\t\t\t#XXX: this is broken for server receiving stream headers\n\t\t\t\tdata = self.sock.recv(size - recvd_size)\n\t\t\t\tif data:\n\t\t\t\t\trecvd_chunks.append(data)\n\t\t\t\t\trecvd_size += len(data)\n\t\t\t\t\tbegin = time.time()\n\t\t\t\telse:\n\t\t\t\t\ttime.sleep(0.1)\n\t\t\texcept socket.error:\n\t\t\t\tpass\n\t\t\n\t\t#XXX: should non-blocking just be the default?\n\t\tself.sock.setblocking(1)\n\t\treturn \"\".join(recvd_chunks)\n\t\n\n\tdef recv(self, size, timeout=0):\n\t\tif not self.sock:\n\t\t\t#XXX: do nothing? throw an exception?\n\t\t\treturn \"\"\n\t\t\n\t\tdata = \"\"\n\t\tif timeout:\n\t\t\tdata = self._recv_size_timeout(size, timeout)\n\t\telse:\n\t\t\tdata = self._recv_size(size)\n\t\t\n\t\tif self.decrypt_method:\n\t\t\tdata = self.decrypt_method(data)\n\t\t\n\t\treturn data\n\n\nclass ACPClientSession(_ACPSession):\n\tdef enable_encryption(self, key, client_iv, server_iv):\n\t\tself.encryption_context = ACPEncryption(key, client_iv, server_iv)\n\t\t\n\t\tself.encrypt_method = encryption_context.client_encrypt\n\t\tself.decrypt_method = encryption_context.server_decrypt\n\n\nclass ACPServerSession(_ACPSession):\n\tdef enable_encryption(self, key, client_iv, server_iv):\n\t\tself.encryption_context = ACPEncryption(key, client_iv, server_iv)\n\t\t\n\t\tself.encrypt_method = self.encryption_context.server_encrypt\n\t\tself.decrypt_method = self.encryption_context.client_decrypt\n\n\n"
  },
  {
    "path": "setup.py",
    "content": "from setuptools import setup\n\nsetup(\n\tname=\"acp\",\n\tversion=\"1.0\",\n\tdescription=\"AirPyrt Tools\",\n\tauthor=\"Vince Cali\",\n\tauthor_email=\"0x56.0x69.0x6e.0x63.0x65@gmail.com\",\n\tpackages=[\"acp\"],\n\tentry_points = {\n\t\t\"console_scripts\": [\"acp=acp.cli:main\"],\n\t\t},\n\tinstall_requires=[\n\t\t\"pycrypto\",\n\t\t]\n\t)\n"
  }
]