├── supplicant ├── bin │ ├── conf.lua │ └── supplicant.lua ├── uninstall.sh ├── install.sh └── init.d │ └── supplicant ├── luamd5.tar.gz ├── img ├── alipay.png └── wechatpay.png └── README.md /supplicant/bin/conf.lua: -------------------------------------------------------------------------------- 1 | hello world! -------------------------------------------------------------------------------- /luamd5.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ok-dok/lua_supplicant/HEAD/luamd5.tar.gz -------------------------------------------------------------------------------- /img/alipay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ok-dok/lua_supplicant/HEAD/img/alipay.png -------------------------------------------------------------------------------- /img/wechatpay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ok-dok/lua_supplicant/HEAD/img/wechatpay.png -------------------------------------------------------------------------------- /supplicant/uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | HOME='/usr/share/supplicant' 4 | /etc/init.d/supplicant stop &> /dev/null 5 | ##删除定时任务、开机启动 6 | sed -i '/supplicant/d' /etc/crontabs/root 7 | rm -f /etc/rc.d/S95supplicant 8 | rm -f /etc/rc.d/K95supplicant 9 | rm -f /etc/init.d/supplicant 10 | if [ -d $HOME ]; then 11 | rm -rf $HOME 12 | fi 13 | echo "Uninstall success!" 14 | -------------------------------------------------------------------------------- /supplicant/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | home="/usr/share/supplicant" 3 | config_file="${home}/bin/conf.lua" 4 | authc_file="${home}/bin/authc.lua" 5 | 6 | install() { 7 | echo "Installing..." 8 | if [ ! -e ${home} ]; then 9 | mkdir ${home} 10 | fi 11 | cp -r ./ ${home} 12 | 13 | rm -f ${home}/install.sh 14 | cp ${home}/init.d/supplicant /etc/init.d/ 15 | chmod +x /etc/init.d/supplicant 16 | 17 | /etc/init.d/supplicant reload 18 | 19 | echo "Install success!" 20 | return 0 21 | } 22 | 23 | install 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenWrt版小蝴蝶拨号认证插件 2 | 3 | 特别声明:本插件仅供研究学习使用,不得用于获取商业利益。 4 | 5 | ## 说明 6 | 7 | 本客户端是一个非常热爱编程并且非常讨厌学校垃圾赛尔网的一枚毕业生写的!从上大学以来,饱受小蝴蝶折磨,不让接路由,不让换电脑,不能开Wifi,不能用虚拟机,一人一电脑一网线!什么鬼!最重要的是网速才TMD 2兆!2兆!2兆!够干嘛吃的,还死贵死贵!饱受折磨呀。。。 8 | 9 | 目前主流的openwrt系统的路由器,后台Web系统都是用Luci开发的!而Luci又是基于Lua的,也就是说,只要你的路由器后台是Luci开发的,按理说都是能够支持本客户端的! 10 | 11 | 那么,openwrt本身就有lua库,lua是基于c++的,通过lua来实现,既简单,又便于移植,还不用拖带python、java那样庞大的库!仅程序代码,小巧又方便! 12 | 13 | 好了,经过几个星期的研究学习,终于实现了lua的小蝴蝶拨号客户端,有了这个客户端,妈妈再也不用担心我的学习! 14 | 15 | ## 更新说明 16 | 17 | -------- 2018-4-10 -------- 18 | 19 | 1. 修复自动发现服务时存在可能无法拨号的bug 20 | 21 | 2. 更新readme 22 | 23 | -------- 2017-11-7 -------- 24 | 25 | 1. 可通过修改bin/conf.lua文件,配置host_ip,service 26 | 27 | 2. 修复城建学院的赛尔网搜索服务返回空值,导致无法获取service,拨号失败的问题,增加了默认service="int",如有不同,也可以通过第1项自行配置。 28 | 29 | 3. 无法获取service时,增加警告提示,使用默认service 30 | 31 | -------- 2017-9-23 -------- 32 | 33 | 1. 增加log指令,可通过`/etc/init.d/supplicant log`来查看拨号日志 34 | 35 | 2. 优化日志记录逻辑 36 | 37 | 3. 增加自动启动拨号,可通过`/etc/init.d/supplicant enable/disable`来启用自动拨号或关闭 38 | 39 | 4. 状态信息修改为显示程序运行情况和拨号情况 40 | 41 | ## 支持院校 42 | 43 | ### 测试通过 44 | 45 | 1. 烟台大学赛尔网 46 | 47 | 2. 广州大学华软软件学院 48 | 49 | 3. 安徽师范大学 50 | 51 | 4. 广州城建学院 52 | 53 | 5. 华南农业大学珠江学院 54 | 55 | ## 运行环境 56 | 57 | 安装有openwrt的路由器,包括但不限于 `Pandora Box` `Busy Box` 等。 58 | 59 | ## 重要提示 60 | 61 | `仅支持` 外网认证(BAS认证) 62 | `不支持` PPPOE、Web认证 63 | 64 | 内网认证请使用 mentohust 65 | 66 | ## 安装说明 67 | 68 | 1. 确认你能够使用ssh连接到openwrt路由器。 69 | 70 | ``` 71 | ssh -p 1022 root@192.168.1.1 72 | ``` 73 | 74 | 2. 将源码中的supplicant目录打包supplicant.tar.gz并上传到路由器/root目录。命令如下: 75 | 76 | ``` 77 | scp -P 1022 supplicant.tar.gz root@192.168.1.1:/root 78 | ``` 79 | 80 | -P 后面是路由器ssh端口号,一般是1022或者22, root@后面是你的路由器ip地址。如果你的路由器地址和端口和上面命令不同,请修改成你自己的。 81 | 82 | 3. ssh连接到路由器,解压,执行: 83 | 84 | ``` 85 | tar -zxvf supplicant.tar.gz 86 | cd supplicant 87 | ls 88 | ``` 89 | 90 | 可以看到 install.sh ,这个就是安装脚本,停!先别着急进行安装!在执行安装之前还有些注意事项,总有些傻瓜不知道,我还是强调一下吧: 91 | 92 | 首先,你要把校园网的网线插在路由器的Wan口!!! 93 | 94 | 首先,你要把校园网的网线插在路由器的Wan口!!! 95 | 96 | 首先,你要把校园网的网线插在路由器的Wan口!!! 97 | 98 | 重要的事情说三遍,嘻嘻! 99 | 100 | 其次,登录你的路由器后台(什么?怎么登录路由器后台地址还要我教?),进入系统后台后修改路由器 Wan口 Mac地址(也就是克隆Mac地址),把你用小蝴蝶拨号的那台电脑上的Mac地址复制过来粘贴上!Mac地址在小蝴蝶拨号客户端里面能找到。 101 | 102 | 接着,如果你学校的赛尔网支持DHCP(ip自动获取),那么修改上网方式为自动上网(有的路由器不叫自动上网,反正不能是PPPOE拨号);如果不支持DHCP,请修改上网方式为静态IP方式上网,按照开通赛尔网时给你的IP地址,子网掩码,默认网关,DNS等通通填好。 103 | 104 | 接下来执行安装: 105 | 106 | ``` 107 | sh install.sh 108 | ``` 109 | 110 | 按照提示输入用户名(Username)、密码(Password),对了,还有可能让你输入Mac地址(Mac Addr),你会疑惑了,为甚么上面我克隆了Mac地址,怎么还让我输入Mac Addr?哦,好吧,我来告诉你: 111 | 112 | 程序本身是会自动获取你克隆的Mac地址的! 113 | 114 | 程序本身是会自动获取你克隆的Mac地址的! 115 | 116 | 程序本身是会自动获取你克隆的Mac地址的! 117 | 118 | 但这好像也并不能排除获取出错导致没获取到Mac地址的情况?发生错误啦!好吧,那你就按照提示输入Mac地址呗!除了让你输入我还能怎么办(你来告诉我,我保证不打死你)? 119 | 120 | 4. 输入完成后终于提示 Install Success! 那么恭喜你了!接下来就是最鸡冻人心的时刻了: 121 | 122 | ``` 123 | root@Hiwifi:~# /etc/init.d/supplicant 124 | Syntax: /etc/init.d/supplicant [command] 125 | 126 | Available commands: 127 | start Start the service 128 | stop Stop the service 129 | restart Restart the service 130 | reload Reload configuration files (or restart if that fails) 131 | enable Enable service autostart 132 | disable Disable service autostart 133 | status Display the service's status 134 | log Show supplicant's log 135 | ``` 136 | 看到了吧? 执行 /etc/init.d/supplicant 就可以看到支持的命令咯!翻译一下: 137 | - start: 启动拨号上网 138 | - stop: 停止拨号上网(这不是有病嘛?我拿来不就是上网的,干嘛要停止?) 139 | - restart: 重启拨号 140 | - reload: 重新加载配置文件,会重新让你输入用户名、密码,当然还有可能让你输入Mac地址 141 | - enable: 允许小蝴蝶自动启动 142 | - disable: 不允许小蝴蝶自动启动 143 | - status: 查看程序拨号状态信息 144 | - log: 查看拨号日志, 拨号失败了来这里查看状态日志,找找原因! 145 | 146 | 那么开始上网吧! 147 | 148 | ## 配置说明 149 | 关于conf.lua的配置说明如下(当且仅当程序拨号获取不到配置或者获取错误时可修改,命令 /etc/init.d/supplicant reload 会更新此配置): 150 | 151 | ``` 152 | dhcp='0' #是否支持dhcp,0--不支持,1--支持,默认为0,可无需改动 153 | version='3.8.2' #小蝴蝶认证版本(默认3.8.2),如出现版本号不对导致无法拨号的情况,请修改为学校小蝴蝶的版本号 154 | service='int' #internet服务(自动获取),一般是int/internet 155 | host_ip='219.218.154.250' #服务器ip地址(自动获取) 156 | ip='180.201.54.232' #本地ip地址(自动获取) 157 | mac_addr='00:90:F5:F7:39:B0' #网卡的mac地址(自动获取) 158 | ``` 159 | 160 | authc.lua配置说明(此文件是在安装时根据输入的用户名和密码生成,可无需改动,如果要重新配置,请使用命令 /etc/init.d/supplicant reload ): 161 | 162 | ``` 163 | username='201358501113' #用户名 164 | password='8888' #密码 165 | ``` 166 | 167 | ## 彩蛋 168 | 如果你安装本客户端后却不能够成功拨号上网,请查看状态日志,找一下原因,如果是配置信息不正确导致的,可以直接到程序目录修改配置文件的哦!程序安装目录在/usr/share/supplicant,bin目录是主程序及配置文件所在,authc.lua存储了你的用户名和密码,conf.lua存储了一些配置信息,包括Mac地址,版本号,dhcp配置等,如果你发现这些信息有可能是不正确的,那么你可以直接修改这些信息。 169 | 如果是ip地址不正确(一般不会出现),ip地址不包含在配置文件中,是程序运行中自动获取的,因此你应该去修改Wan口网卡的ip地址。 170 | 171 | 如果安装不成功,提示缺少md5.lua,在本项目目录,你可以发现一个luamd5.tar.gz的压缩包,这是从我自己极路由1s上提取出来的,你可以尝试一下将其上传到路由器,解压到/usr/lib/lua目录下,再次执行安装,如果依然不能成功,提示md5不能正确识别或不能加载,那可能是这个md5不适合你的系统,请去百度下载安装符合你系统的md5模块,或者自行编译? 172 | 173 | ## 问题交流 174 | 175 | QQ群:`623929715` 176 | 177 | ## 特别感谢 178 | 感谢 [xingrz](https://github.com/xingrz/swiftz-protocal "xingrz/swiftz-protocal") 提供的协议。 179 | 感谢 [HinsYang](https://github.com/HinsYang "HinsYang's GitHub") 提交的bug。 180 | 感谢各位帮助测试的童鞋! 181 | 182 | ## 特别支持 183 | 如果你成功使用本软件客户端拨号了,那么首先要恭喜你!记得回来点击页面右上角的 `star` 按钮,给我点亮一颗小星星吧!另外呢,如果你不嫌弃的话,可以给我打赏哟!一块不嫌少,一百块不嫌多,赚点打赏费,更新维护才更有动力嘛!支持微信和支付宝! 184 | 185 | ![支付宝](https://github.com/shawn-hou/lua_supplicant/raw/master/img/alipay.png) 186 | ![微信](https://github.com/shawn-hou/lua_supplicant/raw/master/img/wechatpay.png) 187 | -------------------------------------------------------------------------------- /supplicant/init.d/supplicant: -------------------------------------------------------------------------------- 1 | #!/bin/sh /etc/rc.common 2 | #chkconfig:2345 80 50 3 | #description:supplicant service 4 | 5 | START=95 6 | STOP=95 7 | 8 | home="/usr/share/supplicant" 9 | config_file="${home}/bin/conf.lua" 10 | authc_file="${home}/bin/authc.lua" 11 | prog="${home}/bin/supplicant.lua" 12 | script_running="${home}/supplicant.pid" 13 | connect_status="${home}/supplicant.status" 14 | info_log="${home}/info.log" 15 | error_log="${home}/error.log" 16 | 17 | EXTRA_COMMANDS="status log" 18 | EXTRA_HELP=" status Display the service's status 19 | log Show supplicant's log" 20 | 21 | start(){ 22 | echo -n "Starting..." 23 | lua ${prog} 1> /dev/null 2> ${home}/error.log & pid=$! 24 | echo ${pid} > ${script_running} 25 | 26 | local info=`cat ${info_log} 2> /dev/null` 27 | local error=`cat ${error_log} 2> /dev/null` 28 | 29 | if [ "${pid}" != "" ]; then 30 | kill -0 ${pid} &> /dev/null 31 | if [ "$?" == "0" ]; then 32 | echo -e "[ \033[32mOK\033[0m ]" 33 | echo "Use command '/etc/init.d/supplicant status' to see status." 34 | return 0 35 | fi 36 | fi 37 | echo -e "[ \033[31mFailed\033[0m ]" 38 | echo -e "${info}\n${error}" 39 | return 0 40 | } 41 | 42 | stop() { 43 | if [ ! -e ${script_running} ]; then 44 | return 0 45 | fi 46 | 47 | echo -n "Stopping..." 48 | local spid=`cat ${script_running} 2> /dev/null` 49 | if [ "${spid}" != "" ]; then 50 | kill -9 ${spid} &> /dev/null 51 | rm ${script_running} 52 | fi 53 | echo -e "[ \033[32mOK\033[0m ]" 54 | return 0 55 | } 56 | 57 | status() { 58 | local cur_date=`date "+%Y-%m-%d %H:%M:%S"` 59 | local running=-1 60 | if [ -e ${script_running} ]; then 61 | local spid=`cat ${script_running}` 62 | if [ "${spid}" != "" ]; then 63 | kill -0 ${spid} &> /dev/null 64 | if [ "$?" == "0" ]; then 65 | running=0 66 | fi 67 | fi 68 | fi 69 | local status=`cat ${connect_status} 2> /dev/null` 70 | if [ "${status}" == "" ]; then 71 | status="offline" 72 | fi 73 | if [ "${status}" == "offline" ]; then 74 | status="\033[31m${status}\033[0m" 75 | else 76 | status="\033[32m${status}\033[0m" 77 | fi 78 | 79 | local error="`cat ${error_log} 2> /dev/null`" 80 | local program="" 81 | if [ ${running} == -1 ]; then 82 | rm ${script_running} &> /dev/null 83 | program="\033[31mstopped\033[0m" 84 | else 85 | program="\033[32mrunning\033[0m" 86 | fi 87 | echo -e "program\t:\t${program}\nstatus\t:\t${status}\ntime\t:\t${cur_date}" 88 | if [ "${error}" != "" ]; then 89 | echo -e "\n\033[31m${error}\033[0m" 90 | fi 91 | 92 | } 93 | 94 | log() { 95 | if [ -e ${info_log} ]; then 96 | cp ${info_log} ${info_log}.color 97 | sed -i 's/\[error\]/\\033[31m[error]\\033[0m/' ${info_log}.color 98 | sed -i 's/\[ info\]/\\033[32m[ info]\\033[0m/' ${info_log}.color 99 | sed -i 's/\[ warn\]/\\033[33m[ warn]\\033[0m/' ${info_log}.color 100 | local info="`cat ${info_log}.color 2> /dev/null`" 101 | if [ "${info}" != "" ]; then 102 | echo -e "${info}" 103 | fi 104 | rm -f ${info_log}.color 105 | fi 106 | } 107 | 108 | reload() { 109 | 110 | if [ -e ${script_running} ]; then 111 | stop 112 | fi 113 | echo "Configuring..." 114 | rm -f ${config_file} 115 | rm -f ${authc_file} 116 | local username="" 117 | while [ "$username" == "" ] 118 | do 119 | echo -n "Username: " 120 | read username 121 | done 122 | local password="" 123 | while [ "$password" == "" ] 124 | do 125 | echo -n "Password: " 126 | read password 127 | done 128 | 129 | local mac_addr=$(uci get network.wan.macaddr 2> /dev/null) 130 | while [ "$mac_addr" == "" ] 131 | do 132 | echo -n "Mac Addr: " 133 | read mac_addr 134 | done 135 | 136 | local dhcp='0' 137 | local version='3.8.2' 138 | echo "#!/usr/bin/lua" > $config_file 139 | echo "dhcp='$dhcp'" >> $config_file 140 | echo "version='$version'" >> $config_file 141 | echo "mac_addr='$mac_addr'" >> $config_file 142 | 143 | echo "#!/usr/bin/lua" > $authc_file 144 | echo "username='$username'" >> $authc_file 145 | echo "password='$password'" >> $authc_file 146 | enable 147 | if [ -e ${config_file} ] && [ -e ${authc_file} ]; then 148 | echo "Configure success!" 149 | start 150 | return 0 151 | else 152 | echo "Configure failed!" 153 | return -1 154 | fi 155 | } 156 | 157 | restart() { 158 | stop 159 | sleep 1 160 | start 161 | } 162 | 163 | enable() { 164 | ln -sf /etc/init.d/supplicant /etc/rc.d/S95supplicant 165 | ln -sf /etc/init.d/supplicant /etc/rc.d/K95supplicant 166 | local autostart=`cat ${config_file} | grep autostart | awk -F '=' '{print $2}' | sed s/[[:space:]]//g` 167 | if [ "$autostart" != "" ]; then 168 | sed -i -e '/autostart/d' $config_file 169 | fi 170 | echo "autostart=true" >> $config_file 171 | echo "0 6 * * * /etc/init.d/supplicant restart" >> /etc/crontabs/root 172 | echo "0 7 * * * /etc/init.d/supplicant restart" >> /etc/crontabs/root 173 | } 174 | disable() { 175 | if [ "$autostart" != "" ]; then 176 | sed -i -e '/autostart/d' $config_file 177 | fi 178 | echo "autostart=false" >> $config_file 179 | rm /etc/rc.d/S95supplicant 180 | rm /etc/rc.d/K95supplicant 181 | sed -i '/supplicant/d' /etc/crontabs/root 182 | } 183 | case "$1" in 184 | start) 185 | start 186 | exit 0 187 | ;; 188 | stop) 189 | stop 190 | exit 0 191 | ;; 192 | restart) 193 | restart 194 | exit 0 195 | ;; 196 | status) 197 | status 198 | exit 0 199 | ;; 200 | enable) 201 | enable 202 | exit 0 203 | ;; 204 | disable) 205 | disable 206 | exit 0 207 | ;; 208 | reload) 209 | reload 210 | exit 0 211 | ;; 212 | log) 213 | log 214 | exit 0 215 | ;; 216 | esac 217 | -------------------------------------------------------------------------------- /supplicant/bin/supplicant.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | nixio = require "nixio" 3 | bit = nixio.bit 4 | md5 = require "md5" 5 | --iconv = require "iconv" 6 | log = {} 7 | 8 | function string.split(input, delimiter) 9 | local input = tostring(input) 10 | local delimiter = tostring(delimiter) 11 | if (delimiter=='') then return false end 12 | local pos = 0 13 | local arr = {} 14 | -- for each divider found 15 | for st,sp in function() return string.find(input,delimiter,pos,true) end do 16 | table.insert(arr,string.sub(input,pos,st-1)) 17 | pos = sp+1 18 | end 19 | table.insert(arr, string.sub(input, pos)) 20 | return arr 21 | end 22 | 23 | function string.trim(str) 24 | return (str:gsub("^%s*(.-)%s*$", "%1")) 25 | end 26 | 27 | function string.isNilOrEmpty(str) 28 | if(type(str) == "nil") then 29 | return true 30 | elseif(type(str)== "string") then 31 | if(string.trim(str) == "") then 32 | return true 33 | else 34 | return false 35 | end 36 | else 37 | return true 38 | end 39 | end 40 | 41 | --查找指定数组中的某个元素的位置,如找到则返回索引位置,未找到返回nil 42 | function table.find(_table,_value,_begin,_end) 43 | local _begin = _begin or 1 44 | local _end = _end or #(_table) 45 | for k,v in ipairs(_table) do 46 | if(k >= _begin and k <= _end and v == _value) then 47 | return k 48 | end 49 | end 50 | return nil 51 | end 52 | 53 | --对指定数组进行截取,从指定开始位置到结束位置,返回截取后的数组子集 54 | function table.sub(_table,_begin,_end) 55 | local _begin = _begin or 1 56 | local _end = _end or #(_table) 57 | local subtable = {} 58 | local k = 1 59 | for i=_begin,_end do 60 | subtable[k] = _table[i] 61 | k = k + 1 62 | end 63 | return subtable 64 | end 65 | 66 | function sleep(n) 67 | os.execute("sleep " .. n) 68 | end 69 | 70 | function log.warn(msg) 71 | log.msg(" [ warn] "..msg) 72 | end 73 | 74 | function log.info(msg) 75 | log.msg(" [ info] "..msg); 76 | end 77 | 78 | function log.error(msg) 79 | log.msg(" [error] "..msg); 80 | end 81 | 82 | function log.printStatus(msg) 83 | local file = io.open(status_file, "w") 84 | file:write(msg) 85 | file:close() 86 | end 87 | 88 | function log.msg(msg) 89 | local logfile = io.open(log_file, "a") 90 | logfile:write(os.date("%m/%d %X",os.time())..msg.."\n") 91 | logfile:close() 92 | end 93 | 94 | function encrypt(buffer) 95 | for i,v in ipairs(buffer) do 96 | buffer[i] = bit.bor(bit.rshift(bit.band(buffer[i], 0x80),6), 97 | bit.rshift(bit.band(buffer[i], 0x40),4), 98 | bit.rshift(bit.band(buffer[i], 0x20),2), 99 | bit.lshift(bit.band(buffer[i], 0x10),2), 100 | bit.lshift(bit.band(buffer[i], 0x08),2), 101 | bit.lshift(bit.band(buffer[i], 0x04),2), 102 | bit.rshift(bit.band(buffer[i], 0x02),1), 103 | bit.lshift(bit.band(buffer[i], 0x01),7)) 104 | end 105 | end 106 | 107 | function decrypt(buffer) 108 | for i,v in ipairs(buffer) do 109 | buffer[i] = bit.bor(bit.rshift(bit.band(buffer[i], 0x80),7), 110 | bit.rshift(bit.band(buffer[i], 0x40),2), 111 | bit.rshift(bit.band(buffer[i], 0x20),2), 112 | bit.rshift(bit.band(buffer[i], 0x10),2), 113 | bit.lshift(bit.band(buffer[i], 0x08),2), 114 | bit.lshift(bit.band(buffer[i], 0x04),4), 115 | bit.lshift(bit.band(buffer[i], 0x02),6), 116 | bit.lshift(bit.band(buffer[i], 0x01),1)) 117 | end 118 | end 119 | 120 | function search_service(mac_addr) 121 | local packet_len = 1 + 1 + 16 + 1 + 1 + 5 + 1 + 1 + 6 122 | local packet = {} 123 | table.insert(packet, 0x07) 124 | table.insert(packet, packet_len) 125 | for i=1,16 do 126 | table.insert(packet, 0) 127 | end 128 | table.insert(packet, 0x08) 129 | table.insert(packet, 0x07) 130 | for i=0,4 do 131 | table.insert(packet,i) 132 | end 133 | table.insert(packet, 0x07); 134 | table.insert(packet, 0x08); 135 | for k,v in ipairs(string.split(mac_addr,':')) do 136 | table.insert(packet,string.format("%d","0x"..v)) 137 | end 138 | 139 | --将packet内容由整型转换为字节 140 | local bpacket = {} 141 | for k,v in ipairs(packet) do 142 | table.insert(bpacket,string.char(v)) 143 | end 144 | --摘要计算校验和 145 | 146 | local md5str = md5.sumhexa(table.concat(bpacket)) 147 | --将校验和加入到packet[3..18] 148 | for i=1,string.len(md5str) do 149 | if(i%2==0) then 150 | packet[i/2+2] = string.format("%d","0x"..string.sub(md5str,i-1,i)) 151 | end 152 | end 153 | 154 | encrypt(packet); 155 | 156 | local bpacket = {} 157 | for k,v in ipairs(packet) do 158 | table.insert(bpacket,string.char(v)) 159 | end 160 | 161 | local recv_msg = send_recv(table.concat(bpacket)) 162 | if(not recv_msg) then return nil end 163 | 164 | local recv_packet = {} 165 | for i=1,string.len(recv_msg) do 166 | recv_packet[i] = string.byte(string.sub(recv_msg,i,i)) 167 | end 168 | 169 | decrypt(recv_packet) 170 | if(check_md5(recv_packet)) then 171 | --取出服务内容 172 | local service_index = table.find(recv_packet, 0x0a) 173 | local service_len = recv_packet[service_index + 1]; 174 | local service_type = table.sub(recv_packet, service_index + 2, service_len + service_index - 1) 175 | local service = {} 176 | for k,v in ipairs(service_type) do 177 | table.insert(service,string.char(v)) 178 | end 179 | local service_str = table.concat(service) 180 | return service_str; 181 | else 182 | return search_service(mac_addr) 183 | end 184 | 185 | end 186 | 187 | function search_server_ip(mac_addr, ip) 188 | local packet_len = 1 + 1 + 16 + 1 + 1 + 5 + 1 + 1 + 16 + 1 + 1 + 6; 189 | local packet = {}; 190 | 191 | table.insert(packet, 0x0c) 192 | table.insert(packet, packet_len) 193 | for i=1,16 do 194 | table.insert(packet, 0x00) 195 | end 196 | 197 | table.insert(packet, 0x08) 198 | table.insert(packet, 0x07) 199 | for i=0,4 do 200 | table.insert(packet,i) 201 | end 202 | 203 | table.insert(packet, 0x09) 204 | table.insert(packet, 0x12) 205 | for i=1,string.len(ip) do 206 | table.insert(packet,string.byte(string.sub(ip,i,i))) 207 | end 208 | for i=1,16-string.len(ip) do 209 | table.insert(packet, 0x00) 210 | end 211 | 212 | table.insert(packet, 0x07) 213 | table.insert(packet, 0x08) 214 | for k,v in ipairs(string.split(mac_addr,':')) do 215 | table.insert(packet,string.format("%d","0x"..v)) 216 | end 217 | 218 | --将packet内容由整型转换为字节 219 | local bpacket = {} 220 | for k,v in ipairs(packet) do 221 | table.insert(bpacket,string.char(v)) 222 | end 223 | --摘要计算校验和 224 | 225 | local md5str = md5.sumhexa(table.concat(bpacket)) 226 | --将校验和加入到packet[3..18] 227 | for i=1,string.len(md5str) do 228 | if(i%2==0) then 229 | packet[i/2+2] = string.format("%d","0x"..string.sub(md5str,i-1,i)) 230 | end 231 | end 232 | 233 | encrypt(packet); 234 | 235 | local bpacket = {} 236 | for k,v in ipairs(packet) do 237 | table.insert(bpacket,string.char(v)) 238 | end 239 | 240 | local recv_msg = send_recv(table.concat(bpacket)) 241 | if(not recv_msg) then return nil end 242 | 243 | local recv_packet = {} 244 | for i=1,string.len(recv_msg) do 245 | recv_packet[i] = string.byte(string.sub(recv_msg,i,i)) 246 | end 247 | 248 | decrypt(recv_packet) 249 | if(check_md5(recv_packet)) then 250 | --取出服务器ip 251 | local server_index = table.find(recv_packet, 0x0c) 252 | local server_len = recv_packet[server_index + 1]; 253 | local server_ip = table.sub(recv_packet, server_index + 2, server_index + server_len - 1) 254 | local host_ip = "" 255 | for k,v in ipairs(server_ip) do 256 | host_ip = host_ip..tostring(v).."." 257 | end 258 | host_ip = string.sub(host_ip,1,string.len(host_ip)-1) 259 | return host_ip; 260 | else 261 | return search_server_ip(mac_addr, ip) 262 | end 263 | 264 | end 265 | 266 | function generate_login(mac_addr, ip, user, pwd, dhcp, service, version) 267 | local packet = {} 268 | table.insert(packet, 0x01) -- 1 请求上线 269 | packet_len = string.len(user) + 2 + 270 | string.len(pwd) + 2 + 271 | string.len(ip) + 2 + 272 | string.len(service) + 2 + 273 | string.len(dhcp) + 2 + 274 | string.len(version) + 2 + 275 | 16 + 2 + 276 | 6 + 2 277 | table.insert(packet,packet_len) 278 | for i=1,16 do 279 | table.insert(packet, 0x00) 280 | end 281 | 282 | table.insert(packet, 0x07) 283 | table.insert(packet, 0x08) 284 | for k,v in ipairs(string.split(mac_addr,':')) do 285 | table.insert(packet,string.format("%d","0x"..v)) 286 | end 287 | 288 | table.insert(packet, 0x01) 289 | table.insert(packet,string.len(user) + 2) 290 | for i=1,string.len(user) do 291 | table.insert(packet,string.byte(string.sub(user,i,i))) 292 | end 293 | 294 | table.insert(packet, 0x02) 295 | table.insert(packet,string.len(pwd) + 2) 296 | for i=1,string.len(pwd) do 297 | table.insert(packet,string.byte(string.sub(pwd,i,i))) 298 | end 299 | 300 | table.insert(packet, 0x09) 301 | table.insert(packet,string.len(ip) + 2) 302 | for i=1,string.len(ip) do 303 | table.insert(packet,string.byte(string.sub(ip,i,i))) 304 | end 305 | 306 | table.insert(packet, 0x0a) 307 | table.insert(packet, string.len(service) + 2) 308 | for i=1,string.len(service) do 309 | table.insert(packet,string.byte(string.sub(service,i,i))) 310 | end 311 | 312 | table.insert(packet, 0x0e) 313 | table.insert(packet, string.len(dhcp) + 2) 314 | for i=1,string.len(dhcp) do 315 | table.insert(packet,tonumber(string.sub(dhcp,i,i))) 316 | end 317 | 318 | table.insert(packet, 0x1f) 319 | table.insert(packet, string.len(version) + 2) 320 | for i=1,string.len(version) do 321 | table.insert(packet,string.byte(string.sub(version,i,i))) 322 | end 323 | 324 | --将packet内容由整型转换为字节 325 | local bpacket = {} 326 | for k,v in ipairs(packet) do 327 | table.insert(bpacket,string.char(v)) 328 | end 329 | --摘要计算校验和 330 | 331 | local md5str = md5.sumhexa(table.concat(bpacket)) 332 | --将校验和加入到packet[3..18] 333 | for i=1,string.len(md5str) do 334 | if(i%2==0) then 335 | packet[i/2+2] = string.format("%d","0x"..string.sub(md5str,i-1,i)) 336 | end 337 | end 338 | 339 | encrypt(packet) 340 | 341 | local bpacket = {} 342 | for k,v in ipairs(packet) do 343 | table.insert(bpacket,string.char(v)) 344 | end 345 | return table.concat(bpacket) 346 | end 347 | 348 | function login(packet) 349 | local recv_msg = send_recv(packet) 350 | if(not recv_msg) then 351 | net_status = -2 352 | return nil 353 | end 354 | local recv_packet = {} 355 | for i=1,string.len(recv_msg) do 356 | recv_packet[i] = string.byte(string.sub(recv_msg,i,i)) 357 | end 358 | 359 | decrypt(recv_packet) 360 | --md5校验 361 | if(check_md5(recv_packet)) then 362 | status = recv_packet[21] 363 | session_len = recv_packet[23] 364 | session = table.sub(recv_packet, 24, session_len + 24 - 1) 365 | pos = table.find(recv_packet, 0x0b, session_len + 24) 366 | message_len = recv_packet[pos + 1] 367 | message = table.sub(recv_packet, pos + 2, message_len + pos + 2 - 1) 368 | msg = {} 369 | for k,v in ipairs(message) do 370 | table.insert(msg,string.char(v)) 371 | end 372 | msg_str = table.concat(msg) 373 | --trans = iconv.new("utf-8","gbk") 374 | --msg_str = trans:iconv(msg_str) 375 | --log(msg_str) 376 | if(status==0) then 377 | --认证出错,可能是用户名密码错误,也可能是不在上网时段, 378 | --或者不是有效用户,或者被管理员禁止认证 379 | --具体原因在msg_str中给出,但需要gbk解码 380 | net_status = -3 381 | return nil 382 | else 383 | net_status = 1 384 | return session 385 | end 386 | else 387 | net_status = -1 388 | return nil 389 | end 390 | 391 | end 392 | 393 | function generate_breathe(mac_addr, ip, session, index) 394 | index = string.format("%x",index) 395 | local packet = {} 396 | table.insert(packet, 0x03) --3 保持在线 5 请求下线 1 请求上线 397 | local packet_len = #(session) + 88 398 | table.insert(packet, packet_len) 399 | for i=1,16 do 400 | table.insert(packet, 0x00) 401 | end 402 | table.insert(packet, 0x08) 403 | table.insert(packet, #(session) + 2) 404 | for k,v in ipairs(session) do 405 | table.insert(packet, v) 406 | end 407 | table.insert(packet, 0x09) 408 | table.insert(packet, 0x12) 409 | for i=1,string.len(ip) do 410 | table.insert(packet,string.byte(string.sub(ip,i,i))) 411 | end 412 | for i=1,16-string.len(ip) do 413 | table.insert(packet,0x00) 414 | end 415 | table.insert(packet, 0x07) 416 | table.insert(packet, 0x08) 417 | for k,v in ipairs(string.split(mac_addr,':')) do 418 | table.insert(packet,string.format("%d","0x"..v)) 419 | end 420 | table.insert(packet, 0x14) 421 | table.insert(packet, 0x06) 422 | 423 | local len = string.len(index) 424 | table.insert(packet,string.format("%d","0x"..string.sub(index,len-7,len-6))) 425 | table.insert(packet,string.format("%d","0x"..string.sub(index,len-5,len-4))) 426 | table.insert(packet,string.format("%d","0x"..string.sub(index,len-3,len-2))) 427 | table.insert(packet,string.format("%d","0x"..string.sub(index,len-1,len-0))) 428 | 429 | local block = { 0x2a, 0x06, 0, 0, 0, 0, 430 | 0x2b, 0x06, 0, 0, 0, 0, 431 | 0x2c, 0x06, 0, 0, 0, 0, 432 | 0x2d, 0x06, 0, 0, 0, 0, 433 | 0x2e, 0x06, 0, 0, 0, 0, 434 | 0x2f, 0x06, 0, 0, 0, 0} 435 | 436 | for k,v in ipairs(block) do 437 | table.insert(packet, v) 438 | end 439 | 440 | --将packet内容由整型转换为字节 441 | local bpacket = {} 442 | for k,v in ipairs(packet) do 443 | table.insert(bpacket,string.char(v)) 444 | end 445 | --摘要计算校验和 446 | 447 | local md5str = md5.sumhexa(table.concat(bpacket)) 448 | --将校验和加入到packet[3..18] 449 | for i=1,string.len(md5str) do 450 | if(i%2==0) then 451 | packet[i/2+2] = string.format("%d","0x"..string.sub(md5str,i-1,i)) 452 | end 453 | end 454 | 455 | encrypt(packet) 456 | 457 | --for k,v in ipairs(packet) do io.write(v,', ') end 458 | 459 | local bpacket = {} 460 | for k,v in ipairs(packet) do 461 | table.insert(bpacket,string.char(v)) 462 | end 463 | return table.concat(bpacket) 464 | end 465 | 466 | function breathe(mac_addr, ip, session, index) 467 | sleep(20) 468 | md5err_cnt = 0 469 | while(true) do 470 | local breathe_packet = generate_breathe(mac_addr, ip, session, index) 471 | 472 | local recv_msg = send_recv(breathe_packet) 473 | if(not recv_msg) then 474 | net_status = -4 475 | return nil 476 | end 477 | 478 | local recv_packet = {} 479 | for i=1,string.len(recv_msg) do 480 | recv_packet[i] = string.byte(string.sub(recv_msg,i,i)) 481 | end 482 | decrypt(recv_packet) 483 | if(check_md5(recv_packet)) then 484 | status = recv_packet[21] 485 | if status == 1 then 486 | --在线 487 | net_status = 1 488 | else 489 | --呼吸出错 490 | net_status = -6 491 | md5err_cnt = md5err_cnt + 1 492 | if(md5err_cnt >= 3) then 493 | return 494 | end 495 | end 496 | else 497 | net_status = -5 498 | end 499 | index = index + 3 500 | sleep(20) 501 | end 502 | end 503 | 504 | function generate_logout(mac_addr, ip, session, index) 505 | index = string.format("%x",index) 506 | local packet = {} 507 | table.insert(packet, 0x05) -- 5 请求下线 3 保持在线 1 请求上线 508 | local packet_len = #(session) + 88 509 | table.insert(packet, packet_len) 510 | for i=1,16 do 511 | table.insert(packet, 0x00) 512 | end 513 | table.insert(packet, 0x08) 514 | table.insert(packet, #(session) + 2) 515 | for k,v in ipairs(session) do 516 | table.insert(packet, v) 517 | end 518 | table.insert(packet, 0x09) 519 | table.insert(packet, 0x12) 520 | for i=1,string.len(ip) do 521 | table.insert(packet,string.byte(string.sub(ip,i,i))) 522 | end 523 | for i=1,16-string.len(ip) do 524 | table.insert(packet, 0x00) 525 | end 526 | table.insert(packet, 0x07) 527 | table.insert(packet, 0x08) 528 | for k,v in ipairs(string.split(mac_addr,':')) do 529 | table.insert(packet,string.format("%d","0x"..v)) 530 | end 531 | table.insert(packet, 0x14) 532 | table.insert(packet, 0x06) 533 | 534 | local len = string.len(index) 535 | table.insert(packet,string.format("%d","0x"..string.sub(index,len-7,len-6))) 536 | table.insert(packet,string.format("%d","0x"..string.sub(index,len-5,len-4))) 537 | table.insert(packet,string.format("%d","0x"..string.sub(index,len-3,len-2))) 538 | table.insert(packet,string.format("%d","0x"..string.sub(index,len-1,len-0))) 539 | 540 | 541 | local block = { 0x2a, 0x06, 0, 0, 0, 0, 542 | 0x2b, 0x06, 0, 0, 0, 0, 543 | 0x2c, 0x06, 0, 0, 0, 0, 544 | 0x2d, 0x06, 0, 0, 0, 0, 545 | 0x2e, 0x06, 0, 0, 0, 0, 546 | 0x2f, 0x06, 0, 0, 0, 0} 547 | 548 | for k,v in ipairs(block) do 549 | table.insert(packet, v) 550 | end 551 | 552 | --将packet内容由整型转换为字节 553 | local bpacket = {} 554 | for k,v in ipairs(packet) do 555 | table.insert(bpacket,string.char(v)) 556 | end 557 | --摘要计算校验和 558 | 559 | local md5str = md5.sumhexa(table.concat(bpacket)) 560 | --将校验和加入到packet[3..18] 561 | for i=1,string.len(md5str) do 562 | if(i%2==0) then 563 | packet[i/2+2] = string.format("%d","0x"..string.sub(md5str,i-1,i)) 564 | end 565 | end 566 | 567 | encrypt(packet) 568 | 569 | local bpacket = {} 570 | for k,v in ipairs(packet) do 571 | table.insert(bpacket,string.char(v)) 572 | end 573 | return table.concat(bpacket) 574 | end 575 | 576 | function logout(mac_addr, ip, session, index) 577 | index = index + 3 578 | logout_packet = generate_logout(mac_addr, ip, session, index) 579 | send(logout_packet) 580 | local recv_msg = receive() 581 | net_status = 0 --下线 582 | end 583 | 584 | --接收报文 585 | function receive() 586 | local recv_msg = udp:recv(4096) 587 | return recv_msg 588 | end 589 | 590 | --发送报文 591 | function send(msg) 592 | udp:send(msg) 593 | end 594 | 595 | --发送并接收 596 | function send_recv(msg) 597 | local time_out_cnt = 3 598 | local recv_msg = nil 599 | while(time_out_cnt > 0) do 600 | --发送报文 601 | send(msg) 602 | --接收报文 603 | recv_msg = receive() 604 | if(recv_msg) then break end 605 | time_out_cnt = time_out_cnt - 1 606 | end 607 | return recv_msg 608 | end 609 | 610 | --md5校验 611 | function check_md5(packet) 612 | local recv_md5 = {} 613 | for i=3, 18 do 614 | table.insert(recv_md5,packet[i]) 615 | packet[i] = 0x00 616 | end 617 | print() 618 | --将packet内容由整型转换为字节 619 | local bpacket = {} 620 | for k,v in ipairs(packet) do 621 | table.insert(bpacket,string.char(v)) 622 | end 623 | local md5str = md5.sumhexa(table.concat(bpacket)) 624 | local md5_packet = {} 625 | for i=1,string.len(md5str) do 626 | if(i%2==0) then 627 | md5_packet[i/2] = string.format("%d","0x"..string.sub(md5str,i-1,i)) 628 | end 629 | end 630 | return table.concat(md5_packet) == table.concat(recv_md5) 631 | end 632 | 633 | function connect() 634 | net_status = 0; 635 | index = 0x01000000 636 | login_packet = generate_login(mac_addr, ip, username, password, dhcp, service, version) 637 | session = login(login_packet) 638 | if(session) then 639 | retry_cnt = 0 640 | log.printStatus("online") 641 | log.info("Connecting the internet success!") 642 | breathe(mac_addr, ip, session, index) 643 | if(net_status ~= 1) then 644 | logout(mac_addr, ip, session, index) 645 | end 646 | end 647 | end 648 | 649 | function search() 650 | udp:connect("1.1.1.8", 3850) 651 | if(string.isNilOrEmpty(host_ip)) then 652 | host_ip = search_server_ip(mac_addr, ip) 653 | end 654 | if(string.isNilOrEmpty(host_ip)) then 655 | log.error("Failed to search for server host ip.") 656 | return false 657 | end 658 | 659 | log.info("Server IP: "..host_ip) 660 | udp:connect(host_ip, port) 661 | if(string.isNilOrEmpty(service)) then 662 | --udp:setpeername(host_ip, port) 663 | service = search_service(mac_addr) 664 | end 665 | if(string.isNilOrEmpty(service)) then 666 | log.warn("Failed to search internet service. Using the default service 'int', if it's not right, please configure service in 'bin/conf.lua'") 667 | service = "int" 668 | --return false 669 | end 670 | 671 | log.info("Service: "..service) 672 | return true 673 | end 674 | 675 | function init() 676 | --log.info("Loading configuration files.") 677 | dofile(config_file) 678 | dofile(authc_file) 679 | pcall(dofile, config_file) 680 | pcall(dofile, authc_file) 681 | port = 3848 682 | 683 | udp = nixio.socket("inet","dgram") 684 | udp:setopt("socket","reuseaddr",1) 685 | udp:setopt("socket","rcvtimeo",10) 686 | 687 | if(string.isNilOrEmpty(ip)) then 688 | udp:connect("1.1.1.8", 3850) 689 | ip = udp:getsockname() 690 | end 691 | 692 | os.execute("echo -n > "..log_file) 693 | log.info("MAC Addr: "..mac_addr) 694 | log.info("Local IP: "..ip) 695 | log.info("Username: "..username) 696 | log.info("Password: "..password) 697 | 698 | end 699 | 700 | function main() 701 | local connect_cnt = 0; 702 | init() 703 | local flag = autostart 704 | while(flag) do 705 | --记录连接次数 706 | connect_cnt = connect_cnt + 1 707 | log.printStatus("connecting") 708 | if(search()) then 709 | connect() 710 | log.printStatus("offline") 711 | if(net_status == -3) then 712 | --认证失败,不再自动连接 713 | log.error("Authentication failure: The authentication information is incorrect, or not in internet time period.") 714 | flag = false; 715 | elseif(net_status == -2 or net_status == -1) then 716 | --连接超时,3秒后重新连接 717 | log.error("Authentication failure: connect timeout, try reconnecting...") 718 | sleep(3) 719 | else 720 | --保持连接失败,3秒后重新连接 721 | log.error("Hold on connecting failed, try reconnecting...") 722 | sleep(3) 723 | end 724 | else 725 | --搜索服务失败,60秒后再次搜索 726 | log.printStatus("offline") 727 | sleep(60) 728 | end 729 | end 730 | end 731 | 732 | home = "/usr/share/supplicant" 733 | config_file = home.."/bin/conf.lua" 734 | authc_file = home.."/bin/authc.lua" 735 | log_file = home.."/info.log" 736 | status_file = home.."/supplicant.status" 737 | net_status = 0 738 | main() 739 | --------------------------------------------------------------------------------