在此警告,协议栈代码具有一定危险性,只用于测试,运行于测试环境如虚拟机环境、内网测试环境。切勿用于真实环境或生产环境,如因此造成严重的后果本人一概不负责。
java 手撸TCP协议栈 (1) 调用pcap ? 完成了操作网卡的api,这是实现tcp协议栈的最重要的基础部分,有了这个基础,就可以飞起来了。
在这里继续介绍在(1)的基础上完整的实现一个简单的静态http服务器。
要完成一个http服务器,首先需要了解底层的协议是什么样子的,这里不贴协议格式了,csdn一搜一大把,各位自己倒杯茶慢慢搜慢慢看。
下面直接贴代码:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.PointerByReference;
public class HttpServerPCap {
static class SocketStruct{
public byte[] remoteMacAddr;
public byte[] remoteAddr;
public int remotePort;
public long identifyId;
public long seqenceId ;
public long acknowlegeId;
public ByteBuffer buffer;
public int status; // 1 received syn 2 estable 3 fin1 4 fin2
}
private static byte[] localMacAddr = {(byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04, (byte)0x05, (byte)0x06};
private static byte[] localIp = {(byte)192, (byte)168, (byte)56, (byte)113};
private static int localPort = 8080;
private static Map<String, SocketStruct> socketStructMap = new ConcurrentHashMap<String, SocketStruct>();
private static Random random = new Random();
private static final int TcpDataMaxLen = 1460;
public static void main(String[] args) {
NativeMapping.PcapErrbuf errbuf = new NativeMapping.PcapErrbuf();
Pointer handle = NativeMapping.pcap_open_live("\\Device\\NPF_{C8E924E8-3C99-411D-8A9B-5A13CCC5E393}", 65536, 1, 1000, errbuf);
if (handle == null) {
System.out.println("Open device failed");
System.exit(-1);
}
NativeMapping.bpf_program prog = new NativeMapping.bpf_program();
String filterStr = String.format("ether dst %02X:%02X:%02X:%02X:%02X:%02X",
localMacAddr[0], localMacAddr[1], localMacAddr[2], localMacAddr[3], localMacAddr[4], localMacAddr[5]);
int rc = NativeMapping.pcap_compile(handle, prog, filterStr, 1, -1);
NativeMapping.pcap_freecode(prog);
long timeStart = System.currentTimeMillis();
while (true) {
receiveTcpData(handle);
for(String key : socketStructMap.keySet()){
SocketStruct s = socketStructMap.get(key);
if(s.status==2){
if(s.buffer!=null
&& s.buffer.position()==s.buffer.limit()
&& s.buffer.position()<s.buffer.capacity()){
int size = TcpDataMaxLen;
if(s.buffer.capacity()-s.buffer.position()<size){
size = s.buffer.capacity()-s.buffer.limit();
}
sendTcpReply(handle, s.remoteMacAddr, s.remoteAddr, s.remotePort,
s.identifyId, s.seqenceId, s.acknowlegeId,
false, false, false, false, true, true, false, false, false,
s.buffer.array(), 0, size);
s.buffer.limit(s.buffer.limit()+size);
}
}
}
}
}
private static int receiveTcpData(Pointer handle) {
int rc;
PointerByReference headerPP = new PointerByReference();
PointerByReference dataPP = new PointerByReference();
rc = NativeMapping.pcap_next_ex(handle, headerPP, dataPP);
switch (rc) {
case 0:
// timeout
break;
case 1:
Pointer headerP = headerPP.getValue();
Pointer dataP = dataPP.getValue();
if (headerP == null || dataP == null) {
} else {
final int len = NativeMapping.pcap_pkthdr.getLen(headerP);
final byte[] buff = dataP.getByteArray(0, NativeMapping.pcap_pkthdr.getCaplen(headerP));
if (buff[12] == 8 && buff[13] == 6 && buff[14] == 0 && buff[15] == 1 && buff[16] == 8 && buff[17] == 0 && buff[20] == 0
&& buff[21] == 1) {
// proc arp request and reply arp reply package
byte[] arpBuff = new byte[42];
System.arraycopy(buff, 6, arpBuff, 0, 6); // dest mac
System.arraycopy(localMacAddr, 0, arpBuff, 6, 6); // src mac
arpBuff[12] = 8; arpBuff[13] = 6;
arpBuff[14] = 0; arpBuff[15] = 1;
arpBuff[16] = 8; arpBuff[17] = 0; // ipv 4
arpBuff[18] = 6; // hw size
arpBuff[19] = 4; // protocol size
arpBuff[20] = 0; arpBuff[21] = 2; // arp reply
System.arraycopy(localMacAddr, 0, arpBuff, 22, 6); // sender mac
System.arraycopy(localIp, 0, arpBuff, 28, 4); // sender ip
System.arraycopy(buff, 22, arpBuff, 32, 6); // target mac
System.arraycopy(buff, 28, arpBuff, 38, 4); // target ip
NativeMapping.pcap_sendpacket(handle, arpBuff, arpBuff.length);
}else if(buff[12] == 8 && buff[13] == 0 && buff[14] == (byte)0x45 && buff[15] == 0 && buff[23]==6 ){
if(calcBuffChecksum(buff, 14, 20)==0){
int ipTotalLen = ((buff[16]&0xff)<<8)+(buff[17]&0xff);
int checkSum = calcBuff16BitsSum(buff, 26, 8);
checkSum += 6+(ipTotalLen-20)+calcBuff16BitsSum(buff, 14+20, ipTotalLen-20);
checkSum = (~(((checkSum>>16)&0xffff)+(checkSum&0xffff)))&0xffff;
if(checkSum==0){
if( buff[30]==localIp[0] && buff[31]==localIp[1] && buff[32]==localIp[2] && buff[33]==localIp[3] ){
int tcpHdrLen = (buff[46]&0xff)>>2;
int tcpFlags = ((buff[46]&0x1)<<8)+(buff[47]&0xff);
int srcPort = ((buff[34]&0xff)<<8)+(buff[35]&0xff);
int destPort = ((buff[36]&0xff)<<8)+(buff[37]&0xff);
String mapId = String.format("%d.%d.%d.%d %d - %d.%d.%d.%d %d",
buff[36]&0xff,buff[37]&0xff,buff[38]&0xff,buff[39]&0xff, destPort,
buff[26]&0xff,buff[27]&0xff,buff[28]&0xff,buff[29]&0xff, srcPort
);
SocketStruct s = socketStructMap.get(mapId);
long tempAcknoledgeId = ((long)(buff[38]&0xff)<<24) + ((buff[39]&0xff)<<16) + ((buff[40]&0xff)<<8) + (buff[41]&0xff);
if(tcpFlags==2){
if(s==null){
s = new SocketStruct();
s.remoteMacAddr = new byte[6];
System.arraycopy(buff, 6, s.remoteMacAddr, 0, 6);
s.remoteAddr = new byte[4];
System.arraycopy(buff, 26, s.remoteAddr, 0, 4);
s.remotePort = ((buff[34]&0xff)<<8)+(buff[35]&0xff);
s.identifyId = random.nextInt(1800)+6000;
s.seqenceId = random.nextInt(1000)+2000;
s.acknowlegeId = ((long)(buff[38]&0xff)<<24) + ((buff[39]&0xff)<<16) + ((buff[40]&0xff)<<8) + (buff[41]&0xff);
socketStructMap.put(mapId, s);
}
sendTcpReply(handle, s.remoteMacAddr, s.remoteAddr, s.remotePort,
s.identifyId, s.seqenceId, ++s.acknowlegeId,
false, false, false, false, true, false, false, true, false,
null, -1, 0 );
s.identifyId+=2;
s.status = 1;
} else if((tcpFlags&0x1fe)==0x10){
if(s!=null){
s.identifyId+=2;
s.acknowlegeId = ((long)(buff[38]&0xff)<<24) + ((buff[39]&0xff)<<16) + ((buff[40]&0xff)<<8) + (buff[41]&0xff);
if(s.status==1){
s.seqenceId++;
s.status = 2;
}else if(s.status==2){
if(s.buffer!=null){
s.seqenceId += s.buffer.limit()-s.buffer.position();
s.buffer.position(s.buffer.limit());
if(s.buffer.position()==s.buffer.capacity()){
sendTcpReply(handle, s.remoteMacAddr, s.remoteAddr, s.remotePort,
s.identifyId, s.seqenceId, s.acknowlegeId,
false, false, false, false, true, false, false, false, true,
null, -1, 0 );
s.status = 3;
}
}
}else if(s.status==3){
sendTcpReply(handle, s.remoteMacAddr, s.remoteAddr, s.remotePort,
s.identifyId, s.seqenceId, s.acknowlegeId,
false, false, false, false, true, false, false, false, true,
null, -1, 0 );
s.status=4;
}else if(s.status==4){
socketStructMap.remove(mapId);
}
}
} else if((tcpFlags&0x1fe)==0x18){
if(s!=null && s.buffer==null){
if(tempAcknoledgeId==s.acknowlegeId){
s.acknowlegeId += ipTotalLen-20-tcpHdrLen;
sendTcpReply(handle, s.remoteMacAddr, s.remoteAddr, s.remotePort,
s.identifyId, s.seqenceId, s.acknowlegeId,
false, false, false, false, true, false, false, false, false,
null, -1, 0 );
procHttpRequestData(s, new String(buff, 14+20+tcpHdrLen, ipTotalLen-20-tcpHdrLen));
}
}
}
if((tcpFlags&1)>0){
if(s!=null){
if(s.status==2 || s.status==3){
sendTcpReply(handle, s.remoteMacAddr, s.remoteAddr, s.remotePort,
s.identifyId, s.seqenceId, ++s.acknowlegeId,
false, false, false, false, true, false, false, false, false,
null, -1, 0 );
//s.identifyId+=2;
if(s.status==2){
s.status=3;
sendTcpReply(handle, s.remoteMacAddr, s.remoteAddr, s.remotePort,
s.identifyId, s.seqenceId, s.acknowlegeId,
false, false, false, false, true, false, false, false, true,
null, -1, 0 );
}else if(s.status==3){
s.status=4;
socketStructMap.remove(mapId);
}
}
}
}
}
}
}
}
return len;
}
break;
case -1:
break;
case -2:
break;
default:
break;
}
return 0;
}
private static void readFileData(File f, ByteBuffer bf) throws Exception{
byte[] buff = new byte[1024];
FileInputStream in = new FileInputStream(f);
while(in.available()>0){
int size = in.read(buff);
bf.put(buff, 0, size);
}
in.close();
}
private static int procHttpRequestData(SocketStruct s, String content){
int pos = content.indexOf("\r\n\r\n");
if(pos<0){
pos = content.indexOf("\n\n");
if(pos<0){
pos = content.lastIndexOf("\r\n");
if(pos<0){
pos = content.length();
}else{
pos += 2;
}
}else{
pos += 2;
}
}else{
pos += 4;
}
String headerContent = content.substring(0, pos);
String[] headrs = headerContent.replace("\r", "").split("\n");
if(headrs[0].startsWith("GET ")){
boolean isError = false;
String resourcePath = headrs[0].substring(4, headrs[0].lastIndexOf(" "));
File f = new File("."+resourcePath);
if(f.exists()){
String reply = "HTTP/1.1 200 OK\r\n"+
"Server: pcap http server\r\n"+
"Content-Type: text/html\r\n"+
"Content-Length: "+f.length()+"\r\n"+
"Accept-Ranges: bytes\r\n\r\n";
s.buffer = ByteBuffer.allocate((int)(reply.getBytes().length+f.length()));
s.buffer.put(reply.getBytes());
try {
readFileData(f, s.buffer);
} catch (Exception e) {
isError = true;
}
}else{
isError = true;
}
if(isError){
File errf = new File("./html/error/404.html");
String reply = "HTTP/1.1 404\r\n"+
"Server: pcap http server\r\n"+
"Content-Type: text/html\r\n\r\n";
s.buffer = ByteBuffer.allocate((int)(reply.getBytes().length+errf.length()));
s.buffer.put(reply.getBytes());
if(errf.exists()){
try {
readFileData(errf, s.buffer);
} catch (Exception e) {
}
}
}
s.buffer.limit(0);
s.buffer.position(0);
}
//String httpContent = content.substring(pos, content.length());
return 0;
}
private static int calcBuff16BitsSum(byte[] buff, int off, int len){
int checkSum = 0;
for(int i=0; i<len;++i){
checkSum += (i%2==0)?((buff[i+off]&0xff)<<8):(buff[i+off]&0xff);
}
return checkSum;
}
private static int calcBuffChecksum(byte[] buff, int off, int len){
int checkSum = calcBuff16BitsSum(buff, off, len);
return (~(((checkSum>>16)&0xffff)+(checkSum&0xffff)))&0xffff;
}
private static int sendTcpReply(Pointer handle, byte[] remoteMacAddr, byte[] remoteAddr, int remotePort,
long identify, long sequenceId, long acknolegeId,
boolean nonce, boolean cwr, boolean ecn, boolean urg, boolean ack, boolean push, boolean reset, boolean syn, boolean fin,
byte[] data, int off, int len ){
byte[] tcpBuff = new byte[2048];
int ipPkgLen = 20+20+len; // no options
System.arraycopy(remoteMacAddr, 0, tcpBuff, 0, 6); // dest mac
System.arraycopy(localMacAddr, 0, tcpBuff, 6, 6); // src mac
tcpBuff[12] = 8; tcpBuff[13] = 0;
tcpBuff[14] = (byte)0x45; // ipv4 hdrlen 20
tcpBuff[15] = 00;
tcpBuff[16] = (byte)((ipPkgLen>>8)&0xff); tcpBuff[17] = (byte)(ipPkgLen&0xff); // ipPkgLen
tcpBuff[18] = (byte)((identify>>8)&0xff); tcpBuff[19] = (byte)(identify&0xff); // protocol size
tcpBuff[22] = 0x40; // ttl
tcpBuff[23] = 6; // protocol tcp
System.arraycopy(localIp, 0, tcpBuff, 26, 4); // sender ip
System.arraycopy(remoteAddr, 0, tcpBuff, 30, 4); // target ip
tcpBuff[34] = (byte)((localPort>>8)&0xff); tcpBuff[35] = (byte)(localPort&0xff); // src port
tcpBuff[36] = (byte)((remotePort>>8)&0xff); tcpBuff[37] = (byte)(remotePort&0xff); // dest port
tcpBuff[38] = (byte)((sequenceId>>24)&0xff);
tcpBuff[39] = (byte)((sequenceId>>16)&0xff);
tcpBuff[40] = (byte)((sequenceId>>8)&0xff);
tcpBuff[41] = (byte)(sequenceId&0xff);
tcpBuff[42] = (byte)((acknolegeId>>24)&0xff);
tcpBuff[43] = (byte)((acknolegeId>>16)&0xff);
tcpBuff[44] = (byte)((acknolegeId>>8)&0xff);
tcpBuff[45] = (byte)(acknolegeId&0xff);
tcpBuff[46] = (byte)((20<<2)&0xf0+(nonce?1:0));
tcpBuff[47] = (byte)((cwr?0x80:0)+(ecn?0x40:0)+(urg?0x20:0)+(ack?0x10:0)+(push?0x08:0)+(reset?0x04:0)+(syn?0x02:0)+(fin?0x01:0));
tcpBuff[48] = (byte)(0x7f);
tcpBuff[49] = (byte)(0xff);
if(data!=null){
System.arraycopy(data, off, tcpBuff, 54, len);
}
int ipHdrChecksum = calcBuffChecksum(tcpBuff, 14, 20);
int tcpCheckSum = calcBuff16BitsSum(tcpBuff, 26, 8);
tcpCheckSum += 6+(20+len)+calcBuff16BitsSum(tcpBuff, 14+20, 20+len);
tcpCheckSum = ~(((tcpCheckSum>>16)&0xffff)+(tcpCheckSum&0xffff));
tcpBuff[24] = (byte)((ipHdrChecksum>>8)&0xff);
tcpBuff[25] = (byte)(ipHdrChecksum&0xff);
tcpBuff[50] = (byte)((tcpCheckSum>>8)&0xff);
tcpBuff[51] = (byte)(tcpCheckSum&0xff);
NativeMapping.pcap_sendpacket(handle, tcpBuff, ipPkgLen+14);
return ipPkgLen+14;
}
}
在代码根目录创建资源目录:
运行代码在虚拟机访问服务:
到这里手撸 tcp 协议栈 完整实现了一个基本的http服务器。
|