[轉(zhuǎn)貼]淺談PHP的Socket模塊
[
![]() 自己寫的東西,首發(fā)在邪惡八進制,現(xiàn)在轉(zhuǎn)過來:)
文章作者:hack988 [PHPWind] 信息來源:邪惡八進制信息安全團隊(www.) 自從冰血弟弟把我加為貴賓以來一直都沒有發(fā)布過任何對邪八有用的信息,實在有點對不起大家。呵呵,我不屬于研究安全人,僅僅對安全特別關(guān)注而已。今天偷閑,發(fā)點php相關(guān)的另類應(yīng)用好了:) 一直以來很少看到有多少人使用php的socket模塊來做一些事情,大概大家都把它定位在腳本語言的范疇內(nèi)吧,但是其實php的socket模塊可以做很多事情,包括做ftplist,http post提交,smtp提交,組包并進行特殊報文的交互(如smpp協(xié)議),whois查詢。這些都是比較常見的查詢。 特別是php的socket擴展庫可以做的事情簡直不會比c差多少。 預(yù)備知識: php的socket連接函數(shù) 1、集成于內(nèi)核的socket 這個系列的函數(shù)僅僅只能做主動連接無法實現(xiàn)端口監(jiān)聽相關(guān)的功能。而且在4.3.0之前所有socket連接只能工作在阻塞模式下。 此系列函數(shù)包括 fsockopen,pfsockopen 這兩個函數(shù)的具體信息可以查詢的用戶手冊 他們均會返回一個資源編號對于這個資源可以使用幾乎所有對文件操作的函數(shù)對其進行操作如fgets(),fwrite(), fclose()等單注意的是所有函數(shù)遵循這些函數(shù)面對網(wǎng)絡(luò)信息流時的規(guī)律,例如: fread() 從文件指針 handle 讀取最多 length 個字節(jié)。 該函數(shù)在讀取完 length 個字節(jié)數(shù),或到達 EOF 的時候,或(對于網(wǎng)絡(luò)流)當一個包可用時就會停止讀取文件,視乎先碰到哪種情況。 可以看出對于網(wǎng)絡(luò)流就必須注意取到的是一個完整的包就停止。 2、php擴展模塊帶有的socket功能。 php4.x 有這么一個模塊extension=php_sockets.dll,RHT9上安裝后也有一個extension=php_sockets.so的(這個依稀記得是有的需要確認一下,好久沒有玩linux了) 當打開這個此模塊以后就意味著php擁有了強大的socket功能,包括listen端口,阻塞及非阻塞模式的切換,multi-client 交互式處理等 這個系列的函數(shù)列表參看http://www./manual/en/ref.sockets.php 看過這個列表覺得是不是非常豐富呢?不過非常遺憾這個模塊還非常年輕還有很多地方不成熟,相關(guān)的參考文檔也非常少:( 我也正在研究中,因此暫時不具體討論它,僅給大家一個參考文章 http://www./pecl/tutorials/sockets.php 下面舉例說明: 例子1 簡單應(yīng)用——whois查詢 看一段代碼 <?php $server="whois.verisign-grs.com";//TLD .com whois server $data = ""; $domain = "abc.com";//serch domain $fp = fsockopen($server,43); if ($fp) { fputs($fp,$domain."\r\n"); while (!feof($fp)) { $data .= fgets($fp,1000); } } fclose($fp); echo ln2br($data); } 下面看看對于ftplist的應(yīng)用 <?php $server="192.168.1.3";//服務(wù)器ip端口用默認的21舉例而已所以不要那么復(fù)雜 $data = ""; $fp = fsockopen($server,21);//打開服務(wù)器 $data .= fread($fp,1024);//讀取狀態(tài)注意用的fread那么是一個可用報文結(jié)束為一段讀取。 fwrite($fp,"USER hack\r\n");//登陸信息 $data .= fgets($fp,1024);//讀取是否有此用戶,是否等待密碼 fwrite($fp,"PASS 123456\r\n");//密碼 $data .= fgets($fp,1024);//是否驗證成功 fwrite($fp,"REST 100\r\n");//重置數(shù)據(jù)流 $data .= fgets($fp,1024); fwrite($fp,"PWD\r\n");//當前目錄 $data .= fgets($fp,1024); fwrite($fp,"TYPE A\r\n");//切換傳輸模式為A——ASCII模式 $data .= fgets($fp,1024); fwrite($fp,"CWD ./123456\r\n");//更換目錄為123456 $data .= fgets($fp,1024); fwrite($fp,"PASV\r\n");//切換為被動模式 $tstring = fgets($fp,1024); $data .=$tstring; $ports=ftp_pasvs($tstring);//獲取服務(wù)器分配的端口及ip fwrite($fp,"LIST -al\r\n");//列表 $sock_data=ftp_data_connection($ports);//連接被動模式下的端口 while (!feof($sock_data)) {//循環(huán)獲取數(shù)據(jù) $data .= fgets($sock_data, 1024); } echo nl2br($data); function ftp_pasvs($string)//用于獲取被動模式下的相關(guān)連接信息 { $ip_port = preg_replace("/^(.+ \()([0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]+,[0-9]+)(\)?.*\r\n)$/i","\\2",$string); return $ip_port; } function ftp_data_connection($ip_port)//連接服務(wù)器數(shù)據(jù)端口 { $ip_port=trim($ip_port); if (!preg_match("/^[0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]+,[0-9]+$/i", $ip_port)) { return "false"; } $DATA = explode(",", $ip_port); $ipaddr = $DATA[0].".".$DATA[1].".".$DATA[2].".".$DATA[3]; $port = $DATA[4]*256 + $DATA[5]; //echo $port."|".$ipaddr;//exit; $data_connection = @fsockopen($ipaddr, $port); if (!$data_connection) { return "false"; } return $data_connection; } ?> 1、為什么采用fread:因為此函數(shù)是以網(wǎng)絡(luò)包為截斷的,使用這樣的截斷來獲得信息可以很好的保持兼容性,因為對于ftpclient來講服務(wù)器是一個未知的環(huán)境(wu-ftp,ser-u,g6ftp...),在這樣的環(huán)境下有利于讀取ftp的特征字符串以便以后使用。 2、為什么不用主動模式連接服務(wù)器傳輸數(shù)據(jù):主動模式必須客戶端指定端口然后進行傳輸,而php自帶的函數(shù)并不具備此特性。除非使用擴展庫,這樣程序兼容性就會。。同時由服務(wù)器指定端口避免了客戶端尋找合適端口造成的額外負擔,盡管這樣的負擔微乎其微。 與這個代碼類似的應(yīng)用還有estmp發(fā)送郵件,管與此應(yīng)用我不多說,有興趣的朋友可以到我的blog上看看有相關(guān)代碼(www.)冰血老弟不介意我做點小廣告吧哈哈。 POST,GET提交這個話題很老了:)我想不需要我在這里提起了文章很多。 在看下面一個例子前先提及一組函數(shù)pack,unpack。 任何一款擁有socket操作能力的語言都有一個專門用于組包的函數(shù),php也不例外當然這組函數(shù)的用途不僅僅是組包。 下面簡單的介紹一下: 應(yīng)用一: 輸入16進制或者2進制流。 $src="3B06"; $binvar = pack(‘H*‘,$src); echo $binvar; echo chr(0x3B).chr(0x06); 應(yīng)用二: 網(wǎng)絡(luò)數(shù)據(jù)流拆包 $elength = str_len($bin); extract(unpack("NLEN/Hcontent", $bin)); 一維數(shù)組并解開到變量$LEN和$content內(nèi)。 下面看例子了 這個是利用php實現(xiàn)中國移動cmpp登陸消息的一個縮寫例子 <?php define("LOGINID",0x00000001);//登陸消息 class SMS{ var $host; var $mtport; var $moport; var $connectout; var $sms_sock; var $loginuser; var $loginpass; var $error_stop=0; function SMS($host,$user,$pass,$mtport,$moport,$timeout=30,$if_conn=0){ $this->host=$host; $this->mtport=$mtport; $this->moport=$moport; $this->loginuser=$user; $this->loginpass=$pass; $if_conn && $this->login(); } function login(){ $fp=@fsockopen($this->host,$this->mtport,$errno,$errstr,$this->connectout); if(!$fp){ $this->sms_sock=‘‘; $this->halt("error in login num=$errno, msg=$errstr"); return false; }else{ //$this->sms_sock=$fp; $data=pack("Na10a32",LOGINID,$this->loginuser,md5($this->loginpass));//這個地方就是組包了 $data=pack("N",strlen($data)+4).$data;//$data是實際內(nèi)容前面這個表示整個報文長度 if(fputs($fp,$data) !== false){ //print_r(unpack("N",fread($fp,4)));//此處用于調(diào)試時檢測用 //print_r(unpack("N",fread($fp,4))); //print_r(unpack("c",fread($fp,4))); //print_r(unpack("N",fread($fp,4))); @fread($fp,12); $results=@fread($fp,4); if($results){ $rs=@unpack("Ncounts",$results);//返回socket結(jié)果。 $this->sms_sock=$fp; }else{ $this->sms_sock=‘‘; $this->halt("error in login: loginid=$this->loginuser loginpass=$this->loginpass"); return false; } }else{ $this->sms_sock=‘‘; $this->halt("error in submit logininfo: loginid=$this->loginuser loginpass=$this->loginpass"); return false; } } return true; } function halt($msg){ echo $msg; flush(); $this->error_stop && exit; } } 末了,不多說。由于時間倉促,有些得不對的地方,望各位用pm通知我,我會及時更正,有什么疑問也可以pm我。 參考資料: 1、rfc 959 (ftp 協(xié)議) 2、RFC 3912 (WHOIS Protocol) 3、SMPP(SMPP Short Message Peer to Peer) 4、CMPP (China Mobile Peer to Peer) |
|
來自: Ralf_Jones > 《PHP》