前言
学校的有线网和无线网都需要使用学号密码登录以后才能访问外网,登录需要在网页端登录,但是我的树莓派没有装桌面环境,平时通过SSH使用故无法访问网页所以也无法登录校园网。
树莓派无法访问外网,所以无法安装在线库,也不能apt-get安装软件,极大影响了我的使用体验。
校园网的登录机制
要想办法让这货能登录到校园网,我参考了这篇文章:树莓派命令行下登陆校园网作者提到他学校的校园网登录时会向服务器POST一个包含账号密码的HTTP请求,登录操作就完成了。我在我的浏览器中打开开发者模式登录了一下校园网,果然找到了一个非常相似的POST。
这个POST请求附带了一个参数method,其值为login,明显是一个用于登录的请求,实体数据部分居然直接包含账号密码明文。那么向服务器发送这个数据包应该就实现登录了。
树莓派登录校园网的方法
既然浏览器登录校园网的机制弄明白了,树莓派登录校园网的方法就有了:让树莓派POST这个包到服务器就好了。树莓派使用http协议最简单的方式就是用python,需要Requests库的支持。但是我的树莓派不能访问外网,所以也装不了Requests,需要另辟蹊径。
HTTP协议是无连接无状态的应用层协议,它基于TCP协议,而且登录校园网的这个POST请求数据帧是纯ASCII码构成的。所以可以考虑socket编程,与学校的服务建立TCP连接,直接把这包数据发送给服务器。这里选择c语言实现。
验证
在编程实现之前,先用串口助手验证一下上述想法能否真的登录校园网,在浏览器开发者模式查到的HTTP请求数据帧被分为Request Headers和From Data两个部分显示,根据HTTP协议,在原数据帧中这两部分之间使用“\r\n\r\n”连接,就是两个换行。将拼接好的数据帧复制到串口助手发送给学校服务器的80端口,服务器果然返回了200。
编程
经过验证,此方法可行。接下来就简单了,编写程序连接到服务器80端口,把数据包发给服务器。
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
char buf[] = "POST /eportal/InterFace.do?method=login HTTP/1.1\r\nHost: ip\r\nConnection: keep-alive\r\nContent-Length: 695\r\nOrigin: http://ip\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36\r\nContent-Type: application/x-www-form-urlencoded; charset=UTF-8\r\nAccept: */*\r\nReferer: http://172.10.255.9/eportal/index.jsp?wlanuserip=ea277fbe6853019d076ff17487d18018&wlanacname=97ab810d5fdf6cc9b8af3e881b6615fd&ssid=&nasip=6be7b81ce0ec6345fcc85877bf5905dc&snmpagentip=&mac=05279fe321bdc7960ed965dba6bc5a00&t=wireless-v2&url=2c0328164651e2b4f13b933ddf36628bea622dedcc302b30&apmac=&nasid=97ab810d5fdf6cc9b8af3e881b6615fd&vid=c5b1cedc64fb0ad5&port=437eae7d2b2f194e&nasportid=5b9da5b08a53a540806c821ff7c143818e3e1e31ee0f0b7322cc694dcfa89c41\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\nCookie: EPORTAL_COOKIE_OPERATORPWD=\r\n\r\nuserId=学号&password=密码&service=%25E5%2586%2585%25E7%25BD%2591%252B%25E5%25A4%2596%25E7%25BD%2591&queryString=wlanuserip%253Dea277fbe6853019d076ff17487d18018%2526wlanacname%253D97ab810d5fdf6cc9b8af3e881b6615fd%2526ssid%253D%2526nasip%253D6be7b81ce0ec6345fcc85877bf5905dc%2526snmpagentip%253D%2526mac%253D05279fe321bdc7960ed965dba6bc5a00%2526t%253Dwireless-v2%2526url%253D2c0328164651e2b4f13b933ddf36628bea622dedcc302b30%2526apmac%253D%2526nasid%253D97ab810d5fdf6cc9b8af3e881b6615fd%2526vid%253Dc5b1cedc64fb0ad5%2526port%253D437eae7d2b2f194e%2526nasportid%253D5b9da5b08a53a540806c821ff7c143818e3e1e31ee0f0b7322cc694dcfa89c41&operatorPwd=&operatorUserId=&validcode=&passwordEncrypt=false";
char buf2[300];
int main(){
int sockfd;
int len;
struct sockaddr_in address;
int result;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
address.sin_family = AF_INET;
address.sin_port = htons(80);
address.sin_addr.s_addr=inet_addr("学校服务器IP");
bzero(&(address.sin_zero), 8);
result = connect(sockfd, (struct sockaddr *)&address, sizeof(struct sockaddr));
if(result == -1) {
perror("连接失败。");
exit(1);
}
write(sockfd, buf, sizeof(buf));
read(sockfd, buf2, 300);
printf("%s\n", buf2);
close(sockfd);
exit(0) ;
}
编译运行,得到服务器返回的结果:
登录成功,安装w3m上B站看看:
完美