[
  {
    "path": ".gitignore",
    "content": "*.sw?\n*.pyc\ntest.*\nconfig*.json\n!config.dev.json\n*.odg\n"
  },
  {
    "path": "README.md",
    "content": "Fyuneru v1.1\n============\n\n\n**向下拉动，见简体中文版本。**\n\n**Fyuneru** lets you set up a server and a client computer within a virtual\nethernet, whose data frames over the virtual network cable are proxified\nparallel via imagnative a lot of protocols. It also provides basical\nencryption, which makes analyzing the traffic more difficult.\n\nThis software is licensed under GPLv2.\n\n\n\n## Principle\n\nFyuneru ships with 2 parts: a core, and several proxies.\n\nThe **core** behaves on the server and client computer in an identical manner.\nIt first sets up the virtual network interface using Linux's TUN device and\ndeals with intercepting IP frames sending to and coming from it. When these\nframes are leaving our computer, they are encrypted to ensure the \nconfidentiality and integrity.\n\nThe core communicates with serveral proxies using UNIX Sockets in UDP\nDatagrams, which are also managed by Fyuneru. The proxies are choosen randomly\nto receive encrypted packets from the core. Then, how the proxies send these\npackets, varies. \n\nCurrently we have officially written 2 proxies by using Shadowsocks and XMPP\nprotocol. Shadowsocks sends UDP packets using an encrypted connection directly\nto a server with little protocol footprint, and XMPP sends our packets using\nTCP via up to 2 intermediate servers. You may configure Fyuneru to initiate\nseveral such proxies, making a lot of proxy paths.\n\nNotice that the core on both server and client behaves the same: it chooses for\nevery IP frame a random proxy, therefore for a given connection or session, the\nactual IP frame of request and response travels highly likely over different\nroutes with different protocols and even different IPs(in case of XMPP, etc).\nThis schema should confuse an observer, e.g. a firewall.\n\n\n\n## Installation\n\n### System Requirements\n\n1. Operating System: currently tested over Fedora 21 and Ubuntu 14.04;\n1. Installed Python2 environment;\n1. Dependencies installed, see below.\n\n### Dependencies\n\n1. `salsa20`, a python module providing encryption for our program. Use\n   `sudo pip install salsa20` to get this.\n2. `xmpppy`, if you want to use XMPP proxies. I have forked an library on\n   github at <https://github.com/sogisha/xmpppy>, follow the instructions\n   to get it installed.\n3. `shadowsocks-libev`, you are likely to compile it on your self. I have also\n   forked one at <https://github.com/sogisha/shadowsocks-libev>, follow the\n   instructions to compile it and install.\n\n## Usage\n\n### Add user and group for Fyuneru\n\nYou have to set up your system with an user and a group, in which Fyuneru\nwill run. Recommended is `nobody` for both.\n\n### Configuration\n\nInstall Fyuneru on both your server and your client. Before running, create a\nfile named `config.json` and place it in the same folder as the `run_as.py`.\nBoth server and client needs such a file, make sure they're **identical**.\n\nAn example of `config.json` is below. Read instructions below to get\nexplanations!\n\n```\n{\n    \"version\": \"1.1\",\n    \"core\": {\n        \"server\": {\n            \"ip\": \"10.1.0.1\"\n        },\n        \"client\": {\n            \"ip\": \"10.1.0.2\"\n        },\n        \"user\": {\n            \"uidname\": \"nobody\",\n            \"gidname\": \"nobody\"\n        },\n        \"key\": \"DEVELOPMENT ONLY\"\n    },\n    \"proxies\": {\n        \"proxy-ss-01\": {\n            \"type\": \"shadowsocks\",\n            \"server\": {\n                \"bin\": \"/usr/local/bin/ss-server\",\n                \"ip\": \"<SERVER-IP>\",\n                \"port\": 31000,\n                \"forward-to\": 10081\n            },\n            \"client\": {\n                \"bin\": \"/usr/local/bin/ss-tunnel\",\n                \"port\": 10080,\n                \"proxy\": {\n                    \"ip\": \"<PROXY-IP>\",\n                    \"port\": 31000\n                }\n            }\n        },\n        \"proxy-xmpp-01\": {\n            \"type\": \"xmpp\",\n            \"server\": {\n                \"jid\": \"jid1@test.com\",\n                \"password\": \"jid1_password\"\n            },\n            \"client\": {\n                \"jid\": \"jid2@example.com\",\n                \"password\": \"jid2_password\"\n            }\n        }\n    }\n}\n```\n\n#### Section `core`\n\n1. Set `core.server.ip` and `core.client.ip` as the desired virtual IP\n   addresses for both computers.\n1. `core.key` must be random and unique for your own security.\n1. `core.user.uidname` and `core.user.gidname` are UID/GID allocated to\n   Fyuneru. After setting up necessary network devices, Fyuneru will jump\n   to this UID/GID and give up root privileges.\n\n#### Section `proxies`\n\nTo configure using a specific type of proxy, specify parameters in `proxies`\nsection.\n\nYou have to give each proxy a name. `proxy-ss-01` or `proxy-xmpp-01` are\nexamples. They are going to appear in the log output.\n\n##### To add a Shadowsocks proxy\n\nYou have to write the section of a proxy like this:\n\n```\n\"<PROXY-NAME>\": {\n    \"type\": \"shadowsocks\",\n    \"server\": {\n        \"bin\": \"/usr/local/bin/ss-server\",\n        \"ip\": \"<SERVER-IP>\",\n        \"port\": 31000,\n        \"forward-to\": 10081\n    },\n    \"client\": {\n        \"bin\": \"/usr/local/bin/ss-tunnel\",\n        \"port\": 10080,\n        \"proxy\": {\n            \"ip\": \"<PROXY-IP>\",\n            \"port\": 31000\n        }\n    }\n}\n```\n\n1. `type` must be `shadowsocks` to tell the program start a Shadowsocks proxy.\n1. `server.bin` points to the `ss-server` binary, and `client.bin` to the\n   `ss-tunnel` binary.\n2. `server.ip` and `server.port` are IP and port, on which the server should\n   listen in Internet.\n3. `server.forward-to` is the port used by the server-side adapter of Fyuneru.\n   Choose it as you like. Similarily is `client.port` the port used by the\n   client-side adapter of Fyuneru.\n4. `client.proxy` is optional. If you want to ask the client to connect to a\n   port-forwarded server, not directly to the server port, use this. Otherwise\n   we'll just connect to `server.ip`:`server.port` directly.\n\n##### To add a XMPP proxy\n\n```\n\"<PROXY-NAME>\": {\n    \"type\": \"xmpp\",\n    \"server\": {\n        \"jid\": \"jid1@test.com\",\n        \"password\": \"jid1_password\"\n    },\n    \"client\": {\n        \"jid\": \"jid2@example.com\",\n        \"password\": \"jid2_password\"\n    }\n}\n```\n\n1. `type` must be `xmpp` to tell the program start a XMPP proxy.\n2. Both server and client requires the same format, providing a `jid` and\n   a `password`. You have to apply for 2 XMPP accounts(they don't have to be\n   from the same service provider, but the client account's provider should be\n   reachable from your Internet environment).\n\n### Run\n\nFind the `run_as.py`, run `python run_as.py s` for setting up a server, and\n`python run_as.py c` for setting up the client. You need root priviledge to\ndo this.\n\nAdd `--debug` after `run_as.py` will enable the debug mode. The IP frames will\nbe dumped to the console.\n\n---\n\nFyuneru\n=======\n\n**Scroll up for English version.**\n\n**Fyuneru**允许您将服务器和客户端计算机用虚拟的以太网连接。\n在虚拟的网线上传递的数据帧实际借助可以想象的一系列协议传送。\n它同时提供基本的加密，使得分析流量更加困难。\n\n本软件在GPLv2协议下进行许可。\n\n\n\n\n## 原理\n\nFyuneru包含两个部分：核心，和一系列代理。\n\n**核心**在服务器和客户端上的行为是相同的。\n它首先使用Linux的TUN设备建立一个虚拟网卡，然后收发这个网卡上的IP数据帧。\n这些数据帧离开我们的计算机之前会被加密，以便保证它们的机密性和完整性。\n\n核心使用Unix Socket和各个代理之间使用UDP数据包通信。这些代理也是Fyuneru管理的。\n核心随机地向代理发送数据包，之后，代理用自己的不同方式将这些数据包发到服务器。\n\n当前我们提供2种代理：利用Shadowsocks和XMPP协议的。\nShadowsocks使用一种难以分析特征的协议向服务器直接发出UDP数据包，\nXMPP将数据包通过TCP连接传递，但利用了最多2个中间服务器。\n您可以配置Fyuneru启动多个这样的代理进程，以便设定多个代理路径。\n\n注意到，服务器和客户端的核心有相同的行为：对每个IP数据帧，它都随机选择一个代理。\n即使是对于同样的连接会话，实际上的IP数据帧也是通过不同的路径、不同的协议甚至不同的IP传递的（例如XMPP协议）。\n这种方式可以扰乱例如防火墙这样的观察者。\n\n\n\n## 安装\n\n### 系统需求\n\n1. 操作系统：当前在Fedora 21和Ubuntu 14.04上进行了测试。\n1. 需要有Python2的环境。\n1. 需要安装如下所述的依赖包。\n\n### 依赖包\n\n1. `salsa20`, 为我们的程序提供加密的Python模块。使用如下命令安装：\n   `sudo pip install salsa20`\n2. `xmpppy`, 如果要使用XMPP代理的话。可以从我在github上fork的地址找到：\n   <https://github.com/sogisha/xmpppy>，按照上面的指示安装。\n3. `shadowsocks-libev`, 您可能需要自己编译安装。我在github上也fork了一份：\n   <https://github.com/sogisha/shadowsocks-libev>，同样按照上面的指示编译安装。\n\n\n\n## 用法 \n\n### 添加Fyuneru使用的用户和组\n\n您需要在系统中添加一个用户和一个组，Fyuneru将以它们的身份运行。\n推荐用`nobody`作为用户和组。\n\n### 配置\n\n您需要在服务器和客户端上都安装Fyuneru。在运行前，创建一个`config.json`文件，\n将其放在和`run_as.py`同样的目录下。\n服务器和客户端都需要这样一个文件，且请确认它们完全一致。\n\n`config.json`的内容类似如下，请根据后文的指示具体配置。\n\n```\n{\n    \"version\": \"1.1\",\n    \"core\": {\n        \"server\": {\n            \"ip\": \"10.1.0.1\"\n        },\n        \"client\": {\n            \"ip\": \"10.1.0.2\"\n        },\n        \"user\": {\n            \"uidname\": \"nobody\",\n            \"gidname\": \"nobody\"\n        },\n        \"key\": \"DEVELOPMENT ONLY\"\n    },\n    \"proxies\": {\n        \"proxy-ss-01\": {\n            \"type\": \"shadowsocks\",\n            \"server\": {\n                \"bin\": \"/usr/local/bin/ss-server\",\n                \"ip\": \"<SERVER-IP>\",\n                \"port\": 31000,\n                \"forward-to\": 10081\n            },\n            \"client\": {\n                \"bin\": \"/usr/local/bin/ss-tunnel\",\n                \"port\": 10080,\n                \"proxy\": {\n                    \"ip\": \"<PROXY-IP>\",\n                    \"port\": 31000\n                }\n            }\n        },\n        \"proxy-xmpp-01\": {\n            \"type\": \"xmpp\",\n            \"server\": {\n                \"jid\": \"jid1@test.com\",\n                \"password\": \"jid1_password\"\n            },\n            \"client\": {\n                \"jid\": \"jid2@example.com\",\n                \"password\": \"jid2_password\"\n            }\n        }\n    }\n}\n```\n\n#### `core`部分\n\n1. 将`core.server.ip`和`core.client.ip`设定为服务器和客户端的虚拟IP地址。\n1. `core.key`必须是一个随机的密钥，这是为了您的安全着想。\n1. `core.user.uidname`和`core.user.gidname`是分配给Fyuneru使用的UID/GID。\n   在设定了虚拟网卡等设备后，Fyuneru将会以这个身份运行，放弃root权限。\n\n#### `proxies`部分\n\n为了配置某个代理，需要在`proxies`部分中增加对应的内容。\n\n您需要给每个代理取一个名字，例如示例中的`proxy-ss-01`或`proxy-xmpp-01`。\n它们会出现在日志中。\n\n##### 要添加一个Shadowsocks代理\n\n您需要将代理部分的配置写成类似如下的形式：\n\n```\n\"<PROXY-NAME>\": {\n    \"type\": \"shadowsocks\",\n    \"server\": {\n        \"bin\": \"/usr/local/bin/ss-server\",\n        \"ip\": \"<SERVER-IP>\",\n        \"port\": 31000,\n        \"forward-to\": 10081\n    },\n    \"client\": {\n        \"bin\": \"/usr/local/bin/ss-tunnel\",\n        \"port\": 10080,\n        \"proxy\": {\n            \"ip\": \"<PROXY-IP>\",\n            \"port\": 31000\n        }\n    }\n}\n```\n\n1. `type`必须是`shadowsocks`，这样程序就会启动一个Shadowsocks代理。\n1. `server.bin`指向`ss-server`这个程序的二进制文件，`client.bin`指向`ss-tunnel`的二进制文件。\n2. `server.ip`和`server.port`是服务器在互联网上监听所用的IP地址和端口号。\n3. `server.forward-to`是服务器端Fyuneru的代理程序所用的内部端口号。\n   按照您想要的数字选择。\n   同样地，`client.port`是客户端Fyuneru代理程序所用的端口号。\n4. `client.proxy`是可选的。如果您为Shadowsocks设定了一个端口转发，\n   可以用这个方式让客户端连接到被转发的IP地址和端口上。\n   如果您不指定，将会使用由`server.ip`:`server.port`确定的目标地址。\n\n##### 要添加一个XMPP代理\n\n```\n\"<PROXY-NAME>\": {\n    \"type\": \"xmpp\",\n    \"server\": {\n        \"jid\": \"jid1@test.com\",\n        \"password\": \"jid1_password\"\n    },\n    \"client\": {\n        \"jid\": \"jid2@example.com\",\n        \"password\": \"jid2_password\"\n    }\n}\n```\n\n1. `type`必须是`xmpp`，这样程序会启动一个XMPP代理。\n2. 服务器和客户端都需要同样形式的配置：`jid`和`password`。\n   您需要申请2个XMPP帐号（他们不一定来自同一个服务提供商，\n   但客户端所用的帐号所在的服务器应当能在您的互联网环境中可以直接访问）。\n\n### 运行\n\n找到`run_as.py`，用命令`python run_as.py s`来启动服务器。\n用`python run_as.py c`来启动客户端。您需要提供root权限。\n\n在`run_as.py`之后添加`--debug`标志将会进入调试模式。\n在此模式下，将会在控制台输出收发的IP数据帧。\n"
  },
  {
    "path": "config.dev.json",
    "content": "{\n    \"version\": \"1.0\",\n    \"core\": {\n        \"server\": {\n            \"ip\": \"10.1.0.1\"\n        },\n        \"client\": {\n            \"ip\": \"10.1.0.2\"\n        },\n        \"user\": {\n            \"uidname\": \"nobody\",\n            \"gidname\": \"nobody\"\n        },\n        \"key\": \"DEVELOPMENT ONLY\"\n    },\n    \"proxies\": {\n        \"proxy-ws-01\": {\n            \"type\": \"websocket\",\n            \"server\": {\n                \"port\": 7501 \n            },\n            \"client\": {\n                \"url\": \"ws://127.0.0.1/login/\"\n            }\n        },\n        \"proxy-ss-01\": {\n            \"type\": \"shadowsocks\",\n            \"tunnel\": {\n                \"binary\":{\n                    \"server\": \"/usr/local/bin/ss-server\",\n                    \"client\": \"/usr/local/bin/ss-tunnel\"\n                },\n                \"server\": {\n                    \"ip\": \"127.0.0.1\",\n                    \"port\": 31000\n                },\n                \"client\": {\n                    \"ip\": \"127.0.0.1\",\n                    \"port\": 10080\n                }\n            },\n            \"entrance\": \"127.0.0.1\"\n        }\n    }\n}\n"
  },
  {
    "path": "doc/TODO.md",
    "content": "任务列表\n========\n\n## v1.2(未确定)\n\n[      ] 1. 在客户端上设立控制端口，以便开发web界面，查看UDP流量、延迟，管理开放情况\n\n[      ] 2. 考虑一种控制协议，可以通过上一条所述方式控制服务器端程序\n\n[      ] 3. 修改Readme中的语法错误\n\n[      ] 4. 用图显示本地各端口的延时情况。\n\n## v1.1\n\n[ DONE ] 1. 重构虚拟网卡为一个类，用纯Python实现，删除了对python-pytun的依赖\n\n[ DONE ] 2. 修改代理接口的协议，也许使用Unix Socket，改进握手的协议，将接口的\n            性能统计（通过DataPacket的时刻）也加入。\n\n[delete] 2.1 v1.1-using-unix-socket 分支，使用Unix Socket作为代理进程和主进程\n             之间的通信方式。需要一种统一的IPC模块来完成任务。\n             (由于UnixSocket的特性，准备放弃此特性，重写由UDP连接的IPC）\n\n[ DONE ] 2.2 使用UDP构建IPC服务器类，并将对代理程序的配置转用IPC完成\n\n[ DONE ] 3. 为Shadowsocks代理增加在服务器重启后也能自动恢复连接的机制（Bugfix）\n\n[ DONE ] 4. 修改加密协议，将加密后的UDP数据包长度随机化\n            此功能目前没有在代码中启用。\n\n[ DONE ] 5. 编写基于XMPP的代理(使用xmpppy)\n\n[ pend ] 6. 使用Python的logging模块来管理log输出。\n\n\n## 已完成的改进\n\n1. 在IP帧封包中加入发送时刻，使得客户端和服务器都可确定某个UDP端口（或者说某个\n   代理机制）是否堵塞。\n1. 检测是否有root权限及在启动TUN后放弃root权限\n1. config.json 加入version键，判断config.json是否兼容于本版本的程序。\n1. config.json 修改，直接对（服务器-客户机）端口对进行配置，并针对每个端口对传\n   递具体代理程序的情况。\n1. 将Debug输出改成彩色。\n\n## 远期计划 或 对其他开发者的建议\n\n下面列的改进是远期计划中的目标，当前并没有实际的编程实现。\n\n1. 在服务器上使用账户管理：\n    1. 可以自动更换通信的对称密钥，使用类似ZRTP的机制，或者传统的公钥体系完成\n       认证\n    1. 用一个服务器为多个虚拟网卡提供服务，将服务器变成虚拟局域网的路由器。在\n       本条实现后，可以开发额外的项目，实现多个虚拟网卡组网，甚至基于局域网内\n       UDP的无服务器音视频加密聊天。\n\n2. （放弃）使用multiprocessing模块代替Unix Socket进行IPC\n   （当前在v1.1-multiprocessing分支中进行开发）。\n"
  },
  {
    "path": "doc/cryptography.md",
    "content": "On the Cryptography in Fyuneru\n==============================\n\nThis article will discuss how cryptography is being used in our program\nFyuneru.\n\nIn Fyuneru, one of the most important tasks is the protection of data packets\nwe have got and to put into the virtual network interface. Such packets are\nencrypted and authenticated before its been send over a proxy, and is\nauthenticated before decrypted after received from a proxy. The encryption\nis independent of the proxy protocol.\n\nEncryption is based on simple symmetric keys. Therefore anyone who wants to\ndeploy a private proxy server, should use SSH or similar relative trustful\nmeans to send its symmetric key to the server. The reason why not using an\nasymmetric method for negotiating a key is, that:\n\n    1) This cannot guarentee more security if the server we are using as proxy\n       is not secure.\n    2) That we want to use construct such ciphertexts, that it has no more\n       characteristics as its length. It has to appear as pseudo-random. No\n       flag bits, no plaintexts...\n\nThe encryption schema is shown as following diagram:\n\n    1. Cleartext:\n                          | BUFFER arbitary length |\n                | DATALEN |                        | RANDOM PADDING |\n     Bytes      |  2      | ?                      | ?              |\n\n    2. Encryption:\n                       | ENCRYPTED BUFFER ................................ |\n           | IV | HMAC |\n     Bytes | 24 |  20  |                                                   |\n\n    3. Result:\n           | PSEUDORANDOM BUFFER OF RANDOM LENGTH ........................ |\n     Bytes | ?                                                             |\n\nEach raw plaintext buffer being encrypted has a length of no more than\n    \n    0xFFFF - 20 - 24 - 2 = 65489 bytes\n\n0xFFFF is the maximal length of a UDP packet, which is being used in our\nprogram as intermediate packets between core and proxies. 20 for a SHA1-HMAC\nresult and 24 for an IV, 2 for the length recording in cleartext.\n\nRandom padding can be applied in the cleartext, to conceal the real length\nof the buffer.\n\nWe use **HMAC-SHA1** to authenticate the **encrypted buffer** instead of\nplaintext in following reasons:\n\n    1. A good HMAC should be indistinguishable against random bytes without\n       knowledge of key. Putting it outside the cleartext and making it to\n       authenticate the cihpertext makes it easier(with a known HMAC key!) to\n       find out invalid packets without decryption.\n\n    2. This is secure enough. SHA1 is attacked only in theory, and that's even\n       not HMAC(HMAC-MD5 is still secure enough, think of that). Even an\n       attacker has got some advances in theory, it doesn't mean he will be\n       able to use it detecting a real-time traffic effectively(making it hard\n       to do deep packet analysis).\n    3. HMAC-SHA1 is 12 bytes shorter as SHA256 or more secure hash algorithms.\n\nUnrevealing the HMAC key will not lead to the leak of encryption key, since the\nHMAC key is derived from encryption key. If however someone has got this key,\nhe will be able to trick our program with seemingly legal traffic, and he\ncannot distinguish or get a proof, that it is us not him that has sent\nanything.\n\nWe use **XSalsa20** to encrypt the buffer. XSalsa20 is a stream cipher, and\nthis is good since it will produce the same bytes of output as the cleartext,\nblock ciphers will make bytes always multiples of some 16 or 32 bytes, which\nmakes it distinguishable against a carefully observer.\n"
  },
  {
    "path": "fyuneru/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n__all__ = [\\\n    \"config\",\n    \"crypto\",\n    \"debug\",\n    \"droproot\",\n    \"procmgr\",\n    \"protocol\"\n]\n"
  },
  {
    "path": "fyuneru/ipc/__init__.py",
    "content": ""
  },
  {
    "path": "fyuneru/ipc/__protocol.py",
    "content": "\"\"\"\nIPC used data packets generation and parsing\n============================================\n\nThis module defines several data packet classes, and a trying function for\nreading a buffer.\n\"\"\"\n\nimport pickle\n\nTYPE_DATAPACKET = 0x01\nTYPE_HEARTBEAT  = 0x02\nTYPE_QUERY      = 0x03\nTYPE_INFO       = 0x04\n\nclass WrongTypeOfPacketException(Exception): pass\n\n##############################################################################\n\n# Data packet, carries a buffer.\n\nclass DataPacket:\n\n    buffer = ''\n    \n    def __init__(self, buf=None):\n        if None == buf: return\n        if ord(buf[0]) != TYPE_DATAPACKET: raise WrongTypeOfPacketException()\n        self.buffer = buf[1:]\n\n    def __str__(self):\n        return chr(TYPE_DATAPACKET) + self.buffer\n\n# Heartbeat packet, carries nothing.\n\nclass HeartbeatPacket:\n    \n    def __init__(self, buf=None):\n        if None == buf: return\n        if ord(buf[0]) != TYPE_HEARTBEAT: raise WrongTypeOfPacketException()\n\n    def __str__(self):\n        return \\\n            chr(TYPE_HEARTBEAT) +\\\n            \"Across the Great Wall, we can reach every corner in the world.\"\n\n# Query packet, carries a question text.\n\nclass QueryPacket:\n\n    question = ''\n    arguments = {}\n\n    def __init__(self, buf=None):\n        if None == buf: return\n        if ord(buf[0]) != TYPE_QUERY: raise WrongTypeOfPacketException()\n        obj = pickle.loads(buf[1:])\n        self.question = obj[\"question\"]\n        self.arguments = obj[\"arguments\"]\n\n    def __str__(self):\n        obj = {\"question\": self.question, \"arguments\": self.arguments}\n        return chr(TYPE_QUERY) + pickle.dumps(obj)\n\n# Info packet, carries an info text.\n\nclass InfoPacket:\n\n    def __init__(self, buf=None):\n        if None == buf: return\n        if ord(buf[0]) != TYPE_INFO: raise WrongTypeOfPacketException()\n        self.__dict__ = pickle.loads(buf[1:])\n\n    def __setattr__(self, name, value):\n        self.__dict__[name] = value\n\n    def __getattr__(self, name):\n        return getattr(self.__dict__, name)\n\n    def __str__(self):\n        return chr(TYPE_INFO) + pickle.dumps(self.__dict__)\n\n##############################################################################\n\ndef loadBufferToPacket(buf):\n    tries = [DataPacket, HeartbeatPacket, QueryPacket, InfoPacket]\n    success = False\n    for packetClass in tries:\n        try:\n            loaded = packetClass(buf)\n            success = True\n            break\n        except WrongTypeOfPacketException:\n            continue\n        except Exception,e:\n            raise e\n    if success: return loaded\n    return None\n\n##############################################################################\n\n__all__ = ['DataPacket', 'HeartbeatPacket', 'QueryPacket', 'InfoPacket', 'loadBufferToPacket']\n\nif __name__ == '__main__':\n    pin1 = InfoPacket()\n    pin2 = InfoPacket()\n\n    pin1.attr1 = '1'\n    pin1.attr2 = '2'\n\n    pin2.attr1 = '3'\n    pin2.attr2 = '4'\n\n    pin3 = InfoPacket(str(pin1))\n    print pin3.attr1\n    print pin3.attr2\n\n    pin4 = InfoPacket(str(pin2))\n    print pin4.attr1\n    print pin4.attr2\n"
  },
  {
    "path": "fyuneru/ipc/client.py",
    "content": "\n# -*- coding: utf-8 -*-\n\n\"\"\"\nInternal Fyuneru Socket for Proxy Processes\n\nA fyuneru socket basing on UDP socket is defined. It is always a listening UDP\nsocket on a port in local addr, which does automatic handshake with given\ninternal magic word, and provides additionally abilities like encryption\nunderlays, traffic statistics, etc.\n\"\"\"\n\nfrom logging import debug, info, warning, error, exception\nfrom time import time\nfrom struct import pack, unpack\nfrom socket import socket, AF_INET, SOCK_DGRAM\n\nfrom fyuneru.util.crypto import Authenticator\nfrom __protocol import *\nfrom .url import IPCServerURL\n\n##############################################################################\n\nclass InternalSocketClient:\n\n    __sock = None\n    __name = None\n    __peer = (None, None) \n    \n    connected = False\n    broken = False\n\n    __lastbeatSent = 0\n    __lastbeatRecv = 0\n\n    __infoHandler = None\n\n    def __init__(self, serverURL):\n        server = IPCServerURL(serverURL)\n\n        self.__sock = socket(AF_INET, SOCK_DGRAM)\n        self.__authenticator = Authenticator(server.key)\n        self.__peer = (server.host, server.port)\n        self.name = server.user\n\n    def __getattr__(self, name):\n        return getattr(self.__sock, name)\n\n    # ---------- heartbeat related\n\n    def __registerLastBeatSent(self):\n        self.__lastbeatSent = time()\n\n    def __registerLastBeatRecv(self):\n        self.__lastbeatRecv = time()\n        self.connected = True\n        self.broken = False\n\n    # ---------- internal mechanism dealing with outbound/inbound data\n\n    def __sendPacket(self, packet):\n        \"\"\"Send a packet class to a destination using local socket.\"\"\"\n        s = self.__authenticator.sign(str(packet))\n        try:\n            self.__sock.sendto(s, self.__peer)\n        except Exception,e:\n            exception(e)\n            self.connected = False\n            self.broken = True\n\n    def __recvBuffer(self, buf, sender):\n        \"\"\"Receive a buffer, unpack into packet, and dispatch it to different\n        handlers. Returns buffer when unpacked is a DataPacket. Otherwise\n        None.\"\"\"\n        # filter out traffic that's not originating from what we thought\n        if sender != self.__peer: return None\n        # See if is a data packet, which is special.\n        buf = self.__authenticator.verify(buf)\n        if not buf: return None # signature check failed\n        packet = loadBufferToPacket(buf)\n        if not packet: return None\n        if isinstance(packet, DataPacket): return packet.buffer\n\n        # If not, call different handlers to handle this.\n        if isinstance(packet, HeartbeatPacket):\n            self.__handleHeartbeatPacket(packet)\n            return None\n        if isinstance(packet, InfoPacket):\n            self.__handleInfoPacket(packet)\n            return None\n\n    # ---------- inner handlers for different packets\n\n    def __handleHeartbeatPacket(self, packet):\n        # heart beat reply received, answer\n        if self.connected == False: debug(\"IPC client connected.\")\n        self.__registerLastBeatRecv()\n\n    def __handleInfoPacket(self, packet):\n        if self.__infoHandler: self.__infoHandler(packet)\n\n    # ---------- public functions\n\n    def doQuery(self, fillerFunc):\n        packet = QueryPacket()\n        s = fillerFunc(packet)\n        if s:\n            debug(\"Sent a query packet.\")\n            self.__sendPacket(packet)\n\n    def onInfo(self, handler):\n        self.__infoHandler = handler\n\n    def close(self):\n        debug(\"IPC socket shutting down...\")\n        try:\n            self.__sock.close()\n        except Exception,e:\n            error(\"Error closing socket: %s\" % e)\n\n    def heartbeat(self):\n        tdiffSent = time() - self.__lastbeatSent\n        tdiffRecv = time() - self.__lastbeatRecv\n        if not self.connected or tdiffSent > 2:\n            try:\n                self.__registerLastBeatSent()\n                self.__sendPacket(HeartbeatPacket())\n                if not self.connected: debug(\"IPC heartbeat sent to server.\")\n            except Exception,e:\n                exception(e)\n                error(\"Heartbeat of IPC connection at client failed.\")\n                self.connected = False\n                self.broken = True\n        if self.connected and tdiffRecv > 5:\n            warning(\"Stale IPC connection at client detected.\")\n            self.connected = False\n            self.broken = True\n\n    def receive(self):\n        buf, sender = self.__sock.recvfrom(65536)\n        buf = self.__recvBuffer(buf, sender) # pre handling this buffer\n\n        if not buf: return None # digested within other mechanism. exit.\n        return buf \n\n    def send(self, buf):\n        if not self.connected: return\n        try:\n            packet = DataPacket()\n            packet.buffer = buf\n            self.__sendPacket(packet)\n        except Exception,e:\n            exception(e)\n            error(\"Failed sending buffer to IPC server.\")\n            self.connected = False # this peer may not work\n            self.broken = True\n"
  },
  {
    "path": "fyuneru/ipc/server.py",
    "content": "# -*- coding: utf-8 -*-\n\n\"\"\"\nInternal Fyuneru Socket for Proxy Processes\n\nA fyuneru socket basing on UDP socket is defined. It is always a listening UDP\nsocket on a port in local addr, which does automatic handshake with given\ninternal magic word, and provides additionally abilities like encryption\nunderlays, traffic statistics, etc.\n\"\"\"\n\nimport os\nfrom logging import debug, info, warning, error, exception\nfrom time import time\nfrom struct import pack, unpack\nfrom socket import socket, AF_INET, SOCK_DGRAM\nimport random\n\nfrom ..util.crypto import Crypto, Authenticator\nfrom fyuneru.util.crypto import randrange\nfrom __protocol import * \n\nIPCPort = 64089\n\n##############################################################################\n\nclass InternalSocketServer:\n\n    __sockpath = None\n    __sock = None\n    \n    local = None\n    IPCKey = None\n    peers = {}\n\n    __answerFunctions = {} # for IPC query/info service\n\n    def __init__(self, key):\n        self.IPCKey = os.urandom(32)\n\n        self.__crypto = Crypto(key)\n        self.__authenticator = Authenticator(self.IPCKey)\n        self.local = (\"127.0.0.1\", IPCPort)\n        \n        self.__sock = socket(AF_INET, SOCK_DGRAM)\n        self.__sock.bind(self.local)\n\n    def __getattr__(self, name):\n        return getattr(self.__sock, name)\n\n    def __registerPeer(self, addrTuple, timestamp=None):\n        \"\"\"Register a peer's activity. This implies we have heard from this\n        peer. If timestamp is given, it will be used to update the last network\n        reception time.\"\"\"\n        now = time()\n\n        if not self.peers.has_key(addrTuple):\n            self.peers[addrTuple] = {\\\n                \"recv\": False,\n                \"send\": False,\n                \"heartbeat\": now,\n            }\n            return\n\n        if timestamp: self.peers[addrTuple][\"recv\"] = timestamp\n        self.peers[addrTuple][\"heartbeat\"] = now\n\n    def __choosePeer(self):\n        \"\"\"Choose a peer randomly. This implies we are going to send a packet\n        to this peer, and thus the sending timing will be updated.\"\"\"\n        possiblePeers = [i for i in self.peers if self.peers[i] != False]\n        if len(possiblePeers) < 1: return None\n        peer = possiblePeers[randrange(0, len(possiblePeers))]\n        self.peers[peer][\"send\"] = time()\n        return peer\n\n    def __sendPacket(self, packet, to):\n        \"\"\"Send a packet class to a destination using local socket.\"\"\"\n        s = self.__authenticator.sign(str(packet))\n        self.__sock.sendto(s, to)\n\n    def __recvBuffer(self, buf, sender):\n        \"\"\"Receive a buffer, unpack into packet, and dispatch it to different\n        handlers. Returns buffer when unpacked is a DataPacket. Otherwise\n        None.\"\"\"\n        # See if is a data packet, which is special.\n        buf = self.__authenticator.verify(buf)\n        if not buf: return None # signature check failed\n        packet = loadBufferToPacket(buf)\n        if not packet: return None\n        if isinstance(packet, DataPacket): return packet.buffer\n\n        # If not, call different handlers to handle this.\n        if isinstance(packet, HeartbeatPacket):\n            self.__handleHeartbeatPacket(packet, sender)\n            return None\n        if isinstance(packet, QueryPacket):\n            self.__handleQueryPacket(packet, sender)\n            return None\n\n    # ---------- inner handlers for different packets\n\n    def __handleHeartbeatPacket(self, packet, sender):\n        # If this is a greeting word, register this as a new connected peer\n        # and answer.\n        self.__registerPeer(sender)\n        self.__sendPacket(packet, sender)\n\n    def __handleQueryPacket(self, packet, sender):\n        question = packet.question\n        debug(\"Got a query packet.\")\n        if self.__answerFunctions.has_key(question):\n            # a new answer formular\n            answer = InfoPacket()\n            # call handler func to fill in the answer formular\n            s = self.__answerFunctions[question](packet.arguments, answer)\n            # send the answer back\n            if s: self.__sendPacket(answer, sender)\n\n    # ---------- public functions\n\n    def onQuery(self, question, answerfunc):\n        self.__answerFunctions[question] = answerfunc\n\n    def close(self):\n        # close socket\n        debug(\"Internal socket shutting down...\")\n        try:\n            self.__sock.close()\n        except Exception,e:\n            error(\"Error closing socket.\")\n            exception(e)\n\n    def clean(self):\n        # reserved for doing clean up jobs relating to the peer delays\n        removeList = []\n        now = time()\n        for each in self.peers:\n            if not self.peers[each]:\n                # if peer has been marked as False, because of errors, etc\n                removeList.append(each)\n                continue\n            # if we have not heard from peer for some while, take it as stale\n            if now - self.peers[each][\"heartbeat\"] > 5:\n                removeList.append(each)\n        if len(removeList) > 0:\n            for each in removeList:\n                # delete peers: forget them(no more tasks will be assigned)\n                self.peers[each] = False\n                del self.peers[each]\n            warning(\\\n                \"Following proxies are removed due to irresponsibility: \\n\" +\n                \" \\n\".join([\"%s:%d\" % i for i in removeList])\n            )\n\n    def receive(self):\n        buf, sender = self.__sock.recvfrom(65536)\n\n        buf = self.__recvBuffer(buf, sender) # pre handling this buffer\n        if not buf: return None # digested within other mechanism. exit.\n\n        # Otherwise, this is data buffer and has to be decrypted correctly.\n        decryption = self.__crypto.decrypt(buf)\n        if not decryption: return None\n\n        # Decrypted data must be also good formated.\n        if len(decryption) < 8: return None\n        header = decryption[:8]\n        timestamp = unpack('<d', header)[0]\n        buf = decryption[8:]\n\n        # Only then we will recognize this as a legal status update from this\n        # peer. Refresh the peer record with updated receiving timings.\n        self.__registerPeer(sender, min(timestamp, time()))\n\n        return buf \n\n    def send(self, buf):\n        # Choose a peer randomly\n        peer = self.__choosePeer()\n        if not peer: \n            error(\"Not even one proxy found. Dropping a packet.\")\n            return\n\n        # Prepare for the data that's going to be sent to this peer\n        header = pack('<d', time())\n        encryption = self.__crypto.encrypt(header + buf)\n\n        # Send to this peer. If anything goes wrong, mark this peer as False\n        try:\n            packet = DataPacket()\n            packet.buffer = encryption\n            self.__sendPacket(packet, peer)\n        except Exception,e:\n            exception(e) # for debug\n            warning(\\\n                (\"Failed sending to proxy listening at %s:%d.\" % peer) +\n                \"This proxy will be removed.\"\n            )\n            self.peers[peer] = False # this peer may not work\n"
  },
  {
    "path": "fyuneru/ipc/tools.py",
    "content": "\"\"\"\nTool functions for IPC\n\"\"\"\nfrom select import select\nfrom logging import info, debug, exception, error\n\nclass InitConfigWaiter:\n    \"\"\"For clients. Send an IPC query with `init` command and block execution,\n    until a packet with proper answer is returned.\"\"\"\n\n    __queried = False\n\n    def __queryFiller(self, packet):\n        packet.question = 'init'\n        packet.arguments = {\"name\": self.__ipc.name}\n        return True\n\n    def __infoReader(self, packet):\n        try:\n            if packet.title != 'init': return\n            self.__queried = {\n                \"user\": (packet.uid, packet.gid),\n                \"config\": packet.config,\n                \"key\": packet.key,\n                \"mode\": packet.mode,\n            }\n        except Exception,e:\n            exception(e)\n\n    def __init__(self, ipc):\n        self.__ipc = ipc\n        ipc.onInfo(lambda p: self.__infoReader(p))\n\n    def wait(self):\n        info(\"Waiting for configuration.\")\n        i = 0\n        while i < 5:\n            self.__ipc.doQuery(lambda p: self.__queryFiller(p))\n            r = select([self.__ipc], [], [], 1.0)[0]\n            i += 1\n            if len(r) < 1: continue\n            self.__ipc.receive()\n            if self.__queried: break\n        if not self.__queried: return None\n        return self.__queried\n"
  },
  {
    "path": "fyuneru/ipc/url.py",
    "content": "\"\"\"\nURL for starting IPC client\n===========================\n\nThis tells the IPC client how to find the IPC server, do authentication\nand identify itself to server.\n\"\"\"\n\n_URLPREFIX = \"fyuneru-ipc://\"\n\nclass InvalidIPCServerURLException(Exception): pass\n\nclass IPCServerURL:\n\n    host = \"127.0.0.1\"\n    port = 64089\n    key = \"\"\n    user = \"\"\n    \n    def __init__(self, url=None):\n        if None == url: return\n        try:\n            if not url.startswith(_URLPREFIX): raise\n            url = url[len(_URLPREFIX):]\n            split = url.replace('@', ':').split(':')\n            user, key, host, port = split\n            user = user.decode('hex')\n            key = key.decode('hex')\n            port = int(port)\n            if not port in xrange(0, 65536): raise\n            self.host, self.port, self.key, self.user = host, port, key, user\n        except:\n            raise InvalidIPCServerURLException()\n\n    def __str__(self):\n        user = self.user.encode('hex')\n        key = self.key.encode('hex')\n        return _URLPREFIX + (\"%s:%s@%s:%d\" % (user, key, self.host, self.port))\n\n\nif __name__ == \"__main__\":\n    url = IPCServerURL()\n    url.key = 'kadsjfakl'\n    url.user = 'proxy1'\n    print str(url)\n\n    url2 = IPCServerURL(str(url))\n    print url2.host, url2.port, url2.key, url2.user\n"
  },
  {
    "path": "fyuneru/net/__init__.py",
    "content": ""
  },
  {
    "path": "fyuneru/net/protocol.py",
    "content": "# -*- coding: utf-8 -*-\n\n\"\"\"\nPackets of Fyuneru\n\n\"\"\"\n\nfrom struct import pack, unpack\n\nSIGN_DATAPACKET = 0x01\n\n##############################################################################\n\n# DataPacket\n\nclass DataPacketException(Exception):\n    pass\n\nclass DataPacket:\n\n    data = ''\n\n    def __unpack(self, buf):\n        if(len(buf) < 1):\n            raise DataPacketException(\"Not a valid data packet.\")\n        sign = unpack('<B', buf[:1])[0]\n        if sign != SIGN_DATAPACKET:\n            raise DataPacketException(\"Not a valid data packet.\")\n        self.data = buf[1:]\n\n    def __str__(self):\n        \"\"\"Pack this packet into a string.\"\"\"\n        buf = pack('<B', SIGN_DATAPACKET)\n        buf += self.data\n        return buf \n    \n    def __init__(self, buf=None):\n        \"\"\"Read a packet with given `buf`, or construct an empty packet.\"\"\"\n        if None != buf:\n            if type(buf) != str:\n                raise DataPacketException(\"Not a valid data packet.\")\n            self.__unpack(buf)\n            return\n\n##############################################################################\n\n# \n\nif __name__ == '__main__':\n    packet = DataPacket()\n    packet.data = 'abcdefg'\n    print DataPacket(str(packet))\n"
  },
  {
    "path": "fyuneru/net/vnet.py",
    "content": "# -*- coding: utf-8 -*-\n\nimport os\nimport fcntl\nimport struct\nfrom logging import info, debug, critical, exception\nfrom select import select\n\nTUNSETIFF = 0x400454ca  \nIFF_TUN   = 0x0001      # Set up TUN device\nIFF_TAP   = 0x0002      # Set up TAP device\nIFF_NO_PI = 0x1000      # Without this flag, received frame will have 4 bytes\n                        # for flags and protocol(each 2 bytes)\n\nclass VirtualNetworkInterfaceException(Exception): pass\n\nclass VirtualNetworkInterface:\n\n    mtu = 1200\n    netmask = \"255.255.255.0\"\n\n    def __getTUNDeviceLocation(self):\n        if os.path.exists(\"/dev/net/tun\"): return \"/dev/net/tun\"\n        if os.path.exists(\"/dev/tun\"): return \"/dev/tun\"\n        critical(\"TUN/TAP device not found on this OS!\")\n        raise VirtualNetworkInterfaceException(\"No TUN/TAP device.\")\n\n    def __init__(self, ip, dstip, netmask=\"255.255.255.0\"):\n        self.addr = ip \n        self.dstaddr = dstip\n        self.netmask = netmask\n\n        try:\n            self.__tun = os.open(self.__getTUNDeviceLocation(), os.O_RDWR)\n            tun = fcntl.ioctl(\\\n                self.__tun,\n                TUNSETIFF,\n                struct.pack(\"16sH\", \"fyuneru-%d\", IFF_TUN)\n            )\n        except Exception,e:\n            exception(e)\n            raise VirtualNetworkInterfaceException(\\\n                \"Cannot set TUN/TAP device.\"\n            )\n        self.name = tun[:16].strip(\"\\x00\")  \n\n    def up(self):\n        os.system(\"ifconfig %s inet %s netmask %s pointopoint %s\" %\\\n            (self.name, self.addr, self.netmask, self.dstaddr)\n        )\n        os.system(\"ifconfig %s mtu %d up\" % (self.name, self.mtu))\n        \n        info(\\\n            \"\"\"%s: mtu %d  addr %s  netmask %s  dstaddr %s\"\"\" % \\\n            (\\\n                self.name, \n                self.mtu, \n                self.addr, \n                self.netmask, \n                self.dstaddr\n            )\n        )\n\n    def fileno(self):\n        return self.__tun\n\n    def write(self, buf):\n        os.write(self.__tun, buf)\n\n    def read(self, size=65536):\n        return os.read(self.__tun, size)\n\n    def close(self):\n        try:\n            os.close(self.__tun)\n        except:\n            pass\n\nif __name__ == \"__main__\":\n    vn = VirtualNetworkInterface({\\\n        \"ip\": \"10.1.0.2\",\n        \"dstip\": \"10.1.0.1\",\n        \"netmask\": \"255.255.255.0\",\n    })\n    vn.up()\n    select([vn], [], [])\n"
  },
  {
    "path": "fyuneru/util/__init__.py",
    "content": ""
  },
  {
    "path": "fyuneru/util/__xsalsa20.py",
    "content": "\"\"\"\nModified XSalsa20/16 Cipher Implementation in Pure Python\n======================================================\n\nThis library provides a pure python implementation of XSalsa20/16 cipher.\n\nThis library is a modification of XSalsa20 cipher and adapted for fyuneru.\nChanged is merely the nonce length from 24 bytes to 28 bytes. The extra 4 bytes\nare given as higher 32 bits of the counter, making the counter only able\nto count with 32 bits. Since our usage of this cipher uses a different nonce\nfor each plaintext being encrypted, but each plaintext will not be more than\n65536 bytes(describable with just 2 bytes), this is better suited.\n\nThis implementation is not audited and may have errors that lead to serious\nproblems. And it's slow. \n\"\"\"\n\nimport array\nimport math\n\nuintArray = lambda l: array.array('I', [0] * l)\nuintArray = lambda l: [0] * l \n\n\nclass XSalsa20:\n    \n    def __salsa20Core(self, x, inbuf, oubuf):\n        for i in xrange(0, 16): x[i] = inbuf[i]\n        R = lambda a,b: (((a) << (b)) | ((a) >> (32 - (b)))) & 0xFFFFFFFF \n        for i in xrange(0, 8): # half of r(=16), r/2 rounds\n            x[ 4] ^= R(x[ 0]+x[12], 7)\n            x[ 8] ^= R(x[ 4]+x[ 0], 9)\n            x[12] ^= R(x[ 8]+x[ 4],13)\n            x[ 0] ^= R(x[12]+x[ 8],18)\n            x[ 9] ^= R(x[ 5]+x[ 1], 7)\n            x[13] ^= R(x[ 9]+x[ 5], 9)\n            x[ 1] ^= R(x[13]+x[ 9],13)\n            x[ 5] ^= R(x[ 1]+x[13],18)\n            x[14] ^= R(x[10]+x[ 6], 7)\n            x[ 2] ^= R(x[14]+x[10], 9)\n            x[ 6] ^= R(x[ 2]+x[14],13)\n            x[10] ^= R(x[ 6]+x[ 2],18)\n            x[ 3] ^= R(x[15]+x[11], 7)\n            x[ 7] ^= R(x[ 3]+x[15], 9)\n            x[11] ^= R(x[ 7]+x[ 3],13)\n            x[15] ^= R(x[11]+x[ 7],18)\n            x[ 1] ^= R(x[ 0]+x[ 3], 7)\n            x[ 2] ^= R(x[ 1]+x[ 0], 9)\n            x[ 3] ^= R(x[ 2]+x[ 1],13)\n            x[ 0] ^= R(x[ 3]+x[ 2],18)\n            x[ 6] ^= R(x[ 5]+x[ 4], 7)\n            x[ 7] ^= R(x[ 6]+x[ 5], 9)\n            x[ 4] ^= R(x[ 7]+x[ 6],13)\n            x[ 5] ^= R(x[ 4]+x[ 7],18)\n            x[11] ^= R(x[10]+x[ 9], 7)\n            x[ 8] ^= R(x[11]+x[10], 9)\n            x[ 9] ^= R(x[ 8]+x[11],13)\n            x[10] ^= R(x[ 9]+x[ 8],18)\n            x[12] ^= R(x[15]+x[14], 7)\n            x[13] ^= R(x[12]+x[15], 9)\n            x[14] ^= R(x[13]+x[12],13)\n            x[15] ^= R(x[14]+x[13],18)\n        for i in xrange(0, 16): oubuf[i] = (inbuf[i] + x[i]) & 0xFFFFFFFF\n\n    def __streamXor(self, iv, key, buf):\n        c2u = lambda src, f:\\\n            src[f] + (src[f+1] << 4) + (src[f+2] << 8) + (src[f+3] << 12)\n        key = bytearray(key)\n        iv = bytearray(iv)\n        len0 = len(buf)\n        buf = bytearray(buf) + bytearray('0' * 64)\n\n        if len(key) != 32 or len(iv) != 28:\n            raise Exception(\"Key must be 32 bytes, IV must be 28 bytes\")\n\n        temp = uintArray(16)\n        b1i, b1o = uintArray(16), uintArray(16)\n        b2i, b2o = uintArray(16), uintArray(16)\n        salsa20Constants = (0x61707865, 0x3320646e, 0x79622d32, 0x6b206574)\n\n        b1i[0], b1i[5], b1i[10], b1i[15] = salsa20Constants\n        b1i[1], b1i[2], b1i[3], b1i[4] = \\\n            c2u(key, 0), c2u(key, 4), c2u(key, 8), c2u(key, 12)\n        b1i[11], b1i[12], b1i[13], b1i[14] = \\\n            c2u(key, 16), c2u(key, 20), c2u(key, 24), c2u(key, 28)\n        b1i[6], b1i[7], b1i[8], b1i[9] = \\\n            c2u(iv, 0), c2u(iv, 4), c2u(iv, 8), c2u(iv, 12)\n\n        self.__salsa20Core(temp, b1i, b1o)\n\n        b2i[0], b2i[5], b2i[10], b2i[15] = salsa20Constants\n        b2i[1], b2i[2], b2i[3], b2i[4] = b1o[0], b1o[5], b1o[10], b1o[15]\n        b2i[11], b2i[12], b2i[13], b2i[14] = b1o[6], b1o[7], b1o[8], b1o[9]\n        b2i[6], b2i[7] = c2u(iv, 16), c2u(iv, 20) \n        b2i[8] = c2u(iv, 24)\n\n        i = 0\n        output = bytearray('0' * (len0 + 64))\n        b2i[9] = 0\n        while i < len0:\n            self.__salsa20Core(temp, b2i, b2o)\n            for j in xrange(0, 16):\n                output[i+0] = buf[i+0] ^ ((b2o[j] & 0x000000FF))\n                output[i+1] = buf[i+1] ^ ((b2o[j] & 0x0000FF00) >> 8)\n                output[i+2] = buf[i+2] ^ ((b2o[j] & 0x00FF0000) >> 16)\n                output[i+3] = buf[i+3] ^ ((b2o[j] & 0xFF000000) >> 24)\n                i += 4\n            b2i[9] += 1\n        return str(output[:len0])\n\n    def encrypt(self, iv, key, buf):\n        return self.__streamXor(iv, key, buf)\n\n    def decrypt(self, iv, key, buf):\n        return self.__streamXor(iv, key, buf)\n\n\nif __name__ == \"__main__\":\n    from salsa20 import XSalsa20_xor\n\n\n    key = bytearray([0] * 32)\n    key[0] = 0x80\n    iv  = bytearray([0] * 28)\n    buf = bytearray([0] * 50)\n    c = XSalsa20()\n    print c.encrypt(iv, key, buf).encode('hex')\n    print XSalsa20_xor(str(buf), str(iv)[:24], str(key)).encode('hex')\n    exit()\n\n    key = '0' * 32\n    iv = '1' * 28\n    buf = 't' * 1684 \n    c = XSalsa20()\n    repeat = 1000\n\n    import time\n\n    a = time.time()\n    for i in xrange(0, repeat):\n        ourres = c.encrypt(iv, key, buf)\n    b = time.time()\n\n    print len(buf) * repeat * 1.0 / (b - a)\n"
  },
  {
    "path": "fyuneru/util/config.py",
    "content": "# -*- coding: utf-8 -*-\n\n\"\"\"\nConfig reader and manager for the core program\n\nClass Configuration provides the functionalities for loading the `config.json`\nand getting them parsed, and for generating the commands necessary for\ninitializing the proxy subprocesses(more details defined in module\n`proxyconf`).\n\"\"\"\n\nimport os\nfrom distutils.version import StrictVersion\nfrom json import loads\n\nfrom ..ipc.url import IPCServerURL\n\nVERSION_REQUIREMENT = \"1.1\" # required version of `config.json`\n\nproxies = {\n    \"shadowsocks\": \"proxy.shadowsocks.py\",\n    \"xmpp\": \"proxy.xmpp.py\",\n}\n\n##############################################################################\n\nclass CoreConfiguration: pass\nclass ProxyConfiguration: pass\n\nclass ConfigFileException(Exception): pass\n\n##############################################################################\n\nclass Configuration:\n\n    def __checkKeyExistence(self, parentDict, *keys):\n        for each in keys:\n            if not parentDict.has_key(each):\n                return False\n        return True\n\n    def __loadCore(self, core):\n        # parse json.core and save to class attributes\n        \n        # get key for cryptography\n        self.key = str(core[\"key\"])\n        if type(self.key) != str:\n            raise ConfigFileException(\"core.key is invalid.\")\n\n        # get IP for server and client\n        if not core[\"server\"].has_key(\"ip\"):\n            raise ConfigFileException(\"core.server.ip must be specified.\")\n        if not core[\"client\"].has_key(\"ip\"):\n            raise ConfigFileException(\"core.client.ip must be specified.\")\n        self.serverIP = core[\"server\"][\"ip\"]\n        self.clientIP = core[\"client\"][\"ip\"]\n\n        # get UID/GID names\n        if not core[\"user\"].has_key('uidname'):\n            raise ConfigFileException(\"core.user.uidname must be specified.\")\n        if not core[\"user\"].has_key('gidname'):\n            raise ConfigFileException(\"core.user.gidname must be specified.\")\n        self.user = (core[\"user\"][\"uidname\"], core[\"user\"][\"gidname\"])\n\n    def __loadProxyAllocations(self, proxies):\n        # get and validate proxy and ports allocations\n        self.__proxies = {}\n\n        for proxyName in proxies:\n            proxyConfig = proxies[proxyName]\n            self.__proxies[proxyName] = proxyConfig\n\n    def listProxies(self):\n        return self.__proxies.keys()\n\n    def getProxyConfig(self, name):\n        if not self.__proxies.has_key(name):\n            raise ConfigFileException(\"No such proxy defined.\")\n        proxyConfig = self.__proxies[name]\n        return proxyConfig\n\n    def getProxyInitParameters(self, proxyName, IPCServer, debug=False):\n        proxyConfig = self.__proxies[proxyName]\n        proxyType = proxyConfig[\"type\"]\n        cmd = [\"python\", proxies[proxyType]]\n        if debug: cmd += [\"--debug\"]\n\n        url = IPCServerURL()\n        url.host = IPCServer.local[0]\n        url.port = IPCServer.local[1]\n        url.user = proxyName\n        url.key = IPCServer.IPCKey\n\n        cmd += [str(url)]\n        return cmd\n\n    def getCoreInitParameters(self, mode):\n        ret = CoreConfiguration()\n        \n        ret.uid = self.user[0]\n        ret.gid = self.user[1]\n        ret.key = self.key\n\n        if mode == 's':\n            ret.localIP = self.serverIP\n            ret.remoteIP = self.clientIP\n        elif mode == 'c':\n            ret.localIP = self.clientIP\n            ret.remoteIP = self.serverIP\n        else:\n            raise Exception(\"Invalid run mode, either `c` or `s`.\")\n\n        return ret \n\n    def __init__(self, config):\n        # try load the configuration file string, and parse into JSON.\n        try:\n            json = loads(config)\n        except Exception,e:\n            raise ConfigFileException(\"config.json parse error.\")\n\n        # read config file version declaration\n        jsonVersion = \"0.0.0\"\n        if json.has_key(\"version\"):\n            jsonVersion = json[\"version\"]\n        if StrictVersion(jsonVersion) < StrictVersion(VERSION_REQUIREMENT):\n            raise ConfigFileException(\"config.json version too old.\")\n\n        # check existence of 'core' and 'proxies' entry\n        if not self.__checkKeyExistence(json, 'core', 'proxies'):\n            raise ConfigFileException(\"config.json incomplete.\")\n\n        # check entries of core\n        jsonCore = json[\"core\"]\n        if not self.__checkKeyExistence(\\\n            jsonCore, \n            'server',\n            'client',\n            'user',\n            'key',\n        ):\n            raise ConfigFileException(\"config.json incomplete.\")\n        self.__loadCore(jsonCore)\n\n        # check for proxy allocations\n        jsonProxies = json[\"proxies\"]\n        self.__loadProxyAllocations(jsonProxies)\n\n\n##############################################################################\n\nif __name__ == \"__main__\":\n    j = open('../config.json', 'r').read()\n    #print j\n    config = Configuration(j)\n    lst = config.listProxies()\n    for n in lst:\n        proxyConfig = config.getProxyConfig(n)\n        print proxyConfig.getInitCommand('s')\n        print proxyConfig.getInitCommand('c')\n"
  },
  {
    "path": "fyuneru/util/crypto.py",
    "content": "\"\"\"\nCryptographical Services\n========================\n\n## Authenticator in IPC communication\n\nIn IPC communications, data are not encrypted, but it has to be authenticated\nsince any process can practically send data to a UDP port our process listening\non. We currently use HMAC-MD5, for local usage this should be enough and\nperformance comes first.\n\n\n## Encryption and Decryption for Internet Traffic\n\n### Schema\n\n    1. Cleartext:\n                          | BUFFER arbitary length |\n                | DATALEN |                        | RANDOM PADDING |\n     Bytes      |  2      | ?                      | ?              |\n\n    2. Encryption:\n                       | ENCRYPTED BUFFER ................................ |\n           | IV | HMAC |\n     Bytes | 24 |  20  |                                                   |\n\n    3. Result:\n           | PSEUDORANDOM BUFFER OF RANDOM LENGTH ........................ |\n     Bytes | ?                                                             |\n\n### Remarks\n\n1. We use HMAC-SHA1 to authenticate the encrypted buffer in following reasons:\n1.1 A good HMAC should be indistinguishable against random bytes without\n    knowledge of key. Putting it outside the cleartext and making it to\n    authenticate the cihpertext makes it easier(with a known HMAC key!) to find\n    out invalid packets without decryption.\n1.2 This is secure enough. SHA1 is attacked only in theory, and that's even not\n    HMAC(HMAC-MD5 is still secure enough, think of that). Even an attacker has\n    got some advances in theory, it doesn't mean he will be able to use it\n    detecting a real-time traffic effectively(making it hard to do deep packet\n    analysis).\n1.3 Unrevealing the HMAC key will not lead to the leak of encryption key, since \n    the HMAC key is derived from encryption key. If however someone has got\n    this key, he will be able to trick our program with seemingly legal\n    traffic, and he cannot distinguish or get a proof, that it is us not him\n    that has sent anything.\n1.4 HMAC-SHA1 is 12 bytes shorter as SHA256.\n\n\"\"\"\n\nimport hashlib \nimport hmac\nimport math\nfrom os import urandom\nfrom struct import pack, unpack\n\nfrom salsa20 import XSalsa20_xor\n\n_HASHALGO = hashlib.sha1\n_HASHLEN = len(_HASHALGO('').digest())\n\n_RESULT_SIZE = 0xFFFF - 24 - 2 - _HASHLEN \n\n\nclass CryptoException(Exception): pass\n\ndef randint(a, b):\n    i = unpack('<L', urandom(4))[0]\n    return a + i % (b-a+1)\n\ndef randrange(a, b):\n    i = unpack('<L', urandom(4))[0]\n    return a + i % (b-a)\n\ndef decidePaddingLength(bufferLength):\n    global _RESULT_SIZE\n    return 0\n    if bufferLength < 1500:\n        randSize = randint(0, 1500)\n        if randSize > bufferLength:\n            return randSize - bufferLength\n        return 0\n    else:\n        maxSize = int(min(_RESULT_SIZE - bufferLength, bufferLength * 2.0))\n        return randint(0, maxSize)\n\ndef KDF1(secret, length):\n    \"\"\"ISO-18033-2 defined simple KDF function. Notice we assume secret is\n    already long and random enough.\"\"\"\n    global _HASHALGO, _HASHLEN\n    d = int(math.ceil(length * 1.0 / _HASHLEN))\n    T = \"\"\n    for i in xrange(0, d):\n        C = pack(\"<H\", i)\n        T = T + _HASHALGO(secret + C).digest()\n    return T[:length]\nKDF = KDF1\n\n##############################################################################\n\nclass Authenticator:\n    \"\"\"Authentication service for IPC mechanism. Provides signing and\n    verifying.  For signing a buffer, the HMAC result will be returned together\n    with original buffer.  This result can then be passed to the verify\n    function on another process. If verification succeeded, return buffer. If\n    not, return None.\"\"\"\n\n    __ALGORITHM_CONFIG = (hashlib.md5, 16) # 16 bytes output for MD5.\n\n    def __init__(self, key):\n        self.__origHMAC = hmac.new(key, '', self.__ALGORITHM_CONFIG[0])\n\n    def __HMAC(self, buf):\n        worker = self.__origHMAC.copy()\n        worker.update(buf)\n        return worker.digest()\n\n    def sign(self, buf):\n        signature = self.__HMAC(buf)\n        return signature + buf\n\n    def verify(self, buf):\n        hlen = self.__ALGORITHM_CONFIG[1]\n        if len(buf) < hlen: return None\n        signature = buf[:hlen]\n        buf = buf[hlen:]\n        if self.__HMAC(buf) != signature: return None\n        return buf\n\n\n\nclass Crypto:\n    \"\"\"Crypto service for traffic over Internet.\"\"\"\n\n    def __init__(self, passphrase):\n        self.__KEY = KDF(passphrase, 32)\n        HMACKEY = KDF(self.__KEY, _HASHLEN)\n        self.__origHMAC = hmac.new(HMACKEY, '', _HASHALGO)\n\n    def __HMAC(self, buf):\n        worker = self.__origHMAC.copy()\n        worker.update(buf)\n        return worker.digest()\n\n    def encrypt(self, buf):\n        if len(buf) > _RESULT_SIZE: \n            raise CryptoException('Buffer too large to be encrypted.')\n\n        buflen = len(buf)\n        lenstr = pack('<H', buflen)\n        padding = '0' * decidePaddingLength(buflen) # TODO discuss security here!\n        cleartext = lenstr + buf + padding\n\n        iv = urandom(24) # since IV will be exposed, has to be crypto. random.\n        ciphertext = XSalsa20_xor(cleartext, iv, self.__KEY)\n        hmac = self.__HMAC(ciphertext)\n\n        return iv + hmac + ciphertext\n\n    def decrypt(self, buf):\n        if len(buf) < 24 + _HASHLEN:\n            return False\n        iv = buf[:24]\n        hmac = buf[24:24+_HASHLEN]\n        ciphertext = buf[24+_HASHLEN:]\n\n        hmac2 = self.__HMAC(ciphertext)\n        if hmac2 != hmac: return False\n\n        decrypted = XSalsa20_xor(ciphertext, iv, self.__KEY)\n        lenstr = decrypted[:2]\n        buflen = unpack('<H', lenstr)[0]\n        buf = decrypted[2:][:buflen]\n        if len(buf) != buflen: return False\n        \n        return buf\n\n\nif __name__ == '__main__':\n    encryptor = Crypto('test')\n    decryptor = Crypto('test')\n\n    import time\n    a = time.time()\n    for x in xrange(0, 1024):\n        encrypted = encryptor.encrypt('a' * 400)\n        decrypted = encryptor.decrypt(encrypted)\n        if not decrypted:\n            print \"*\"\n    b = time.time()\n    print \"done in %f seconds\" % (b-a)\n#    print len(encrypted), decryptor.decrypt(encrypted)\n"
  },
  {
    "path": "fyuneru/util/debug.py",
    "content": "import socket\nfrom struct import * \nimport logging\nimport sys\nimport os\n\n##############################################################################\n\ndef configLoggingModule(debug):\n    logLevel = logging.INFO\n    if debug: logLevel = logging.DEBUG\n\n    procname = os.path.basename(sys.argv[0])\n    logFormat = \\\n        \"\\n=== [%%(asctime)-15s] [%s|%d]: %%(levelname)s\\n %%(message)s\"\\\n        % (procname, os.getpid())\n    \n\n    logging.basicConfig(\\\n        level=logLevel,\n        format=logFormat\n    )\n\n##############################################################################\n\ndef colorify(text, color):\n    colors = {\\\n        \"blue\": '\\033[94m',\n        \"green\": '\\033[92m',\n        \"red\": '\\033[91m',\n    }\n    ENDC = '\\033[0m'\n    BOLD = '\\033[1m'\n    UNDERLINE = '\\033[4m'\n    return colors[color] + text + ENDC\n\n##############################################################################\n\ndef _decodeIPFrame(buf):\n    # meaning this is not raw packet, but with 4 bytes prefixed as in TUN\n    # device defined\n    if buf[:2] == '\\x00\\x00': buf = buf[4:] \n\n    ip_header = buf[0:20]\n    iph = unpack('!BBHHHBBH4s4s' , ip_header)\n     \n    version_ihl = iph[0]\n    version = (version_ihl >> 4) & 0xF\n    ihl = version_ihl & 0xF\n     \n    iph_length = ihl * 4\n     \n    ttl = iph[5]\n    protocol = iph[6]\n    s_addr = socket.inet_ntoa(iph[8])\n    d_addr = socket.inet_ntoa(iph[9])\n\n    payload = buf[iph_length:]\n\n    ip_meta = {\n        \"version\": version,\n        \"length\": str(ihl),\n        \"TTL\": ttl,\n        \"protocol\": protocol,\n        \"src\": str(s_addr),\n        \"dst\": str(d_addr),\n    }\n    return ip_meta, payload\n\ndef showPacket(buf):\n    width = 16\n    lines = []\n    origbuf = buf\n    while buf != '':\n        lines.append(buf[:width])\n        buf = buf[width:]\n    ret = ''\n    start = 0\n    for line in lines:\n        hexstr = ''\n        asciistr = ''\n        for c in line:\n            ordc = ord(c)\n            hexstr += '%02x ' % ordc\n            if ordc <= 0x7E and ordc >= 0x20:\n                asciistr += c\n            else:\n                asciistr += '.'\n        start += len(line)\n        hexstr = hexstr.ljust(3 * width)\n        ret += \"%08x: %s: %s\\n\" % (start, hexstr, asciistr)\n\n    ipReport = ''\n    try:\n        ipMeta, ipPayload = _decodeIPFrame(origbuf)\n        ipReport = \"Ver %s :: TTL %d :: Protocol %s :: Src %s :: Dst %s\" % (\\\n            ipMeta[\"version\"],\n            ipMeta[\"TTL\"],\n            ipMeta[\"protocol\"],\n            ipMeta[\"src\"],\n            ipMeta[\"dst\"]\n        )\n        ret = ipReport + \"\\n\" + ret\n    except Exception,e:\n        print e\n        pass\n\n    return ret.strip() \n\n##############################################################################\n\nif __name__ == '__main__':\n    import os\n    print showPacket(os.urandom(200))\n"
  },
  {
    "path": "fyuneru/util/droproot.py",
    "content": "\"\"\"\nFunction to drop root privileges\n<http://stackoverflow.com/questions/2699907/dropping-root-permissions-in-python>\n\nThis has to be used in 2 places:\n* `run_as.py`, whose root privilege will be passed to `tunnel.py`. Root is\n  no more needed after `tunnel.py` is started, and will be dropped.\n* `tunnel.py`, which will need root to setup TUN device. After that, root will\n  be dropped.\n\"\"\"\n\nimport grp\nimport os\nimport pwd\n\ndef dropRoot(uid_name='nobody', gid_name='nobody'):\n    if os.getuid() != 0:\n        # We're not root so, like, whatever dude\n        return\n\n    # Get the uid/gid from the name\n    running_uid = pwd.getpwnam(uid_name).pw_uid\n    running_gid = grp.getgrnam(gid_name).gr_gid\n\n    # Remove group privileges\n    os.setgroups([])\n\n    # Try setting the new uid/gid\n    os.setgid(running_gid)\n    os.setuid(running_uid)\n\n    # Ensure a very conservative umask\n    old_umask = os.umask(077)\n"
  },
  {
    "path": "fyuneru/util/pidfile.py",
    "content": "# -*- coding: utf-8 -*-\n\nimport os\nimport time\n\n\n\nclass ProcessRunningException(Exception): pass\nclass PidfileNonExistentException(Exception): pass\n\nclass PidfileWatcher:\n    \"\"\"Watches if a pidfile exists. If exist at time of initialization of this\n    instance, may also be used to check if the pidfile is changed or deleted.\n      This is useful for subprocesses who may want to exit after main process\n    is terminated.\n    \"\"\"\n    __lastcheck = 0\n    pid = None\n    pidfile = None\n\n    def __init__(self, path):\n        self.pidfile = path\n        self.__lastcheck = time.time()\n        try:\n            pidfile = open(self.pidfile, 'r')\n            self.pid = pidfile.read()\n            pidfile.close()\n        except:\n            raise PidfileNonExistentException()\n\n    def check(self):\n        try:\n            pidfile = open(self.pidfile, 'r')\n            pid = pidfile.read()\n            pidfile.close()\n        except:\n            return False\n        return pid == self.pid\n        \n\nclass PidfileCreator:\n    def __init__(self, path, log=sys.stdout.write, warn=sys.stderr.write):\n        self.pidfile = path\n        self.log = log\n        self.warn = warn\n\n    def __enter__(self):\n        try:\n            self.pidfd = os.open(self.pidfile, os.O_CREAT|os.O_WRONLY|os.O_EXCL)\n            self.log('locked pidfile %s' % self.pidfile)\n        except OSError as e:\n            if e.errno == errno.EEXIST:\n                pid = self._check()\n                if pid:\n                    self.pidfd = None\n                    raise ProcessRunningException('process already running in %s as pid %s' % (self.pidfile, pid))\n                else:\n                    os.remove(self.pidfile)\n                    self.warn('removed staled lockfile %s' % (self.pidfile))\n                    self.pidfd = os.open(self.pidfile, os.O_CREAT|os.O_WRONLY|os.O_EXCL)\n            else:\n                raise\n\n        os.write(self.pidfd, str(os.getpid()))\n        os.close(self.pidfd)\n        return self\n\n    def __exit__(self, t, e, tb):\n        # return false to raise, true to pass\n        if t is None:\n            # normal condition, no exception\n            self._remove()\n            return True\n        elif t is PidfileProcessRunningException:\n            # do not remove the other process lockfile\n            return False\n        else:\n            # other exception\n            if self.pidfd:\n                # this was our lockfile, removing\n                self._remove()\n        return False\n\n    def _remove(self):\n        self.log('removed pidfile %s' % self.pidfile)\n        os.remove(self.pidfile)\n\n    def _check(self):\n        \"\"\"check if a process is still running\n\n        the process id is expected to be in pidfile, which should exist.\n\n        if it is still running, returns the pid, if not, return False.\"\"\"\n        with open(self.pidfile, 'r') as f:\n            try:\n                pidstr = f.read()\n                pid = int(pidstr)\n            except ValueError:\n                # not an integer\n                self.log(\"not an integer: %s\" % pidstr)\n                return False\n            try:\n                os.kill(pid, 0)\n            except OSError:\n                self.log(\"can't deliver signal to %s\" % pid)\n                return False\n            else:\n                return pid\n"
  },
  {
    "path": "fyuneru/util/procmgr.py",
    "content": "# -*- coding: utf-8 -*-\n\nimport signal\nimport subprocess\nimport time\nfrom logging import debug, info, warning, error\n\n\nclass ProcessManagerException(Exception): pass\n\nclass ProcessManager:\n\n    __processes = {}\n    __commands = {}\n\n    def __init__(self):\n        signal.signal(signal.SIGTERM, self.killall)\n\n    def __pollAll(self):\n        for each in self.__processes:\n            try:\n                self.processes[each].poll()\n            except:\n                pass\n\n    def __startProcess(self, name, command):\n        info(\"ProcessManager starting [%s]: %s\" % (name, \" \".join(command)))\n        proc = subprocess.Popen(command)\n        self.__processes[name] = proc\n        self.__commands[name] = command\n\n    def new(self, name, command):\n        if self.__processes.has_key(name):\n            raise ProcessManagerException(\\\n                \"Child process already registered with name [%s]\" % name)\n        self.__startProcess(name, command)\n\n    def killall(self, tolerance=1.0):\n        for each in self.__processes:\n            self.kill(each, tolerance)\n\n    def kill(self, name, tolerance=1.0):\n        if not self.__processes.has_key(name): return True\n        sigtermSuccess = False\n        if tolerance:\n            try:\n                info(\"ProcessManager stopping [%s] gracefully.\" % name)\n                self.__processes[name].terminate()\n                time.sleep(1.0)\n                self.__processes[name].poll()\n                sigtermSuccess = (self.__processes[name].returncode != None)\n            except:\n                pass\n        if sigtermSuccess:\n            info(\"ProcessManager confirmed stop of [%s].\" % name)\n            return True\n        warning(\"ProcessManager couldn't stop [%s] with tolerance.\" % name)\n        try:\n            self.__processes[name].kill()\n            info(\"ProcessManager killed [%s].\" % name)\n            return True\n        except Exception,e:\n            error((\"ProcessManager cannot stop [%s]. \" % name) + \\\n                \"It may has been already killed. Giving up.\")\n            return False\n\n    def restart(self, name, tolerance=1.0):\n        if not self.__commands.has_key(name): return False\n        if not self.kill(name, tolerance):\n            return False\n        command = self.__commands[name]\n        self.__startProcess(name, command)\n\n    def wait(self, name):\n        self.__processes[name].wait()\n\n\nclass ParentProcessWatcher:\n    __last = 0\n    def __init__(self, pid, terminator):\n        self.__pid = pid\n        self.__func = terminator\n        self.__last = time.time()\n\n    def watch(self):\n        now = time.time()\n        if now - self.__last < 10: return\n        proclist = subprocess.check_output(['ps', '-e']).split('\\n')\n        strpid = str(self.__pid)\n        found = False\n        for each in proclist:\n            if each.strip().startswith(strpid):\n                found = True\n                return\n        info(\"Watching: %d\" % self.__last)\n        if found:\n            self.__last = now\n        else:\n            warning(\"Parent pid [%d] not present anymore.\" % self.__pid)\n            self.__func(None, None)\n"
  },
  {
    "path": "proxy.icmp.py",
    "content": "\n\"\"\"\nICMP Proxy Process\n==================\n\nThis is a very simple proxy that puts our encrypted payloads into ICMP packets.\nSince encrypted payloads will be recognized using cryptographical means, we\nwon't do anything on the payload.\n\nICMP Proxy is currently only one-way: client->server is okay, reverse is not\nsupported. You have to use other means of proxies to get replied.\n\"\"\"\n# TODO: modify fyuneru.intsck to make server not try to use this tunnel as\n# answer.\n\n\nimport argparse\nimport signal\nfrom select import select\n\nfrom fyuneru.ipc.client import InternalSocketClient\nfrom fyuneru.droproot import dropRoot\n\ndef log(x):\n    print \"proxy-icmp-client: %s\" % x\n\n# ----------- parse arguments\n\nparser = argparse.ArgumentParser()\n\n# drop privilege to ...\nparser.add_argument(\"--uidname\", metavar=\"UID_NAME\", type=str, required=True)\nparser.add_argument(\"--gidname\", metavar=\"GID_NAME\", type=str, required=True)\n\nparser.add_argument(\"--client-send-to\", type=str, required=False)\n\nargs = parser.parse_args()\n\n\n##############################################################################\n\nif None == args.client_send_to:\n    # run as server mode\n    sck = socket.socket(socket.AF_INET,socket.SOCK_RAW,socket.IPPROTO_ICMP)\n    sck.setsockopt(socket.SOL_IP, socket.IP_HDRINCL, 1)\n    dropRoot(args.uidname, args.gidname)\nelse:\n    # run as client mode\n    pass\n\nlocal = InternalSocketClient(args.socket)\n\n##############################################################################\n\ndef doExit(signum, frame):\n    global local, proxy\n    try:\n        local.close()\n    except:\n        pass\n    try:\n        proxy.xmpp.disconnect()\n    except:\n        pass\n    print \"exit now.\"\n    exit()\nsignal.signal(signal.SIGTERM, doExit)\n\n##############################################################################\n\nsockets = {\n    proxy.xmpp.Connection._sock: 'proxy',\n    local: 'local',\n}\n\nwhile True:\n    try:\n        local.heartbeat()\n        r, w, _ = select(sockets.keys(), [], [], 1)\n        for each in r:\n            if sockets[each] == 'proxy':\n                proxy.xmpp.Process(1)\n                for b in proxy.recvQueue:\n                    log(\"Received %d bytes, sending to core.\" % len(b))\n                    local.send(b)\n                proxy.recvQueue = []\n\n            if sockets[each] == 'local':\n                recv = local.receive()\n                if not recv: continue\n                log(\"Received %d bytes, sending to tunnel.\" % len(recv))\n                proxy.send(recv)\n\n\n    except KeyboardInterrupt:\n        doExit(None,None)\n\n\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "proxy.shadowsocks.py",
    "content": "# -*- coding: utf-8 -*-\n\nimport argparse\nimport os\nimport sys\nfrom select import select\nimport socket\nimport signal\nimport time\nimport logging\nfrom logging import info, debug, warning, error\nimport hashlib\nimport hmac\n\nfrom fyuneru.ipc.client import InternalSocketClient\nfrom fyuneru.util.droproot import dropRoot\nfrom fyuneru.util.procmgr import ProcessManager\nfrom fyuneru.util.debug import configLoggingModule\nfrom fyuneru.ipc.url import IPCServerURL\nfrom fyuneru.ipc.tools import InitConfigWaiter \n\nENCRYPTION_METHOD = 'aes-256-cfb'\n\n##############################################################################\n\n# ----------- parse arguments\n\nparser = argparse.ArgumentParser()\n\nparser.add_argument(\"--debug\", action=\"store_true\", default=False)\nparser.add_argument(\"IPC_SERVER_URL\", type=str)\n\nargs = parser.parse_args()\n\n##############################################################################\n\nconfigLoggingModule(args.debug)\n\n##############################################################################\n\n# use command line to initialize IPC client\n\nipc = InternalSocketClient(args.IPC_SERVER_URL)\n\nqueried = InitConfigWaiter(ipc).wait()\nif not queried:\n    error(\"Configuration timed out. Exit.\")\n    ipc.close()\n    sys.exit(1)\n\n\n##############################################################################\n\ndebug(\"Drop privilege to %s:%s\" % queried[\"user\"])\ndropRoot(*queried[\"user\"])\n\n##############################################################################\n\n# start shadowsocks process\n\nprocmgr = ProcessManager()\n\nsharedsecret= hmac.HMAC(\n    str(ipc.name + '-shadowsocks'),\n    queried[\"key\"],\n    hashlib.sha256\n).digest().encode('base64').strip()\nproxyConfig = queried[\"config\"]\n\nforwardToPort = proxyConfig[\"server\"][\"forward-to\"] # exit port at server\n\nif 'c' == queried[\"mode\"]: # CLIENT mode\n    if proxyConfig[\"client\"].has_key(\"proxy\"):\n        connectIP = proxyConfig[\"client\"][\"proxy\"][\"ip\"]\n        connectPort = proxyConfig[\"client\"][\"proxy\"][\"port\"]\n    else:\n        connectIP = proxyConfig[\"server\"][\"ip\"]\n        connectPort = proxyConfig[\"server\"][\"port\"]\n    sscmd = [\n        proxyConfig[\"client\"][\"bin\"],                   # shadowsocks-libev\n        '-U',                                           # UDP relay only\n        '-L', \"127.0.0.1:%d\" % forwardToPort,           # destinating UDP addr\n        '-k', sharedsecret,                             # key\n        '-s', connectIP,                                # server host\n        '-p', str(connectPort),                         # server port\n        '-b', \"127.0.0.1\",                              # local addr\n        '-l', str(proxyConfig[\"client\"][\"port\"]),       # local port(entrance)\n        '-m', ENCRYPTION_METHOD,                        # encryption method\n    ]\nelif 's' == queried['mode']: # SERVER mode\n    sscmd = [\n        proxyConfig[\"server\"][\"bin\"],                   # shadowsocks-libev\n        '-U',                                           # UDP relay only\n        '-k', sharedsecret,                             # key\n        '-s', proxyConfig[\"server\"][\"ip\"],              # server host\n        '-p', str(proxyConfig[\"server\"][\"port\"]),       # server port\n        '-m', ENCRYPTION_METHOD,                        # encryption method\n    ]\nelse:\n    sys.exit(127)\n\nprocmgr.new('shadowsocks', sscmd)\n    \n\n##############################################################################\n\nproxySocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\n\nif 's' == queried[\"mode\"]:\n    proxySocket.bind(('127.0.0.1', forwardToPort))\n    proxyPeer = None                  # not knowing where to send data back\nelse:\n    # send to local tunnel entrance\n    proxyPeer = ('127.0.0.1', proxyConfig[\"client\"][\"port\"])\n\n##############################################################################\n\ndef doExit(signum, frame):\n    global ipc, proxySocket, procmgr\n    try:\n        ipc.close()\n    except:\n        pass\n    try:\n        proxySocket.close()\n    except:\n        pass\n    try:\n        procmgr.killall()\n    except:\n        pass\n    info(\"Exit now.\")\n    exit()\nsignal.signal(signal.SIGTERM, doExit)\n\n##############################################################################\n\nwhile True:\n    try:\n        ipc.heartbeat()\n\n        selected = select([ipc, proxySocket], [], [], 1.0)\n        if len(selected) < 1:\n            continue\n        readables = selected[0]\n\n        for each in readables:\n            if each == ipc:\n                buf = ipc.receive()\n                if None == buf: continue\n                if None == proxyPeer: continue\n                debug(\"Received %d bytes, sending to tunnel.\" % len(buf))\n                proxySocket.sendto(buf, proxyPeer)\n            \n            if each == proxySocket:\n                buf, sender = each.recvfrom(65536)\n                proxyPeer = sender\n                debug(\"Received %d bytes, sending back to core.\" % len(buf))\n                ipc.send(buf)\n\n        if ipc.broken: doExit(None, None)\n    except KeyboardInterrupt:\n        doExit(None, None)\n"
  },
  {
    "path": "proxy.tcp.py",
    "content": "# -*- coding: utf-8 -*-\n\n\"\"\"\nUDP over TCP proxy for Fyuneru.\n\"\"\"\n\nimport argparse\nimport os\nfrom select import select\nimport socket\nimport signal\nimport time\nimport logging\nfrom logging import info, debug, warning, error\n\nfrom fyuneru.ipc.client import InternalSocketClient\nfrom fyuneru.util.droproot import dropRoot\nfrom fyuneru.util.debug import configLoggingModule\n\n##############################################################################\n\n# ----------- parse arguments\n\nparser = argparse.ArgumentParser()\n\n# if enable debug mode\nparser.add_argument(\"--debug\", action=\"store_true\", default=False)\n\n# drop privilege to ...\nparser.add_argument(\"--uidname\", metavar=\"UID_NAME\", type=str, required=True)\nparser.add_argument(\"--gidname\", metavar=\"GID_NAME\", type=str, required=True)\n\n# mode for this script to run\nparser.add_argument(\\\n    \"--mode\",\n    type=str,\n    choices=[\"server\", \"client\"],\n    required=True\n)\n\nargs = parser.parse_args()\n\n##############################################################################\n\nconfigLoggingModule(args.debug)\n\n##############################################################################\n\ndropRoot(args.uidname, args.gidname)\n\n##############################################################################\n\nclass Datagram2Stream:\n\n    __buffer = \"\"\n    \n    def put(self, datagram):\n        self.__buffer += datagram.encode('base64').strip() + '\\n'\n\n    def get(self, size=32768):\n        ret = self.__buffer[:size]\n        self.__buffer = self.__buffer[size:]\n        return ret\n\nclass Stream2Datagram:\n\n    __buffer = \"\"\n\n    def put(self, buf):\n        self.__buffer += buf\n\n    def get(self):\n        n = self.__buffer.find('\\n')\n        if n < 0: return None\n        self.__buffer = self.__buffer[n:].strip()\n        try:\n            ret = self.__buffer[:n].strip().decode('base64')\n            return ret\n        except:\n            return None\n    \n\n##############################################################################\n\nlocalSocket = InternalSocketClient() \n\nproxySocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\n\nif 'server' == args.mode:\n    proxySocket.bind(('127.0.0.1', args.FORWARD_TO))\n    proxyPeer = None                  # not knowing where to send data back\nelse:\n    proxyPeer = ('127.0.0.1', args.l) # send to local tunnel entrance\n\n##############################################################################\n\ndef doExit(signum, frame):\n    global localSocket, proxySocket, procmgr\n    try:\n        localSocket.close()\n    except:\n        pass\n    try:\n        proxySocket.close()\n    except:\n        pass\n    try:\n        procmgr.killall()\n    except:\n        pass\n    info(\"Exit now.\")\n    exit()\nsignal.signal(signal.SIGTERM, doExit)\n\n##############################################################################\n\nwhile True:\n    try:\n        localSocket.heartbeat()\n\n        selected = select([localSocket, proxySocket], [], [], 1.0)\n        if len(selected) < 1:\n            continue\n        readables = selected[0]\n\n        for each in readables:\n            if each == localSocket:\n                buf = localSocket.receive()\n                if None == buf: continue\n                if None == proxyPeer: continue\n                debug(\"Received %d bytes, sending to tunnel.\" % len(buf))\n                proxySocket.sendto(buf, proxyPeer)\n            \n            if each == proxySocket:\n                buf, sender = each.recvfrom(65536)\n                proxyPeer = sender\n                debug(\"Received %d bytes, sending back to core.\" % len(buf))\n                localSocket.send(buf)\n\n        if localSocket.broken: doExit(None, None)\n    except KeyboardInterrupt:\n        doExit(None, None)\n"
  },
  {
    "path": "proxy.xmpp.py",
    "content": "\"\"\"\nXMPP Proxy Process\n==================\n\nThis proxy utilizes the `xmpppy` library. You have to first install it before\nusing this. If\n    \n    sudo pip install xmpppy\n\ndoesn't work, you may have to download it from <http://xmpppy.sourceforge.net>\nand install manually.\n\"\"\"\n\n\nimport argparse\nimport sys\nimport signal\nfrom select import select\nimport logging\nfrom logging import info, warning, debug, exception, error\n\nimport xmpp\n\nfrom fyuneru.ipc.client import InternalSocketClient\nfrom fyuneru.util.droproot import dropRoot\nfrom fyuneru.util.debug import configLoggingModule\nfrom fyuneru.ipc.url import IPCServerURL\nfrom fyuneru.ipc.tools import InitConfigWaiter\n\n##############################################################################\n\n# ----------- parse arguments\n\nparser = argparse.ArgumentParser()\n\nparser.add_argument(\"--debug\", action=\"store_true\", default=False)\nparser.add_argument(\"IPC_SERVER_URL\", type=str)\n\nargs = parser.parse_args()\n\n##############################################################################\n\nconfigLoggingModule(args.debug)\n\n# use command line to initialize IPC client\n\nipc = InternalSocketClient(args.IPC_SERVER_URL)\n\nqueried = InitConfigWaiter(ipc).wait()\nif not queried:\n    error(\"Configuration timed out. Exit.\")\n    ipc.close()\n    sys.exit(1)\n\n##############################################################################\n\ndebug(\"Drop privilege to %s:%s\" % queried[\"user\"])\ndropRoot(*queried[\"user\"])\n\n##############################################################################\n\nclass SocketXMPPProxyException(Exception): pass\n\nclass SocketXMPPProxy:\n\n    def __init__(self, jid, password, peer):\n        self.__jid = xmpp.protocol.JID(jid)\n        self.__peer = xmpp.protocol.JID(peer)\n        self.__peerJIDStripped = self.__peer.getStripped()\n\n        self.xmpp = xmpp.Client(\\\n            self.__jid.getDomain(),\n            debug=[\"always\", \"socket\", \"nodebuilder\", \"dispatcher\"]\n        )\n        connection = self.xmpp.connect()\n        if not connection:\n            raise SocketXMPPProxyException(\"Unable to connect.\")\n\n        authentication = self.xmpp.auth(\\\n            self.__jid.getNode(),\n            password\n        )\n        if not authentication:\n            raise SocketXMPPProxyException(\"Authentication error.\")\n\n        self.__registerHandlers()\n        self.__sendPresence()\n\n    def __registerHandlers(self):\n        self.xmpp.RegisterHandler('message', self.message)\n\n    def __sendPresence(self):\n        presence = xmpp.protocol.Presence(priority=999)\n        self.xmpp.send(presence)\n\n    recvQueue = []\n\n    def message(self, con, event):\n        msgtype = event.getType()\n        fromjid = event.getFrom().getStripped()\n        if fromjid != self.__peerJIDStripped: return\n        if msgtype in ('chat', 'normal', None):\n            body = event.getBody()\n            try:\n                self.recvQueue.append(body.decode('base64'))\n            except:\n                pass\n\n    def send(self, buf):\n        buf = buf.encode('base64')\n        message = xmpp.protocol.Message(to=self.__peer, body=buf, typ='chat')\n        self.xmpp.send(message)\n\n##############################################################################\n\nproxyConfig = queried[\"config\"]\nif 's' == queried[\"mode\"]:\n    localJID = proxyConfig[\"server\"][\"jid\"]\n    localPassword = proxyConfig[\"server\"][\"password\"]\n    remoteJID = proxyConfig[\"client\"][\"jid\"]\nelif 'c' == queried[\"mode\"]:\n    localJID = proxyConfig[\"client\"][\"jid\"]\n    localPassword = proxyConfig[\"client\"][\"password\"]\n    remoteJID = proxyConfig[\"server\"][\"jid\"]\nelse:\n    sys.exit(127)\nproxy = SocketXMPPProxy(localJID, localPassword, remoteJID)\n\ninfo(\"XMPP proxy from [%s](local) to [%s](remote).\" % (localJID, remoteJID))\n\n##############################################################################\n\ndef doExit(signum, frame):\n    global ipc, proxy\n    try:\n        ipc.close()\n    except:\n        pass\n    try:\n        proxy.xmpp.disconnect()\n    except:\n        pass\n    print \"exit now.\"\n    exit()\nsignal.signal(signal.SIGTERM, doExit)\n\n##############################################################################\n\nsockets = {\n    proxy.xmpp.Connection._sock: 'proxy',\n    ipc: 'ipc',\n}\n\nwhile True:\n    try:\n        if ipc.broken: doExit(None, None)\n        ipc.heartbeat()\n        r, w, _ = select(sockets.keys(), [], [], 1)\n        for each in r:\n            if sockets[each] == 'proxy':\n                proxy.xmpp.Process(1)\n                for b in proxy.recvQueue:\n                    debug(\"Received %d bytes, sending to core.\" % len(b))\n                    ipc.send(b)\n                proxy.recvQueue = []\n\n            if sockets[each] == 'ipc':\n                recv = ipc.receive()\n                if not recv: continue\n                debug(\"Received %d bytes, sending to tunnel.\" % len(recv))\n                proxy.send(recv)\n    except KeyboardInterrupt:\n        doExit(None,None)\n\n    except Exception,e:\n        exception(e)\n"
  },
  {
    "path": "run_as.py",
    "content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nimport argparse\nimport os\nfrom select import select\nimport signal\nimport sys\nimport logging\nfrom logging import info, debug, warning, error, exception, critical\n\nfrom fyuneru.net.vnet import VirtualNetworkInterface\nfrom fyuneru.util.config import Configuration\nfrom fyuneru.util.debug import showPacket, configLoggingModule\nfrom fyuneru.util.droproot import dropRoot\nfrom fyuneru.net.protocol import DataPacket, DataPacketException\nfrom fyuneru.ipc.server import InternalSocketServer\nfrom fyuneru.util.procmgr import ProcessManager\n\n##############################################################################\n\nparser = argparse.ArgumentParser()\n\nparser = argparse.ArgumentParser(description=\"\"\"\n    This is the initator of Fyeneru proxy. Before running, put a `config.json`\n    in the same path as this file.\n    Requires root priviledge for running this script.\n\"\"\")\nparser.add_argument(\\\n    \"--debug\",\n    action=\"store_true\",\n    default=False,\n    help = \"Print debug info, e.g. packet data.\"\n)\nparser.add_argument(\\\n    \"mode\",\n    metavar=\"MODE\",\n    type=str, \n    choices=['s', 'c'],\n    help=\"\"\"\n        Either 'c' or 's', respectively for client mode and server mode.\n    \"\"\"\n)\n\nargs = parser.parse_args()\n\n\nPATH = os.path.realpath(os.path.dirname(sys.argv[0]))\nMODE = args.mode\n\nMTU = 1400 \n\n##############################################################################\n\n# ---------- config log/debug functions\n\nconfigLoggingModule(args.debug)\n\n# ---------- load and parse configuration file\n\nconfig = Configuration(open(os.path.join(PATH, 'config.json'), 'r').read())\ncoreConfig = config.getCoreInitParameters(MODE)\n\n# ---------- initialize IPC and ProcessManager\n\nipc = InternalSocketServer(coreConfig.key)\nprocesses = ProcessManager()\n\n# ---------- config TUN device and start up\n\nif \"client\" == args.mode:\n    info(\"Running as client.\")\nelse:\n    info(\"Running as server.\")\ntun = VirtualNetworkInterface(coreConfig.localIP, coreConfig.remoteIP)\ntun.netmask = \"255.255.255.0\"\ntun.mtu = MTU\n\ntun.up()\ninfo(\"%s: up now.\" % tun.name)\n\n# ----------- prepare info for IPC clients(part of each proxy process)\n\n# register answer functions with ipc.onQuery(question, func), providing\n# services for IPC client to get its necessary information\n\ndef ipcOnQueryInit(argv, answer):\n    global config, MODE\n    try:\n        proxyName = argv[\"name\"]\n        proxyConfig = config.getProxyConfig(proxyName)\n        answer.title = 'init' \n\n        answer.uid = config.user[0]\n        answer.gid = config.user[1]\n        answer.config = proxyConfig\n        answer.key = config.key\n        answer.mode = MODE\n    except Exception,e:\n        exception(e)\n        error(\"We cannot answer an init query.\")\n        return False\n    return True\nipc.onQuery('init', ipcOnQueryInit)\n\n# ---------- initialize proxy processes\n\nfor proxyName in config.listProxies():\n    proxyCommand = config.getProxyInitParameters(proxyName, ipc, args.debug)\n    processes.new(proxyName, proxyCommand)\n\n# ---------- drop root privileges\n\ndropRoot(coreConfig.uid, coreConfig.gid)\n\n##############################################################################\n\n# register exit function, and start IO loop\n\nreads = [ipc, tun] # for `select` function\n\ndef doExit(signum, frame):\n    global reads, processes \n    info(\"Exit now.\")\n    # first close TUN devices\n    for each in reads: each.close()\n    # kill processes\n    t = 1.0 # second(s) waiting for exit\n    try:\n        processes.killall(t)\n        info(\"Exiting. Wait %f seconds for child processes to exit.\" % t)\n    except Exception,e:\n        error(\"Exiting, error: %s\" % e)\n    info(\"Good bye.\")\n    sys.exit()\nsignal.signal(signal.SIGTERM, doExit)\nsignal.signal(signal.SIGINT, doExit)\n\n\nwhile True:\n    try:\n        \n        # ---------- deal with I/O things\n        \n        readables = select(reads, [], [], 5.0)[0]\n        for each in readables:\n            if each == tun:\n                # ---------- forward packets came from tun0\n                buf = each.read(65536) # each.read(each.mtu)\n                # pack buf with timestamp\n                packet = DataPacket()\n                packet.data = buf\n                # encrypt and sign buf\n                ipc.send(str(packet))\n                debug(showPacket(buf))\n            if each == ipc:\n                # ---------- receive packets from internet\n                buf = each.receive()\n                if buf == None:\n                    # Received buffer being digested by InternalSocket itself,\n                    # either some internal mechanism packet, or packet with\n                    # wrong destination, or packet decryption failed...\n                    continue\n\n                try:\n                    packet = DataPacket(buf)\n                except DataPacketException, e:\n                    # if failed reading the packet\n                    debug(\"[%d] --> %s: Bad packet - %s\" % (i, tun.name, e))\n                    continue\n                \n                # send buf to network interface\n                tun.write(packet.data)\n                debug(showPacket(packet.data))\n\n        # ---------- deal with tunnel delay timings\n\n        ipc.clean()\n\n\n    except KeyboardInterrupt:\n        doExit(None, None)\n"
  }
]