[
  {
    "path": ".gitignore",
    "content": "### /Users/maple/.gitignore-boilerplates/Python.gitignore\n\n*.py[cod]\n\n# C extensions\n*.so\n\n# Packages\n*.egg\n*.egg-info\ndist\nbuild\neggs\nparts\nbin\nvar\nsdist\ndevelop-eggs\n.installed.cfg\nlib\nlib64\n\n# Installer logs\npip-log.txt\n\n# Unit test / coverage reports\n.coverage\n.tox\nnosetests.xml\n\n#Translations\n*.mo\n\n#Mr Developer\n.mr.developer.cfg\n.project\n.pydevproject\n\n\n### /Users/maple/.gitignore-boilerplates/Global/OSX.gitignore\n\n.DS_Store\nIcon\r\r\n\n# Thumbnails\n._*\n\n# Files that might appear on external disk\n.Spotlight-V100\n.Trashes\n\n\n### /Users/maple/.gitignore-boilerplates/Global/Linux.gitignore\n\n.*\n!.gitignore\n*~\n\n\n"
  },
  {
    "path": "AUTHORS",
    "content": "Author\n------\n\nMaple <MapleValley8gmail.com>\n\nContributors\n------------\n\n- qiao <xueqiaoxu@gmail.com>\n- houqp <dave2008713@gmail.com>\n- tigersoldier <tigersoldi@gmail.com>\n"
  },
  {
    "path": "CHANGES",
    "content": "YaH3C Changelog\n==============\n\nHere is the full list of changes between each YaH3C release.\n\nVersion 0.5\n-----------\n\nReleased on September 24th 2012\n\n* add command line argument support\n* add dhcp support\n* refactory usermgr module\n\nVersion 0.4\n-----------\n\nReleased on September 20th 2012\n\n* remove plugin module\n\nVersion 0.3\n-----------\n\nReleased on April 1th 2012\n\n* add eap-md5 support\n\nVersion 0.2\n-----------\n\nReleased on December 29th 2011\n\n* complete refactory \n* plugins support\n\nVersion 0.1\n-----------\n\nReleased on November 14th 2011\n\n* Initial commit\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License\n\nCopyright (c) 2010-2012 YaH3C Authors. All rights reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "YaH3C\n=====\n\nYaH3C 是用于校园网认证的客户端，支持中山大学东校区。\n\n感谢 [lpy](https://github.com/lpy) 为 OS X 提供的[版本](https://github.com/lpy/Yah3c)。\n\n为什么不用iNode\n---------------\n\n* i开头完全是对Apple的侮辱嘛 = =\n* 强制添加启动项\n* gui和cli使用的udp交互，很多时候Linux/Mac下掉线完全是因为实现的效率太低\n* 安装脚本居然是自删除的\n* 默认强制记录日志文件，增加CPU负载，减少硬盘寿命，一段时间后就过GB了（是否有敏感信息不得而知了）\n* Linux下仅仅支持Ubuntu 32位系统\n* Mac下安装后居然要重启\n* 使用的第三方库乱放和HomeBrew冲突\n\n依赖\n------------\n \n* 主流Linux发行版，包括OpenWrt/DD-WRT\n* Python2\n\n安装\n------------\n\n首先，从github上下载，可以直接利用`git clone`，也可以下载压缩包自己解压然后安装。下面以git为例，如果没有则需要先安装：\n\n```bash\n# Ubuntu/Debian\nsudo apt-get install git\n\n# ArchLinux\nsudo pacman -S git\n```\n然后，从项目中clone下来并安装\n\n```bash\ngit clone git://github.com/humiaozuzu/YaH3C.git\ncd YaH3C\nsudo python setup.py install\n```\n\n**ArchLinux**默认安装的python是python3，你需要手动安装python2。\n\n使用\n----\n\n完整的联网过程有2步，首先使用本客户端通过交换机的认证，然后获取ip。\n\n### 认证\n\n程序运行时必须要有root权限：\n\n```bash\nsudo yah3c\n```\n\n根据程序的提示输入账号密码就可以开始认证了，有些选项如果看不懂请直接按`Enter`。\n\n### 获取ip\n\n因为YaH3C仅仅是**认证**客户端，所以通过认证后你需要自己获取ip联网，不过为了方便还是添加了dhcp支持。\n\n如果没有指定dhcp的命令，你可以在认证成功后使用自己喜欢的网络管理工具获取IP，如NetworkManager或Wicd。\n\nYaH3C支持基本的命令行参数，执行`yah3c -h`可以看到支持的命令行参数\n\n``` bash\n$ yah3c -h       \nusage: yah3c [-h] [-u USERNAME] [-debug]\n\nYet Another H3C Authentication Client\n\noptional arguments:\n  -h, --help            show this help message and exit\n  -u USERNAME, --username USERNAME\n                        Login in with this username\n  -debug                Enable debugging mode\n```\n\n如执行`sudo yah3c -u Maple`可以自动认证`Maple`这个帐号\n\n配置文件格式\n---------\n用户的登陆信息按照如下的格式保存在文件`/etc/yah3c.conf`中：\n\n``` ini\n[account]                  # 你的帐户 \npassword = 123456          # 密码\nethernet_interface = eth0  # 使用的网卡，默认为eth0\ndhcp_command = dhcpcd      # 验证成功后使用的dhcp命令(dhcpcd/dhclient)，默认为空\ndaemon = True              # 验证成功后是否变成daemon进程，默认为是\n```\n\nScreenShots\n-----------\n\n认证成功:\n\n![success](https://raw.github.com/humiaozuzu/YaH3C/master/screenshots/success.png)\n\n认证失败:\n\n![failure](https://raw.github.com/humiaozuzu/YaH3C/master/screenshots/failure.png)\n\n\nTodo\n----\n* ~~添加BSD BPF 支持，这样在OS X也可以使用了~~\n* 完善收集调试信息的功能，方便用户提交认证信息\n* 完善对H3C协议的支持\n\nThanks\n------\n* [qiao](https://github.com/qiao) - Write python installation script for YaH3C\n* [houqp](https://github.com/houqp) - Refered to houqp's [pyh3c](https://github.com/houqp/pyh3c)\n* [tigersoldier](https://github.com/tigersoldier) - Write EAP-Md5 for YaH3C\n\nLicense\n-------\nYaH3C的代码使用MIT License发布，此外，禁止使用YaH3C以及YaH3C的修改程序用于商业目的（比如交叉编译到路由进行销售等行为）\n"
  },
  {
    "path": "scripts/yah3c",
    "content": "#!/usr/bin/env python\n\nif __name__ == '__main__':\n    import yah3c.yah3c\n    yah3c.yah3c.main()\n"
  },
  {
    "path": "setup.py",
    "content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nfrom distutils.core import setup\nimport yah3c.yah3c\n\nsetup(name='yah3c',\n      version=yah3c.yah3c.__version__,\n      description='A program for h3c authentication in SYSU east campus.',\n      author='maple',\n      author_email='maplevalley8@gmail.com',\n      url='https://github.com/humiaozuzu/YaH3C',\n      download_url='https://github.com/humiaozuzu/YaH3C',\n      license='MIT',\n      packages=['yah3c', 'yah3c/colorama'],\n      scripts=['scripts/yah3c'],\n      )\n"
  },
  {
    "path": "yah3c/__init__.py",
    "content": ""
  },
  {
    "path": "yah3c/colorama/__init__.py",
    "content": "from .initialise import init, deinit, reinit\nfrom .ansi import Fore, Back, Style\nfrom .ansitowin32 import AnsiToWin32\n\nVERSION = '0.2.4'\n\n"
  },
  {
    "path": "yah3c/colorama/ansi.py",
    "content": "'''\r\nThis module generates ANSI character codes to printing colors to terminals.\r\nSee: http://en.wikipedia.org/wiki/ANSI_escape_code\r\n'''\r\n\r\nCSI = '\\033['\r\n\r\ndef code_to_chars(code):\r\n    return CSI + str(code) + 'm'\r\n\r\nclass AnsiCodes(object):\r\n    def __init__(self, codes):\r\n        for name in dir(codes):\r\n            if not name.startswith('_'):\r\n                value = getattr(codes, name)\r\n                setattr(self, name, code_to_chars(value))\r\n\r\nclass AnsiFore:\r\n    BLACK   = 30\r\n    RED     = 31\r\n    GREEN   = 32\r\n    YELLOW  = 33\r\n    BLUE    = 34\r\n    MAGENTA = 35\r\n    CYAN    = 36\r\n    WHITE   = 37\r\n    RESET   = 39\r\n\r\nclass AnsiBack:\r\n    BLACK   = 40\r\n    RED     = 41\r\n    GREEN   = 42\r\n    YELLOW  = 43\r\n    BLUE    = 44\r\n    MAGENTA = 45\r\n    CYAN    = 46\r\n    WHITE   = 47\r\n    RESET   = 49\r\n\r\nclass AnsiStyle:\r\n    BRIGHT    = 1\r\n    DIM       = 2\r\n    NORMAL    = 22\r\n    RESET_ALL = 0\r\n\r\nFore = AnsiCodes( AnsiFore )\r\nBack = AnsiCodes( AnsiBack )\r\nStyle = AnsiCodes( AnsiStyle )\r\n\r\n"
  },
  {
    "path": "yah3c/colorama/ansitowin32.py",
    "content": "\r\nimport re\r\nimport sys\r\n\r\nfrom .ansi import AnsiFore, AnsiBack, AnsiStyle, Style\r\nfrom .winterm import WinTerm, WinColor, WinStyle\r\nfrom .win32 import windll\r\n\r\n\r\nif windll is not None:\r\n    winterm = WinTerm()\r\n\r\n\r\ndef is_a_tty(stream):\r\n    return hasattr(stream, 'isatty') and stream.isatty()\r\n\r\n\r\nclass StreamWrapper(object):\r\n    '''\r\n    Wraps a stream (such as stdout), acting as a transparent proxy for all\r\n    attribute access apart from method 'write()', which is delegated to our\r\n    Converter instance.\r\n    '''\r\n    def __init__(self, wrapped, converter):\r\n        # double-underscore everything to prevent clashes with names of\r\n        # attributes on the wrapped stream object.\r\n        self.__wrapped = wrapped\r\n        self.__convertor = converter\r\n\r\n    def __getattr__(self, name):\r\n        return getattr(self.__wrapped, name)\r\n\r\n    def write(self, text):\r\n        self.__convertor.write(text)\r\n\r\n\r\nclass AnsiToWin32(object):\r\n    '''\r\n    Implements a 'write()' method which, on Windows, will strip ANSI character\r\n    sequences from the text, and if outputting to a tty, will convert them into\r\n    win32 function calls.\r\n    '''\r\n    ANSI_RE = re.compile('\\033\\[((?:\\d|;)*)([a-zA-Z])')\r\n\r\n    def __init__(self, wrapped, convert=None, strip=None, autoreset=False):\r\n        # The wrapped stream (normally sys.stdout or sys.stderr)\r\n        self.wrapped = wrapped\r\n\r\n        # should we reset colors to defaults after every .write()\r\n        self.autoreset = autoreset\r\n\r\n        # create the proxy wrapping our output stream\r\n        self.stream = StreamWrapper(wrapped, self)\r\n\r\n        on_windows = sys.platform.startswith('win')\r\n\r\n        # should we strip ANSI sequences from our output?\r\n        if strip is None:\r\n            strip = on_windows\r\n        self.strip = strip\r\n\r\n        # should we should convert ANSI sequences into win32 calls?\r\n        if convert is None:\r\n            convert = on_windows and is_a_tty(wrapped)\r\n        self.convert = convert\r\n\r\n        # dict of ansi codes to win32 functions and parameters\r\n        self.win32_calls = self.get_win32_calls()\r\n\r\n        # are we wrapping stderr?\r\n        self.on_stderr = self.wrapped is sys.stderr\r\n\r\n\r\n    def should_wrap(self):\r\n        '''\r\n        True if this class is actually needed. If false, then the output\r\n        stream will not be affected, nor will win32 calls be issued, so\r\n        wrapping stdout is not actually required. This will generally be\r\n        False on non-Windows platforms, unless optional functionality like\r\n        autoreset has been requested using kwargs to init()\r\n        '''\r\n        return self.convert or self.strip or self.autoreset\r\n\r\n\r\n    def get_win32_calls(self):\r\n        if self.convert and winterm:\r\n            return {\r\n                AnsiStyle.RESET_ALL: (winterm.reset_all, ),\r\n                AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT),\r\n                AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL),\r\n                AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL),\r\n                AnsiFore.BLACK: (winterm.fore, WinColor.BLACK),\r\n                AnsiFore.RED: (winterm.fore, WinColor.RED),\r\n                AnsiFore.GREEN: (winterm.fore, WinColor.GREEN),\r\n                AnsiFore.YELLOW: (winterm.fore, WinColor.YELLOW),\r\n                AnsiFore.BLUE: (winterm.fore, WinColor.BLUE),\r\n                AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA),\r\n                AnsiFore.CYAN: (winterm.fore, WinColor.CYAN),\r\n                AnsiFore.WHITE: (winterm.fore, WinColor.GREY),\r\n                AnsiFore.RESET: (winterm.fore, ),\r\n                AnsiBack.BLACK: (winterm.back, WinColor.BLACK),\r\n                AnsiBack.RED: (winterm.back, WinColor.RED),\r\n                AnsiBack.GREEN: (winterm.back, WinColor.GREEN),\r\n                AnsiBack.YELLOW: (winterm.back, WinColor.YELLOW),\r\n                AnsiBack.BLUE: (winterm.back, WinColor.BLUE),\r\n                AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA),\r\n                AnsiBack.CYAN: (winterm.back, WinColor.CYAN),\r\n                AnsiBack.WHITE: (winterm.back, WinColor.GREY),\r\n                AnsiBack.RESET: (winterm.back, ),\r\n            }\r\n\r\n\r\n    def write(self, text):\r\n        if self.strip or self.convert:\r\n            self.write_and_convert(text)\r\n        else:\r\n            self.wrapped.write(text)\r\n            self.wrapped.flush()\r\n        if self.autoreset:\r\n            self.reset_all()\r\n\r\n\r\n    def reset_all(self):\r\n        if self.convert:\r\n            self.call_win32('m', (0,))\r\n        elif is_a_tty(self.wrapped):\r\n            self.wrapped.write(Style.RESET_ALL)\r\n\r\n\r\n    def write_and_convert(self, text):\r\n        '''\r\n        Write the given text to our wrapped stream, stripping any ANSI\r\n        sequences from the text, and optionally converting them into win32\r\n        calls.\r\n        '''\r\n        cursor = 0\r\n        for match in self.ANSI_RE.finditer(text):\r\n            start, end = match.span()\r\n            self.write_plain_text(text, cursor, start)\r\n            self.convert_ansi(*match.groups())\r\n            cursor = end\r\n        self.write_plain_text(text, cursor, len(text))\r\n\r\n\r\n    def write_plain_text(self, text, start, end):\r\n        if start < end:\r\n            self.wrapped.write(text[start:end])\r\n            self.wrapped.flush()\r\n\r\n\r\n    def convert_ansi(self, paramstring, command):\r\n        if self.convert:\r\n            params = self.extract_params(paramstring)\r\n            self.call_win32(command, params)\r\n\r\n\r\n    def extract_params(self, paramstring):\r\n        def split(paramstring):\r\n            for p in paramstring.split(';'):\r\n                if p != '':\r\n                    yield int(p)\r\n        return tuple(split(paramstring))\r\n\r\n\r\n    def call_win32(self, command, params):\r\n        if params == []:\r\n            params = [0]\r\n        if command == 'm':\r\n            for param in params:\r\n                if param in self.win32_calls:\r\n                    func_args = self.win32_calls[param]\r\n                    func = func_args[0]\r\n                    args = func_args[1:]\r\n                    kwargs = dict(on_stderr=self.on_stderr)\r\n                    func(*args, **kwargs)\r\n        elif command in ('H', 'f'): # set cursor position\r\n            func = winterm.set_cursor_position\r\n            func(params, on_stderr=self.on_stderr)\r\n        elif command in ('J'):\r\n            func = winterm.erase_data\r\n            func(params, on_stderr=self.on_stderr)\r\n\r\n"
  },
  {
    "path": "yah3c/colorama/initialise.py",
    "content": "import atexit\r\nimport sys\r\n\r\nfrom .ansitowin32 import AnsiToWin32\r\n\r\n\r\norig_stdout = sys.stdout\r\norig_stderr = sys.stderr\r\n\r\nwrapped_stdout = sys.stdout\r\nwrapped_stderr = sys.stderr\r\n\r\natexit_done = False\r\n\r\n\r\ndef reset_all():\r\n    AnsiToWin32(orig_stdout).reset_all()\r\n\r\n\r\ndef init(autoreset=False, convert=None, strip=None, wrap=True):\r\n\r\n    if not wrap and any([autoreset, convert, strip]):\r\n        raise ValueError('wrap=False conflicts with any other arg=True')\r\n\r\n    global wrapped_stdout, wrapped_stderr\r\n    sys.stdout = wrapped_stdout = \\\r\n        wrap_stream(orig_stdout, convert, strip, autoreset, wrap)\r\n    sys.stderr = wrapped_stderr = \\\r\n        wrap_stream(orig_stderr, convert, strip, autoreset, wrap)\r\n\r\n    global atexit_done\r\n    if not atexit_done:\r\n        atexit.register(reset_all)\r\n        atexit_done = True\r\n\r\n\r\ndef deinit():\r\n    sys.stdout = orig_stdout\r\n    sys.stderr = orig_stderr\r\n\r\n\r\ndef reinit():\r\n    sys.stdout = wrapped_stdout\r\n    sys.stderr = wrapped_stdout\r\n\r\n\r\ndef wrap_stream(stream, convert, strip, autoreset, wrap):\r\n    if wrap:\r\n        wrapper = AnsiToWin32(stream,\r\n            convert=convert, strip=strip, autoreset=autoreset)\r\n        if wrapper.should_wrap():\r\n            stream = wrapper.stream\r\n    return stream\r\n\r\n\r\n"
  },
  {
    "path": "yah3c/colorama/win32.py",
    "content": "\r\n# from winbase.h\r\nSTDOUT = -11\r\nSTDERR = -12\r\n\r\ntry:\r\n    from ctypes import windll\r\nexcept ImportError:\r\n    windll = None\r\n    SetConsoleTextAttribute = lambda *_: None\r\nelse:\r\n    from ctypes import (\r\n        byref, Structure, c_char, c_short, c_uint32, c_ushort\r\n    )\r\n\r\n    handles = {\r\n        STDOUT: windll.kernel32.GetStdHandle(STDOUT),\r\n        STDERR: windll.kernel32.GetStdHandle(STDERR),\r\n    }\r\n\r\n    SHORT = c_short\r\n    WORD = c_ushort\r\n    DWORD = c_uint32\r\n    TCHAR = c_char\r\n\r\n    class COORD(Structure):\r\n        \"\"\"struct in wincon.h\"\"\"\r\n        _fields_ = [\r\n            ('X', SHORT),\r\n            ('Y', SHORT),\r\n        ]\r\n\r\n    class  SMALL_RECT(Structure):\r\n        \"\"\"struct in wincon.h.\"\"\"\r\n        _fields_ = [\r\n            (\"Left\", SHORT),\r\n            (\"Top\", SHORT),\r\n            (\"Right\", SHORT),\r\n            (\"Bottom\", SHORT),\r\n        ]\r\n\r\n    class CONSOLE_SCREEN_BUFFER_INFO(Structure):\r\n        \"\"\"struct in wincon.h.\"\"\"\r\n        _fields_ = [\r\n            (\"dwSize\", COORD),\r\n            (\"dwCursorPosition\", COORD),\r\n            (\"wAttributes\", WORD),\r\n            (\"srWindow\", SMALL_RECT),\r\n            (\"dwMaximumWindowSize\", COORD),\r\n        ]\r\n        def __str__(self):\r\n            return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % (\r\n                self.dwSize.Y, self.dwSize.X\r\n                , self.dwCursorPosition.Y, self.dwCursorPosition.X\r\n                , self.wAttributes\r\n                , self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right\r\n                , self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X\r\n            )\r\n\r\n    def GetConsoleScreenBufferInfo(stream_id=STDOUT):\r\n        handle = handles[stream_id]\r\n        csbi = CONSOLE_SCREEN_BUFFER_INFO()\r\n        success = windll.kernel32.GetConsoleScreenBufferInfo(\r\n            handle, byref(csbi))\r\n        return csbi\r\n\r\n\r\n    def SetConsoleTextAttribute(stream_id, attrs):\r\n        handle = handles[stream_id]\r\n        return windll.kernel32.SetConsoleTextAttribute(handle, attrs)\r\n\r\n\r\n    def SetConsoleCursorPosition(stream_id, position):\r\n        position = COORD(*position)\r\n        # If the position is out of range, do nothing.\r\n        if position.Y <= 0 or position.X <= 0:\r\n            return\r\n        # Adjust for Windows' SetConsoleCursorPosition:\r\n        #    1. being 0-based, while ANSI is 1-based.\r\n        #    2. expecting (x,y), while ANSI uses (y,x).\r\n        adjusted_position = COORD(position.Y - 1, position.X - 1)\r\n        # Adjust for viewport's scroll position\r\n        sr = GetConsoleScreenBufferInfo(STDOUT).srWindow\r\n        adjusted_position.Y += sr.Top\r\n        adjusted_position.X += sr.Left\r\n        # Resume normal processing\r\n        handle = handles[stream_id]\r\n        return windll.kernel32.SetConsoleCursorPosition(handle, adjusted_position)\r\n\r\n    def FillConsoleOutputCharacter(stream_id, char, length, start):\r\n        handle = handles[stream_id]\r\n        char = TCHAR(char)\r\n        length = DWORD(length)\r\n        num_written = DWORD(0)\r\n        # Note that this is hard-coded for ANSI (vs wide) bytes.\r\n        success = windll.kernel32.FillConsoleOutputCharacterA(\r\n            handle, char, length, start, byref(num_written))\r\n        return num_written.value\r\n\r\n    def FillConsoleOutputAttribute(stream_id, attr, length, start):\r\n        ''' FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )'''\r\n        handle = handles[stream_id]\r\n        attribute = WORD(attr)\r\n        length = DWORD(length)\r\n        num_written = DWORD(0)\r\n        # Note that this is hard-coded for ANSI (vs wide) bytes.\r\n        return windll.kernel32.FillConsoleOutputAttribute(\r\n            handle, attribute, length, start, byref(num_written))\r\n\r\n"
  },
  {
    "path": "yah3c/colorama/winterm.py",
    "content": "\r\nfrom . import win32\r\n\r\n\r\n# from wincon.h\r\nclass WinColor(object):\r\n    BLACK   = 0\r\n    BLUE    = 1\r\n    GREEN   = 2\r\n    CYAN    = 3\r\n    RED     = 4\r\n    MAGENTA = 5\r\n    YELLOW  = 6\r\n    GREY    = 7\r\n\r\n# from wincon.h\r\nclass WinStyle(object):\r\n    NORMAL = 0x00 # dim text, dim background\r\n    BRIGHT = 0x08 # bright text, dim background\r\n\r\n\r\nclass WinTerm(object):\r\n\r\n    def __init__(self):\r\n        self._default = win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes\r\n        self.set_attrs(self._default)\r\n        self._default_fore = self._fore\r\n        self._default_back = self._back\r\n        self._default_style = self._style\r\n\r\n    def get_attrs(self):\r\n        return self._fore + self._back * 16 + self._style\r\n\r\n    def set_attrs(self, value):\r\n        self._fore = value & 7\r\n        self._back = (value >> 4) & 7\r\n        self._style = value & WinStyle.BRIGHT\r\n\r\n    def reset_all(self, on_stderr=None):\r\n        self.set_attrs(self._default)\r\n        self.set_console(attrs=self._default)\r\n\r\n    def fore(self, fore=None, on_stderr=False):\r\n        if fore is None:\r\n            fore = self._default_fore\r\n        self._fore = fore\r\n        self.set_console(on_stderr=on_stderr)\r\n\r\n    def back(self, back=None, on_stderr=False):\r\n        if back is None:\r\n            back = self._default_back\r\n        self._back = back\r\n        self.set_console(on_stderr=on_stderr)\r\n\r\n    def style(self, style=None, on_stderr=False):\r\n        if style is None:\r\n            style = self._default_style\r\n        self._style = style\r\n        self.set_console(on_stderr=on_stderr)\r\n\r\n    def set_console(self, attrs=None, on_stderr=False):\r\n        if attrs is None:\r\n            attrs = self.get_attrs()\r\n        handle = win32.STDOUT\r\n        if on_stderr:\r\n            handle = win32.STDERR\r\n        win32.SetConsoleTextAttribute(handle, attrs)\r\n\r\n    def set_cursor_position(self, position=None, on_stderr=False):\r\n        if position is None:\r\n            #I'm not currently tracking the position, so there is no default.\r\n            #position = self.get_position()\r\n            return\r\n        handle = win32.STDOUT\r\n        if on_stderr:\r\n            handle = win32.STDERR\r\n        win32.SetConsoleCursorPosition(handle, position)\r\n\r\n    def erase_data(self, mode=0, on_stderr=False):\r\n        # 0 (or None) should clear from the cursor to the end of the screen.\r\n        # 1 should clear from the cursor to the beginning of the screen.\r\n        # 2 should clear the entire screen. (And maybe move cursor to (1,1)?)\r\n        #\r\n        # At the moment, I only support mode 2. From looking at the API, it \r\n        #    should be possible to calculate a different number of bytes to clear, \r\n        #    and to do so relative to the cursor position.\r\n        if mode[0] not in (2,):\r\n            return\r\n        handle = win32.STDOUT\r\n        if on_stderr:\r\n            handle = win32.STDERR\r\n        # here's where we'll home the cursor\r\n        coord_screen = win32.COORD(0,0) \r\n        csbi = win32.GetConsoleScreenBufferInfo(handle)\r\n        # get the number of character cells in the current buffer\r\n        dw_con_size = csbi.dwSize.X * csbi.dwSize.Y\r\n        # fill the entire screen with blanks\r\n        win32.FillConsoleOutputCharacter(handle, ord(' '), dw_con_size, coord_screen)\r\n        # now set the buffer's attributes accordingly\r\n        win32.FillConsoleOutputAttribute(handle, self.get_attrs(), dw_con_size, coord_screen );\r\n        # put the cursor at (0, 0)\r\n        win32.SetConsoleCursorPosition(handle, (coord_screen.X, coord_screen.Y))\r\n"
  },
  {
    "path": "yah3c/eapauth.py",
    "content": "\"\"\" EAP authentication handler\n\nThis module sents EAPOL begin/logoff packet\nand parses received EAP packet \n\n\"\"\"\n\n__all__ = [\"EAPAuth\"]\n\nimport socket\nimport os, sys, pwd\nfrom subprocess import call\n\nfrom colorama import Fore, Style, init\n# init() # required in Windows\nfrom eappacket import *\n\ndef display_prompt(color, string):\n    prompt = color + Style.BRIGHT + '==> ' + Style.RESET_ALL\n    prompt += Style.BRIGHT + string + Style.RESET_ALL\n    print prompt\n\ndef display_packet(packet):\n    # print ethernet_header infomation\n    print 'Ethernet Header Info: '\n    print '\\tFrom: ' + repr(packet[0:6])\n    print '\\tTo: ' + repr(packet[6:12])\n    print '\\tType: ' + repr(packet[12:14])\n\nclass EAPAuth:\n    def __init__(self, login_info):\n        # bind the h3c client to the EAP protocal \n        self.client = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(ETHERTYPE_PAE))\n        self.client.bind((login_info['ethernet_interface'], ETHERTYPE_PAE))\n        # get local ethernet card address\n        self.mac_addr = self.client.getsockname()[4]\n        self.ethernet_header = get_ethernet_header(self.mac_addr, PAE_GROUP_ADDR, ETHERTYPE_PAE)\n        self.has_sent_logoff = False\n        self.login_info = login_info\n        self.version_info = '\\x06\\x07bjQ7SE8BZ3MqHhs3clMregcDY3Y=\\x20\\x20'\n\n    def send_start(self):\n        # sent eapol start packet\n        eap_start_packet = self.ethernet_header + get_EAPOL(EAPOL_START)\n        self.client.send(eap_start_packet)\n\n        display_prompt(Fore.GREEN, 'Sending EAPOL start')\n\n    def send_logoff(self):\n        # sent eapol logoff packet\n        eap_logoff_packet = self.ethernet_header + get_EAPOL(EAPOL_LOGOFF)\n        self.client.send(eap_logoff_packet)\n        self.has_sent_logoff = True\n\n        display_prompt(Fore.GREEN, 'Sending EAPOL logoff')\n\n    def send_response_id(self, packet_id):\n        self.client.send(self.ethernet_header + \n                get_EAPOL(EAPOL_EAPPACKET,\n                    get_EAP(EAP_RESPONSE,\n                        packet_id,\n                        EAP_TYPE_ID,\n                        self.version_info + self.login_info['username'])))\n\n    def send_response_md5(self, packet_id, md5data):\n        md5 = self.login_info['password'][0:16]\n        if len(md5) < 16:\n            md5 = md5 + '\\x00' * (16 - len (md5))\n        chap = []\n        for i in xrange(0, 16):\n            chap.append(chr(ord(md5[i]) ^ ord(md5data[i])))\n        resp = chr(len(chap)) + ''.join(chap) + self.login_info['username']\n        eap_packet = self.ethernet_header + get_EAPOL(EAPOL_EAPPACKET, get_EAP(EAP_RESPONSE, packet_id, EAP_TYPE_MD5, resp))\n        try:\n            self.client.send(eap_packet)\n        except socket.error, msg:\n            print \"Connection error!\"\n            exit(-1)\n\n    def send_response_h3c(self, packet_id):\n        resp = chr(len(self.login_info['password'])) + self.login_info['password'] + self.login_info['username']\n        eap_packet = self.ethernet_header + get_EAPOL(EAPOL_EAPPACKET, get_EAP(EAP_RESPONSE, packet_id, EAP_TYPE_H3C, resp))\n        try:\n            self.client.send(eap_packet)\n        except socket.error, msg:\n            print \"Connection error!\"\n            exit(-1)\n\n    def display_login_message(self, msg):\n        \"\"\"\n            display the messages received form the radius server,\n            including the error meaasge after logging failed or \n            other meaasge from networking centre\n        \"\"\"\n        try:\n            print msg.decode('gbk')\n        except UnicodeDecodeError:\n            print msg\n\n    def EAP_handler(self, eap_packet):\n        vers, type, eapol_len  = unpack(\"!BBH\",eap_packet[:4])\n        if type != EAPOL_EAPPACKET:\n            display_prompt(Fore.YELLOW, 'Got unknown EAPOL type %i' % type)\n\n        # EAPOL_EAPPACKET type\n        code, id, eap_len = unpack(\"!BBH\", eap_packet[4:8])\n        if code == EAP_SUCCESS:\n            display_prompt(Fore.YELLOW, 'Got EAP Success')\n            \n            if self.login_info['dhcp_command']:\n                display_prompt(Fore.YELLOW, 'Obtaining IP Address:')\n                call([self.login_info['dhcp_command'], self.login_info['ethernet_interface']])\n\n            if self.login_info['daemon'] == 'True':\n                daemonize('/dev/null','/tmp/daemon.log','/tmp/daemon.log')\n        \n        elif code == EAP_FAILURE:\n            if (self.has_sent_logoff):\n                display_prompt(Fore.YELLOW, 'Logoff Successfully!')\n\n                #self.display_login_message(eap_packet[10:])\n            else:\n                display_prompt(Fore.YELLOW, 'Got EAP Failure')\n\n                #self.display_login_message(eap_packet[10:])\n            exit(-1)\n        elif code == EAP_RESPONSE:\n            display_prompt(Fore.YELLOW, 'Got Unknown EAP Response')\n        elif code == EAP_REQUEST:\n            reqtype = unpack(\"!B\", eap_packet[8:9])[0]\n            reqdata = eap_packet[9:4 + eap_len]\n            if reqtype == EAP_TYPE_ID:\n                display_prompt(Fore.YELLOW, 'Got EAP Request for identity')\n                self.send_response_id(id)\n                display_prompt(Fore.GREEN, 'Sending EAP response with identity = [%s]' % self.login_info['username'])\n            elif reqtype == EAP_TYPE_H3C:\n                display_prompt(Fore.YELLOW, 'Got EAP Request for Allocation')\n                self.send_response_h3c(id)\n                display_prompt(Fore.GREEN, 'Sending EAP response with password')\n            elif reqtype == EAP_TYPE_MD5:\n                data_len = unpack(\"!B\", reqdata[0:1])[0]\n                md5data = reqdata[1:1 + data_len]\n                display_prompt(Fore.YELLOW, 'Got EAP Request for MD5-Challenge')\n                self.send_response_md5(id, md5data)\n                display_prompt(Fore.GREEN, 'Sending EAP response with password')\n            else:\n                display_prompt(Fore.YELLOW, 'Got unknown Request type (%i)' % reqtype)\n        elif code==10 and id==5:\n            self.display_login_message(eap_packet[12:])\n        else:\n            display_prompt(Fore.YELLOW, 'Got unknown EAP code (%i)' % code)\n\n    def serve_forever(self):\n        try:\n            self.send_start()\n            while True:\n                eap_packet = self.client.recv(1600)\n\n                # strip the ethernet_header and handle\n                self.EAP_handler(eap_packet[14:])\n        except KeyboardInterrupt:\n            print Fore.RED + Style.BRIGHT + 'Interrupted by user' + Style.RESET_ALL\n            self.send_logoff()\n        except socket.error , msg:\n            print \"Connection error: %s\" %msg\n            exit(-1)\n\ndef daemonize (stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):\n\n    '''This forks the current process into a daemon. The stdin, stdout, and\n    stderr arguments are file names that will be opened and be used to replace\n    the standard file descriptors in sys.stdin, sys.stdout, and sys.stderr.\n    These arguments are optional and default to /dev/null. Note that stderr is\n    opened unbuffered, so if it shares a file with stdout then interleaved\n    output may not appear in the order that you expect. '''\n\n    # Do first fork.\n    try: \n        pid = os.fork() \n        if pid > 0:\n            sys.exit(0)   # Exit first parent.\n    except OSError, e: \n        sys.stderr.write (\"fork #1 failed: (%d) %s\\n\" % (e.errno, e.strerror) )\n        sys.exit(1)\n\n    # Decouple from parent environment.\n    os.chdir(\"/\") \n    os.umask(0) \n    os.setsid() \n\n    # Do second fork.\n    try: \n        pid = os.fork() \n        if pid > 0:\n            sys.exit(0)   # Exit second parent.\n    except OSError, e: \n        sys.stderr.write (\"fork #2 failed: (%d) %s\\n\" % (e.errno, e.strerror) )\n        sys.exit(1)\n\n    # Now I am a daemon!\n    \n    # Redirect standard file descriptors.\n    si = open(stdin, 'r')\n    so = open(stdout, 'a+')\n    se = open(stderr, 'a+', 0)\n    os.dup2(si.fileno(), sys.stdin.fileno())\n    os.dup2(so.fileno(), sys.stdout.fileno())\n    os.dup2(se.fileno(), sys.stderr.fileno())\n"
  },
  {
    "path": "yah3c/eappacket.py",
    "content": "from struct import *\n\n## Constants\n# Reference: http://tools.ietf.org/html/rfc3748\nETHERTYPE_PAE = 0x888e\nPAE_GROUP_ADDR = \"\\x01\\x80\\xc2\\x00\\x00\\x03\"\nBROADCAST_ADDR = \"\\xff\\xff\\xff\\xff\\xff\\xff\"\n\nEAPOL_VERSION = 1\nEAPOL_EAPPACKET = 0\n\n# packet info for EAPOL_EAPPACKET\nEAPOL_START = 1\nEAPOL_LOGOFF = 2\nEAPOL_KEY = 3\nEAPOL_ASF = 4\n\nEAP_REQUEST = 1\nEAP_RESPONSE = 2\nEAP_SUCCESS = 3\nEAP_FAILURE = 4\n\n# packet info followed by EAP_RESPONSE\n# 1       Identity\n# 2       Notification\n# 3       Nak (Response only)\n# 4       MD5-Challenge\n# 5       One Time Password (OTP)\n# 6       Generic Token Card (GTC)\n# 254     Expanded Types\n# 255     Experimental use\nEAP_TYPE_ID = 1                # identity\nEAP_TYPE_MD5 = 4               # md5 Challenge\nEAP_TYPE_H3C = 7               # H3C eap packet(used for SYSU east campus)\n\n### Packet builders\ndef get_EAPOL(type, payload=\"\"):\n    return pack(\"!BBH\", EAPOL_VERSION, type, len(payload))+payload\n\ndef get_EAP(code, id, type=0, data=\"\"):\n    if code in [EAP_SUCCESS, EAP_FAILURE]:\n        return pack(\"!BBH\", code, id, 4)\n    else:\n        return pack(\"!BBHB\", code, id, 5+len(data), type)+data\n\ndef get_ethernet_header(src, dst, type):\n    return dst+src+pack(\"!H\",type)\n"
  },
  {
    "path": "yah3c/usermgr.py",
    "content": "\"\"\" User Management Module\n\nThis module reads the 'users.conf' file and gets all users's info.\n\"\"\"\n\n__all__ = [\"UserMgr\"]\n\nimport ConfigParser\n\nclass UserMgr:\n    \"\"\"User Manager\n    The format of the user_info is:\n    user_info = {\n        \"username\": \"maple\",\n        \"password\": \"valley\",\n        \"ethernet_interface\": \"eth0\",\n        \"dhcp_command\": \"dhcpcd\",\n        \"daemon\": True,\n        # following has not implemented yet\n        \"carry_version_info\": True,\n        \"broadcast_logoff\": False\n        \"packet_type\": \"unicast\"\n    }\n    \"\"\"\n    def __init__(self, path=None):\n        if path is None:\n            self.users_cfg_path = '/etc/yah3c.conf'\n        else:\n            self.users_cfg_path = path\n        self.config = ConfigParser.ConfigParser()\n        self.config.read(self.users_cfg_path)\n\n    def save_and_reload(self):\n        fp = open(self.users_cfg_path, 'w')\n        self.config.write(fp)\n        fp.close()\n        self.config.read(self.users_cfg_path)\n       \n    def get_user_number(self):\n        return len(self.config.sections())\n\n    def get_all_users_info(self):\n        users_info = []\n        for username in self.config.sections():\n            user_info = dict(self.config.items(username))\n            user_info['username'] = username\n            users_info.append(user_info)\n\n        return users_info\n\n    def get_user_info(self, username):\n        user_info = dict(self.config.items(username))\n        user_info['username'] = username\n        return user_info\n    \n    def add_user(self, user_info):\n        self.config.add_section(user_info['username'])\n        self.update_user_info(user_info)\n\n    def remove_user(self, username):\n        self.config.remove_section(username)\n        self.save_and_reload()\n\n    def update_user_info(self, user_info):\n        self.config.set(user_info['username'], 'password',\n                        user_info['password'])\n        self.config.set(user_info['username'], 'ethernet_interface',\n                        user_info['ethernet_interface'])\n        self.config.set(user_info['username'], 'dhcp_command',\n                        user_info['dhcp_command'])\n        self.config.set(user_info['username'], 'daemon',\n                        user_info['daemon'])\n        self.save_and_reload()\n"
  },
  {
    "path": "yah3c/yah3c.py",
    "content": "#!/usr/bin/env python\n# -*- coding:utf-8 -*-\n\"\"\" Main program for YaH3C.\n\n\"\"\"\n\n__version__ = '0.5'\n\nimport os, sys\nimport ConfigParser\nimport getpass\nimport argparse\nimport logging\n\nimport eapauth\nimport usermgr\n\n\ndef parse_arguments():\n    parser = argparse.ArgumentParser(description='Yet Another H3C Authentication Client', prog='yah3c')\n    parser.add_argument('-u', '--username',\n            help='Login in with this username')\n    # parser.add_argument('-p', '--password',\n    #         help='Password')\n    # parser.add_argument('-i', '--interface', default='eth0',\n    #         help='Etherent interface used. Set as eth0 by default.')\n    # parser.add_argument('-d', '--daemon', action='store_true',\n    #         help='Fork to background after authentication.')\n    # parser.add_argument('-D', '--dhcp',\n    #         help='DHCP cmd used to obtain ip after authentication.')\n    parser.add_argument('-debug', action='store_true',\n            help='Enable debugging mode')\n    args = parser.parse_args()\n    return args\n\ndef prompt_user_info():\n    username = raw_input('Input username: ')\n    while True:\n        password = getpass.getpass('Input password: ')\n        password_again = getpass.getpass('Input again: ')\n        if password == password_again:\n            break\n        else:\n            print 'Password do not match!'\n\n    dev = raw_input('Decice(eth0 by default): ')\n    if not dev:\n        dev = 'eth0'\n\n    choice = raw_input('Forked to background after authentication(Yes by default)\\n<Y/N>: ')\n    if choice == 'n' or choice == 'N':\n        daemon = False\n    else:\n        daemon = True\n\n    dhcp_cmd = raw_input('Dhcp command(Press Enter to pass): ')\n    if not dhcp_cmd:\n        dhcp_cmd = ''\n    return {\n        'username': username,\n        'password': password,\n        'ethernet_interface': dev,\n        'daemon': daemon,\n        'dhcp_command': dhcp_cmd\n    }\n\ndef enter_interactive_usermanager():\n    um = usermgr.UserMgr()\n\n    if um.get_user_number() == 0:\n        choice = raw_input('No user conf file found, creat a new one?\\n<Y/N>: ')\n        if choice == 'y' or choice == 'Y': \n            login_info = prompt_user_info()\n            um.add_user(login_info)\n        else: \n            exit(-1)\n    \n    # user has been created or already have users\n    users_info = um.get_all_users_info()\n\n    print '0 - add a new user'\n    for i, user_info in enumerate(users_info):\n        print '%d - %s(%s)' %(i + 1, user_info['username'], user_info['ethernet_interface'])\n\n    while True:\n        try:\n            choice = int(raw_input('Your choice: '))\n        except ValueError:\n            print 'Please input a valid number!'\n        else: break;\n    if choice == 0:\n        try:\n            user_info = prompt_user_info()\n            um.add_user(user_info)\n        except ConfigParser.DuplicateSectionError:\n            print 'User already exist!'\n            exit(-1)\n    else: \n        return users_info[choice - 1]\n\ndef start_yah3c(login_info):\n    yah3c = eapauth.EAPAuth(login_info)\n    yah3c.serve_forever()\n\ndef main():\n    args = parse_arguments()\n    args = vars(args)\n\n    # check for root privilege\n    if not (os.getuid() == 0):\n        print (u'亲，要加sudo!')\n        exit(-1)\n\n    # check if debugging mode enabled\n    if args['debug'] is True:\n        logging.basicConfig(level=logging.DEBUG,\n                format='%(asctime)s %(levelname)s: %(message)s',\n                datefmt='%Y-%m-%d %H:%M:%S')\n        logging.debug('Debugging mode enabled.')\n        logging.debug(args)\n\n    # if no username specified then enter interactive mode\n    if args['username'] is None:\n        login_info = enter_interactive_usermanager()\n        logging.debug(login_info)\n        start_yah3c(login_info)\n\n    # if there is username, then get it's info\n    um = usermgr.UserMgr()\n    login_info = um.get_user_info(args['username'])\n    logging.debug(login_info)\n    start_yah3c(login_info)\n\nif __name__ == \"__main__\":\n    main()\n"
  }
]