📄 fileagentpacket.java
字号:
/*
* LumaQQ - Java QQ Client
*
* Copyright (C) 2004 luma <stubma@163.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package edu.tsinghua.lumaqq.qq.obsolete;
import java.nio.ByteBuffer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.tsinghua.lumaqq.qq.Crypter;
import edu.tsinghua.lumaqq.qq.QQ;
import edu.tsinghua.lumaqq.qq.packets.PacketParseException;
/**
* <pre>
* 文件中转包,这个包的格式和FilePacket的子类格式相同甚少,因此是一个独立的类,它
* 需要使用FileDataPacket,因为它是中转包,会包含中转数据。当它不用来中转数据时,
* 也有自己的格式。具体格式如下:
*
* 文件中转请求包(从客户端发出)以0x04开头,内容如下:
* 1. 0x04
* 2. 客户端版本号,2字节
* 3. 整个的包长,2字节
* 4. 命令,2字节
* 5. 序号,2字节
* 6. 我的QQ号,4字节
* 7. 未知的8字节,无规律
* 8. 根据命令的不同,后面的内容不同:
* a. 如果命令是0x0001时,表示请求中转服务,格式为
* i. 认证令牌长度,2字节
* ii. 认证令牌
* iii. 未知2字节,0x07D0,十进制值2000 (从这个字段开始加密)
* iv. 文件传输另一方的QQ号,4字节
* b. 如果命令是0x0002,表示接收者向中转服务器报到,格式为:
* i. 文件会话ID,4字节
* ii. 认证令牌长度,2字节(从这里开始加密)
* iii. 认证令牌
* c. 如果命令是0x0003,为封装文件数据的包,格式为(均不加密)
* i. 文件会话ID,4字节
* ii. 未知的4字节
* iii. 被封装的数据包长度,2字节
* iv. 数据包,也就是FileDataPacket的格式
* d. 如果命令是0x0004,为中转结束包,格式为:
* i. 文件会话ID,4字节
* ii. 我的QQ号 (从这个字段开始加密)
* iii. 未知的两字节,一般为全0,可能表示成功
* e. 如果命令是0x0005,是对服务器0x0005的回复,其中7部分全0。格式为:
* i. 文件会话ID,4字节
* ii. 未知的4字节,全0 (加密)
* f. 如果命令是0x0006,6部分全0。格式为:
* i. 文件会话ID,4字节
* ii. 未知的2字节,全0 (加密)
* 9. 尾部0x3
*
* 文件中转回复包(从服务器端发出)以0x04开头,格式为:
* 1. 0x04
* 2. 中转服务器版本号
* 3. 整个的包长,2字节
* 4. 命令,2字节
* 5. 序号,2字节
* 6. 我的QQ号,4字节
* 7. 未知的8字节,无规律
* 8. 根据命令的不同,后面的内容不同:
* A. 如果命令是0x0001,表示对中转请求的回应,且6部分和请求相同。格式为(加密):
* a. 应答码,2字节,根据应答码不同,内容不同
* I. 如果是0x0000,表示接受中转请求,后面的格式为:
* i. 中转服务器IP,little-endian格式,4字节
* ii. 中转服务器端口,2字节
* iii. 文件会话ID,4字节
* iv. 4字节重定向IP,全0
* v. 2字节重定向端口,全0
* vi. 未知的两字节,全0
* II. 如果是0x0001,表示重定向,格式为
* i. 中转服务器IP,4字节,全0
* ii. 中转服务器端口,2字节,全0
* iii. 文件会话ID,4字节,全0
* iv. 4字节重定向IP,little-endian格式
* v. 2字节重定向端口
* vi. 未知的两字节,全0
* B. 如果命令是0x0002,表示中转服务器对接收者报到的应答,格式为:
* a. 文件会话ID,4字节
* b. 未知的4字节,全0(从这里开始加密)
* C. 如果命令是0x0003,表示中转的数据,格式为(均不加密):
* a. 文件会话ID,4字节
* b. 未知的4字节
* c. 被封装的数据包长度,2字节
* d. 数据包,也就是FileDataPacket的格式
* D. 如果命令是0x0005,则是通知客户端可以开始传送数据,格式为:
* a. 文件会话ID,4字节
* b. 已经传送完成的文件数,两字节 (从这个字段开始加密)
* c. 后面的消息长度,2字节
* d. 消息,一般是"it's time for transfering data"
* E. 如果命令是0x0006,则是对客户端0x0006的回复,7部分全0,格式为:
* a. 文件会话ID,4字节
* b. 未知的两字节,全0
* c. 未知的4字节,0x00013880,十进制值80000
* d. 未知的2字节,0x003C,十进制值60
* e. 未知的4字节,全0
* 9. 尾部,0x3
* </pre>
*
* @author luma
*/
@SuppressWarnings("unused")
public class FileAgentPacket {
// Log对象
protected static Log log = LogFactory.getLog(FilePacket.class);
// 文件守望者
private FileWatcher watcher;
// 命令
protected char command;
// 序号
protected char sequence;
// 加密解密类
protected static Crypter crypter = new Crypter();
// 被封装的FileDataPacket
protected FileDataPacket fdp;
// 加密部分缓冲区
protected static ByteBuffer buffer = ByteBuffer.allocate(50);
// 所有的包都会有的字段
private char sessionId;
// 请求中转服务回复包字段,如果是重定向,则ip为重定向ip;如果是接受,ip为中转服务器ip
// 这里的IP已经被转换成big-endian格式
private char replyCode;
private byte[] agentIp;
private int agentPort;
/**
* 构造函数
* @param watcher 文件守望者对象
*/
public FileAgentPacket(FileWatcher watcher) {
this.watcher = watcher;
this.fdp = new FileDataPacket(watcher);
}
/**
* 填充包内容到out中,out中原来的内容将被清空,填充完毕后,out的
* position等于包长
* @param out ByteBuffer对象
*/
public void fill(ByteBuffer out) {
out.clear();
// 填充头部
putHead(out);
// 填充包体
putBody(out);
// 填充包尾
putTail(out);
}
/**
* 填充包内容到out中,out中原来的内容
* @param out
* @param from
*/
public void fill(ByteBuffer out, int from) {
out.position(from);
// 填充头部
putHead(out);
// 填充包体
putBody(out);
// 填充包尾
putTail(out);
}
/**
* 从in的当前位置开始解析文件中转包,解析后的position将位于这个包之后
* @param in
* @throws PacketParseException
*/
public void parse(ByteBuffer in) throws PacketParseException {
// 解析头部
parseHead(in);
// 解析包体
parseBody(in);
}
/**
* 从in的当前未知开始解析包体
* @param in
* @throws PacketParseException
*/
private void parseBody(ByteBuffer in) throws PacketParseException {
switch(command) {
case QQ.QQ_FILE_CMD_REQUEST_AGENT:
/* 0x0001 */
parseRequestAgent(in);
break;
case QQ.QQ_FILE_CMD_CHECK_IN:
/* 0x0002 */
parseCheckIn(in);
break;
case QQ.QQ_FILE_CMD_FORWARD:
/* 0x0003 */
parseForward(in);
break;
case QQ.QQ_FILE_CMD_IT_IS_TIME:
/* 0x0005 */
parseItIsTime(in);
break;
case QQ.QQ_FILE_CMD_I_AM_READY:
/* 0x0006 */
parseIAmReady(in);
break;
default:
log.error("不支持的命令类型");
break;
}
}
/**
* 从in的当前未知开始解析报到的回复包
* @param in
*/
private void parseCheckIn(ByteBuffer in) {
// 会话ID
sessionId = (char)in.getInt();
}
/**
* 从in的当前未知开始解析I Am Ready回复包
* @param in
*/
private void parseIAmReady(ByteBuffer in) {
// 会话ID
sessionId = (char)in.getInt();
// TODO 后面的目前看来没有什么用处,先忽略
}
/**
* 从in的当前未知开始解析ItIsTime通知包
* @param in
*/
private void parseItIsTime(ByteBuffer in) {
// 会话ID
sessionId = (char)in.getInt();
// TODO 后面的目前看来没有什么用处,先忽略
}
/**
* 从in的当前未知开始解析数据转发包
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -