ホームページ バックエンド開発 PHPチュートリアル PHP はトラフィック コピー tcpcopy (php + python) を実装します

PHP はトラフィック コピー tcpcopy (php + python) を実装します

Jun 20, 2016 pm 01:04 PM
python

tcpcopy のソースコードを読むと、php と python の両方で raw ソケットを操作できることが分かりました。したがって、tcpcopy は php と python を使用して実装されており、コードは比較的単純です

tcpcopy の重要な点は、理論的には、tcp リクエスト メッセージのデータ部分が取得され、IP 層でテスト サーバーに転送される限り、トラフィック コピーが実現できるということです。

したがって、偽の TCP 接続を維持してテスト サーバーを欺くために、TCP セッションを維持するだけで済みます。

コードの実装は主に TCP ステート マシンに基づいており、パケット キャプチャの乱れの問題を考慮し、tcpdump デバッグと組み合わせ、php を使用して tcpcopy を実装し、Python を使用してインターセプトを実装します。

次のコードをテストした後、クライアントは 5000 の http リクエスト (約 50 の長い接続) を開始し、すべてのトラフィックがテスト マシンにコピーされます。

実際、tcpcopy は lvs、nat、およびオペレーターのトラフィック ハイジャックと同様に機能し、tcp プロトコル スタックを欺くことによって目的を達成します。同様に、lvs のロード バランシング機能も同様に、達成できます(後で試してみます)。

コード:

1.tcpcopy.php

2 つのプロセスが動作し、1 つのプロセスはパケットをキャプチャしてキューに入れる責任を負い、もう 1 つのプロセスはキャプチャされたデータ パケットを消費する責任を負うため、tcpcopy プロセスの効率は高くありません。

<?php
date_default_timezone_set('PRC');
ini_set('memory_limit','512M');
error_reporting(E_ALL);
ini_set( 'display_errors', 'On' );
ini_set( "log_errors", "On" );
ini_set( "error_log", "/tmp/php_error.log" ); 


$local_ip = "192.168.56.101";
$src_ip = "192.168.56.101";
$dest_ip = "192.168.56.102";
$src_port = 50000+rand(1,10000);
$src_port = 50000;
$local_udp_port = 20000;
$g_remote_test_ip = "192.168.56.102";
$dest_port = 8080;
$seq_num = 1000000000+rand(1,1000000000);
$seq_num = 1;
$ack_num = 0;
$g_fake_port_indx = 20000;

class RawSocket{
	public $s;
	private $dest_ip;
	private $dest_port;
	
	const FIN = 1;	
	const SYN = 2;	
	const ACK = 16;	

	/*
	private $seq_num;
	private $ack_num
	*/
	public function create_listen_udp( $local_ip, $local_port ){
		// var_dump( func_get_args( ) );
		// $local_ip = "0.0.0.0";
		// $local_port = 53;
		$s = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
		// socket_set_nonblock( $s );
		$bind_ret = socket_bind($s, $local_ip, $local_port );
		if( $s === false 
		    || $bind_ret === false ){
		   echo "socket_last_error_str:" . socket_strerror(socket_last_error()) . "\n";
		   return false;
		}

		/*
		var_dump( socket_getsockname( $s , $addr, $port ) );
		var_dump( $addr );
		var_dump( $port );
		*/
		
		$this->s = $s;		
		return $this->s;
	}
	
	// listen local ip
	public function create_listen_socket( $local_ip, $remote_ip=null ){
		$one = 1;
		$raw_socket = socket_create( AF_INET, SOCK_RAW, getprotobyname("tcp") ); 
		// $raw_socket = socket_create( AF_INET, SOCK_RAW, getprotobyname("icmp") );			/* no need bind,connect, 不能抓不属于自己的包 */
		// $raw_socket = socket_create( AF_INET, SOCK_RAW, 1 ); 
		socket_set_nonblock( $raw_socket );
		// trick, 3 is stand for header control
		$set_ret = socket_set_option( $raw_socket, getprotobyname("ip"), 3, $one); 	
		$conn_ret = $bind_ret = true;
		if( isset( $local_ip) ){
			// 设置 dest ip 
			$bind_ret = socket_bind($raw_socket, $local_ip );
		}
		if( isset( $remote_ip) ){
			// 不限制 source_ip
			// $conn_ret = socket_connect( $raw_socket, $remote_ip, 0 );
		}
		// $bind_ret = socket_connect( $raw_socket, $remote_ip, 0 );
		// $bind_ret = socket_bind($raw_socket, $local_ip );
		if( $raw_socket === false 
		    || $set_ret === false
			|| $conn_ret === false
		    || $bind_ret === false ){
		   echo "socket_last_error_str:" . socket_strerror(socket_last_error()) . "\n";
		   return false;
		}
		$this->s = $raw_socket;
		
		return $this->s;

	}

	// send raw socket to ip
	public function create_send_socket( $remote_ip ){
		$this->dest_ip = $remote_ip;
		$dest_ip_fake = "127.0.0.1";
		$dest_ip_fake = "192.168.56.101";
		$dest_port_fake = 8080;
		$one = 1;
		$raw_socket = socket_create( AF_INET, SOCK_RAW, getprotobyname("tcp") ); 
		// $raw_socket = socket_create( AF_INET, SOCK_RAW, getprotobyname("ip") ); 
		socket_set_nonblock( $raw_socket );
		// trick, 3 is stand for header control
		$set_ret = socket_set_option( $raw_socket, getprotobyname("ip"), 3, $one); 
		
		// trick, connect is needed anyway.	$dest_ip must by right.			
		$conn_ret = socket_connect( $raw_socket, $remote_ip, 0 );
		// $conn_ret = socket_connect( $raw_socket, $dest_ip_fake, $dest_port );

		// trick, for read
		// socket_bind($raw_socket, $src_ip );
		if( $raw_socket === false 
		    || $set_ret === false
		    || $conn_ret === false ){
		   echo "socket_last_error_str:" . socket_strerror(socket_last_error()) . "\n";
		   return false;
		}
		$this->s = $raw_socket;
		
		return $this->s;
	}

	public function create_socket( $src_ip, $src_port, $dest_ip=null, $dest_port=null ){
		$this->dest_ip = $dest_ip;
		$this->dest_port = $dest_port;
		$dest_ip_fake = "127.0.0.1";
		$dest_ip_fake = "192.168.56.101";
		$dest_port_fake = 8080;
		$one = 1;
		$raw_socket = socket_create( AF_INET, SOCK_RAW, getprotobyname("tcp") ); 
		socket_set_nonblock( $raw_socket );
		// trick, 3 is stand for header control
		$set_ret = socket_set_option( $raw_socket, getprotobyname("ip"), 3, $one); 
		
		// trick, connect is needed anyway.	$dest_ip must by right.			
		// $conn_ret = socket_connect( $raw_socket, $dest_ip, $dest_port );
		// $conn_ret = socket_connect( $raw_socket, $dest_ip_fake, $dest_port );

		// trick, for read
		socket_bind($raw_socket, $src_ip );
		if( $raw_socket === false 
		    || $set_ret === false
		    || $conn_ret === false ){
		   echo "socket_last_error_str:" . socket_strerror(socket_last_error()) . "\n";
		   return false;
		}
		$this->s = $raw_socket;
		
		return $this->s;
	}	

	public function socket_read_raw( ){
		$read_ret = socket_read( $this->s, 65535 );	
		 if( $read_ret === false  ){
			$error = socket_last_error();
			if( 11 !== $error ){
				echo "socket_last_error_str:" . socket_strerror(socket_last_error()) . "\n";
				return null;
			}
		   return false;
		}

        if( strlen($read_ret) > 20 ){
			// echo "read_data...\n";
            return self::IPUnpack( $read_ret );
        }
        return null;
	}

	public function socket_read_udp( ){
		$read_ret = socket_recvfrom( $this->s, $buf, 65535, 0, $from='', $port=0 );	
		//echo $read_ret . "\n";
		if( $read_ret === false  ){
			$error = socket_last_error();
			if( 11 !== $error ){
				echo "socket_last_error_str:" . socket_strerror(socket_last_error()) . "\n";
				return null;
			}
		   return false;
		}

        if( $read_ret > 20 ){
			// echo "udp_read_data...\n";
            return self::IPUnpack( $buf );
        }
        return null;
	}
/*
	public function socket_recv_raw( ){
		$buf = '';
		$read_ret = socket_recvfrom( $this->s, $buf, 65535 );	
		 if( $read_ret === false  ){
			$error = socket_last_error();
			if( 11 !== $error ){
				echo "socket_last_error_str:" . socket_strerror(socket_last_error()) . "\n";
				return null;
			}
		   return false;
		}
        if( strlen($buf) > 20 ){
			echo "read_data...\n";
            return $this->IPUnpack( $buf );
        }
        return null;
	}
*/
    public function socket_send(
                            $src_ip, $src_port, $dest_ip, $dest_port, 
                            $seq_num, $ack_num, $tcp_flag,
                            $tcp_data,$timestamp, $ts_echo  ){
        $ip_data = self::TCPPacket( ip2long($src_ip), ip2long($dest_ip), $src_port, $dest_port, 
	        $seq_num, $ack_num, $tcp_flag, 
	        $tcp_data, $timestamp, $ts_echo );
		
        
		// echo "tcp_data_md5:" . md5( $ip_data ) . "\n";
        $ip_data = self::IPPacket("tcp", ip2long($src_ip), ip2long($dest_ip), $ip_data );
	
        $write_ret = socket_write( $this->s, $ip_data );
		if( $write_ret === false || $write_ret !== strlen($ip_data)){
		   echo "socket_last_error_str:" . socket_strerror(socket_last_error()) . "\n";
           return false;
		}
		// echo "socket_last_error_str:" . socket_strerror(socket_last_error()) . "\n";
		// echo "write_ret:" . json_encode( $write_ret ) . "\n";
        return $write_ret;
    }

	/* IP header */
	/*
	versionAndHeaderlen: 1Byte
	service: 1Byte
	totalLen: 2Bytes
	PacketID: 2Bytes
	SliceInfo: 2Bytes
	TTL: 1Byte
	type: 1Byte
	checksum: 2Bytes			==> just ip header, 16bits fan ma sum
	srcIP: 4Bytes
	destIP: 4Bytes
	*/
	public static function IPUnpack( $packet ){
		$arr = unpack("Cverlen/x/ntotal_len/x4/Cttl/Ctype/ncheck_sum/Nsrc_ip/Ndest_ip/x*", $packet);

		$arr['version'] = $arr['verlen'] >> 4;	
		$arr['header_len'] = ($arr['verlen'] & 0x0f) << 2;	
		unset( $arr['verlen'] );
		
		$arr['type'] = getprotobynumber( $arr['type'] );
			
		$arr['src_ip'] = long2ip( $arr['src_ip'] );
		$arr['dest_ip'] = long2ip( $arr['dest_ip'] );

		if( $arr['type'] == 'tcp' ){
			$arr['tcp'] = self::TCPUnPack( substr($packet, $arr['header_len'] ) );
			$tcp_data_len = $arr['total_len'] - $arr['header_len'] - $arr['tcp']['header_len'];
			$arr['tcp']['data_len'] = $tcp_data_len;
		}
		
		if( strlen($packet) < 20 ) return false;
		return $arr;
	}

	public static function IPPacket( $proto, $src_ip, $dest_ip, $data ){
		$ver_len = 4<<4 | 5;	
		$service = 0;
		$total_len = 20+strlen($data);	
		$id_flag_offset = 0;
		$ttl = 65;
		$type = getprotobyname( $proto);
		$chk_sum = 0;
		$i = 2;
		while( $i-- ){
			$header = pack("CCn"."N"."CCn"."N"."N",
				$ver_len, $service, $total_len,
				$id_flag_offset,
				$ttl, $type, str2int($chk_sum,2),
				$src_ip, $dest_ip
			);
			$chk_sum= self::check_sum( $header, false );
		}
        //echo "check_sum_verify:";
		//get_asc($this->check_sum( $header ));

		return $header . $data;
	}

	/* TCP header */
	/*
	srcPort: 2Bytes
	destPort: 2Bytes
	seqNum: 4Bytes
	ackNum: 4Bytes
	headerLenAndFlag: 2Byte			==> 4Bits(Len)+6Bits(reserved)+(U,ACK,PSH,RST,SYN,FIN)
	windowSize: 2Bytes
	checkSum: 2Bytes 			==> tcp header + tcp data
	urgentPoint: 2Bytes
	*/
	/* 
	 * opt: kind(8bit)+len(8bit)+content
	 */
	public static function TCPUnpack( $packet ){
		$arr = unpack("nsrc_port/ndest_port/Nseq_num/Nack_num/nhdrlen_flag/nwindow_size/ncheck_sum/nurgent/xtcp_data", $packet."*" );
		$arr['header_len'] = ($arr['hdrlen_flag'] >> 12 ) << 2;
		$flag = $arr['hdrlen_flag'] & 0x3f; 
		unset( $arr['hdrlen_falg'] );

		$arr['FIN'] = ($flag & 0x01) ? true : false;
		$arr['SYN'] = (($flag>>1) & 0x01) ? true : false;
		$arr['RST'] = (($flag>>2) & 0x01) ? true : false;
		$arr['ACK'] = (($flag>>4) & 0x01) ? true : false;

		$arr['tcp_data'] = strlen($packet) == $arr['header_len'] ? '': substr( $packet, $arr['header_len'] );
		return $arr;
	}

	public static function TCPPacket( $src_ip, $dest_ip, $src_port, $dest_port, $seq_num, $ack_num, $flag, 
				$tcp_data, $timestamp=null, $ts_echo=null ){

		$window_size = 6000;
		$chk_sum = 0;
		$header_len = 20 >> 2;
		$header_option = "";
		if( $timestamp !== null ){
			$header_option = pack("CCNNn", 8, 10, $timestamp, $ts_echo, 0);
			$header_len = (20+12) >> 2;
		}
		$i = 2;
		while( $i -- ){
				$tcp_header = pack("nn"."N"."N"."nn"."nn",
				$src_port, $dest_port,
				$seq_num,
				$ack_num,
				($header_len << 12) + $flag, $window_size,
				str2int($chk_sum,2), 0
				//$chk_sum, 0
				) 
				. $header_option;
			$ps_header = pack("NNCCn",
				$src_ip, $dest_ip,
				0, getprotobyname("tcp"),
				strlen($tcp_header)+strlen($tcp_data)
			);
			$chk_sum = self::check_sum( $ps_header . $tcp_header . $tcp_data );
		}
		$packet = $tcp_header . $tcp_data;
		return $packet;
	}

	private static function check_sum( $data, $need_pack=false ){
	    if( strlen($data)%2 ){
	    	$data .= "\x00";
	    }
	    $bits = unpack("n*", $data );	
	    $chk_sum = array_sum( $bits );
	    while( $chk_sum >> 16 ){
		    $chk_sum = ($chk_sum >> 16) + ($chk_sum & 0xffff);
	    }
             $chk_sum = 0xffff & ~$chk_sum;
	    if(  true || $need_pack ){
                $chk_sum = pack("n*", $chk_sum );
                return $chk_sum;
         }
	    return $chk_sum;
   	}
}




$TCP_SYN = 1<<1;
$TCP_ACK = 1<<4;
$TCP_FIN = 1;
$tcp_data = "";
$tcp_flag = 0 | $TCP_SYN;
$timestamp = 100000000; 
//$timestamp = null;
$ts_echo = 0;

/*
$raw_socket = socket_create( AF_INET, SOCK_RAW, getprotobyname("tcp") ); 
socket_set_nonblock( $raw_socket );
$one = 1;
$set_ret = socket_set_option( $raw_socket, getprotobyname("ip"), 3, $one); 
$conn_ret = socket_connect( $raw_socket, $dest_ip_fake, $dest_port );

while( true ){
	break;
	$read_ret = socket_read( $raw_socket, 65535 );
	if( $read_ret !== false ){
	echo json_encode( IPUnpack( $read_ret ) ) . "\n";
	}
}
*/


$g_send_socket = $raw_socket_1 = new RawSocket();
$s1 = $raw_socket_1->create_send_socket($dest_ip);
/*
$raw_socket_1->socket_send( $src_ip, $src_port, $dest_ip, $dest_port,
        $seq_num, $ack_num, $tcp_flag,
        $tcp_data, $timestamp, $ts_echo );
*/

$raw_socket_2 = new RawSocket( );
$s2 = $raw_socket_2->create_listen_socket( /*local_ip*/ $local_ip, /*$dest_ip*/  null );
/*
while( true ){
	
	$read_ret = $raw_socket_2->socket_read_raw( );
	if( $read_ret !== false && null !== $read_ret ){
		echo json_encode( $read_ret ) . "\n";
	}
}
*/



$raw_socket_3= new RawSocket( );
$s3 = $raw_socket_3->create_listen_udp( /*local_ip*/ $local_ip, /*$dest_ip*/  $local_udp_port );
/*
while( true ){
	
	$read_ret = $raw_socket_3->socket_read_udp( );
	if( $read_ret !== false && null !== $read_ret ){
		echo json_encode( $read_ret ) . "\n";
	}
}
*/



$socket_map = array(
	(string)($s1) => array('type'=>'send_raw', 'obj'=>$raw_socket_1),
	(string)($s2) => array('type'=>'read_raw', 'obj'=>$raw_socket_2),
	(string)($s3) => array('type'=>'read_udp', 'obj'=>$raw_socket_3),
);

// var_dump( $socket_map );


/*
$raw_socket_4 = new RawSocket();
$raw_socket_4->create_listen_socket($local_ip);
while( true ){	
	$read_ret = $raw_socket_4->socket_recv_raw(  );
	if( $read_ret !== false && null !== $read_ret ){
		echo json_encode( $read_ret ) . "\n";
	}
}
*/

$g_real_map = array();
$g_fake_map = array();

$g_fake_request_info = array(
	'real_ip'=>'',
	'real_port'=>'',
	'fake_ip'=>'',
	'fake_port'=>'',
	'send_data_len'=>0,
	'receive_data_len'=>0,
	'send_data_count'=>0,
	'init_seq'=>0,
	'next_seq'=>0,
	'next_ack'=>0,
	'need_deal'=>array(),
	'need_deal_seq_min'=>0,
	'need_deal_seq_next'=>0,
	'update_time'=>0,
	'state'=>'INIT',	
	'update_time'=>0,	
	'real_next_seq'=>0,					// 与 next_seq 同步更新
	'real_init_seq'=>0,
	'test_init_seq'=>0,					// 测试服务器开始序号
	'latest_ack'=>0,					// 测试服务器最后的确认
);

function add_need_deal_packet( & $fake_info, & $ip_packet, $real_time_request=true ){
	global $g_cur_version;		

	$ip_packet['cur_version'] = $g_cur_version;		
	$ip_packet['enqueue_state'] = true;
	isset( $ip_packet['enqueue_count'] )? $ip_packet['enqueue_count']++ : $ip_packet['enqueue_count'] = 1;
	$fake_info['need_deal'][] = $ip_packet;	
	
	if( count($fake_info['need_deal']) == 1 ){
		$fake_info['need_deal_seq_min'] = $fake_info['need_deal_seq_max'] = $ip_packet['tcp']['seq_num'];
		$fake_info['need_deal_seq_next'] = $ip_packet['tcp']['seq_num'];
	}else if( $real_time_request && $fake_info['need_deal_seq_next'] != $ip_packet['tcp']['seq_num'] ){
		echo "ip_packet:" . json_encode( $ip_packet ) . "\n";
		$tmp_fake_info = $fake_info;
		$tmp_fake_info['need_deal_count'] = count( $tmp_fake_info['need_deal'] );
		unset( $tmp_fake_info['need_deal'] );
		echo "fake_info: " . json_encode( $tmp_fake_info ) . "\n";
		echo ( "FATAL, may be lost packet\n" );
	}
	if( $real_time_request ){
		$fake_info['need_deal_seq_next'] = get_next_seq( $fake_info['need_deal_seq_next'], $ip_packet['tcp']['data_len'] ) ;
	}
	
}

$g_cur_version = 1;

function get_offset( $cur, $old ){
	$offset = $cur - $old;
	if( $offset < 0 && abs($offset) > (0xffffffff>>1) ){
		$offset += (0xffffffff+1);
	}
	return $offset;
}

function get_next_seq( $cur, $inc ){
	$next = $cur + $inc;
	if( $next > 0xffffffff ){
		$next %= (0xffffffff+1);
	}
	return $next;
}

function is_before( $cur, $old ){
	$diff = $cur - $old;
	if( $diff > 0 ) return true;	
	if( $diff < -(0xffffffff>>1) ) return true;
	return false;	
}

function is_after_or_equal( $cur, $old ){
	$diff = $cur - $old;
	if( $diff >= 0 ) return true;	
	if( $diff < -(0xffffffff>>1) ) return true;
	return false;	
}

function is_before_or_equal( $cur, $old ){
	return is_after_or_equal( $old, $cur );
}

function is_after( $cur, $old ){
	return is_before( $old, $cur );
}

function is_real_after( $fake_info, $cur ){
	$old = $fake_info['real_next_seq'];
	$diff = $cur - $old;
	if( $diff > 0 ) return true;
	//if( $diff < 0 && abs($diff) > (0xffffffff>>1) ) return true;
	if( $diff < -(0xffffffff>>1) ) return true;
	return false;	
}
function is_real_before( $fake_info, $cur ){
	return  ! is_real_after($fake_info, $cur );
}
function is_real_equal( $fake_info, $cur ){
	return $fake_info['real_next_seq'] == $cur;
}
function update_next_seq( & $fake_info, $inc ){
	$fake_info['next_seq'] = get_next_seq( $fake_info['next_seq'] , $inc );
	$fake_info['real_next_seq'] = get_next_seq( $fake_info['real_next_seq'] , $inc );	
	
}
function update_next_ack( & $fake_info, $inc ){
	$fake_info['next_ack'] = get_next_seq( $fake_info['next_ack'] , $inc );
}

/*
function get_fake_offset( $fake_info, $cur ){
	return get_offset( $cur , $fake_info['next_seq'] );
}

function get_real_offset( $fake_info, $cur ){
	return get_offset( $cur , $fake_info['real_next_seq'] );
}
*/

function set_update_time( & $fake_info ){
	$cur = intval( microtime( true ) * 1000 );
	$fake_info['update_time'] = $cur;
}


function deal_delay_data( & $fake_info, $not_crontab = true ){
	global $g_cur_version;
	if( $not_crontab ) {		
		set_update_time( $fake_info );
	}
	if( count($fake_info['need_deal']) < 1 ) return;
	if( $fake_info['state'] !== 'ESTABLISHED' ) return;

	

	// return;
		
	echo "count_need_deal: " . count($fake_info['need_deal']) . "\n";
	$tmp_fake_info = $fake_info;
	unset( $tmp_fake_info['need_deal'] );
	echo "fake_info: " . json_encode( $tmp_fake_info ) . "\n";
	echo "need_deal_BEGIN\n";
	$g_cur_version ++;
	$latest_undeal_version = $g_cur_version;
	$count = 0;
	while( true ){		
		$count ++;
		echo "deal_delay_data, packet count in buffer:" . count($fake_info['need_deal']) . "\n";
		// echo "deal_delay_data: $count\n";
		$ip_packet = array_shift($fake_info['need_deal']);
		$ip_packet['enqueue_state'] = false;		
			
		echo "fake_info: " . json_encode( $tmp_fake_info ) . "\n";
		echo sprintf("real_seq:[%d], real_data_len:[%d]\n" ,$ip_packet['tcp']['seq_num'], $ip_packet['tcp']['data_len']);

		receive_real_request( $ip_packet, "need_deal" );	
		echo "ip_packet: " . json_encode( $ip_packet ) . "\n";
		echo "ip_packet_enqueue_state:" . $ip_packet['enqueue_state'] . "\n"; 
		echo "enqueue_count:" . $ip_packet['enqueue_count'] . "\n";
		if( $ip_packet['cur_version'] >= $latest_undeal_version ){
			break;
		}	
		break;
		if( 0 == count($fake_info['need_deal']) ){
			break;
		}
	}
	echo "need_deal_END\n";
}

function crontab_task( ){
	global $g_fake_map, $g_real_map;

	// echo "crontab_task..............\n";
	$now = intval( microtime(true)*1000 );

	if( count( $g_real_map ) == 0 ){
		// echo "g_real_map is emtpy\n";
	}else{
		// echo "g_real_map count:" . count( $g_real_map ) . "\n";
	}

	foreach( $g_real_map as & $fake_info ){		

/*
		echo "crontab_task................................\n";
		$update_time = $fake_info['update_time'];
		echo "now[ $now ], update_time[ $update_time ]\n";
		echo sprintf("now_update_time_diff:%d\n", $now - $update_time);		
*/
		$diff = $now - $fake_info['update_time'];
		// echo "diff: " . $diff . "\n";

		if( count($fake_info['need_deal']) > 0 ){
			//&& ($now - $fake_info['update_time'] > -1) ){				
			deal_delay_data( $fake_info, false );			
		}

		// echo $fake_info['state'] . "\n";		
		if( $fake_info['state'] == 'TIME_WAIT' && $diff > 1000*3 ){
			$real_ip_port_key = $fake_info['real_ip'] .":" . $fake_info['real_port'];
			$fake_ip_port_key = $fake_info['fake_ip'] .":" . $fake_info['fake_port'];
			unset( $g_real_map[ $real_ip_port_key ] );
			unset( $g_fake_map[ $fake_ip_port_key ] );
			return;
		}else if( $diff > 1000*3 ){
			// var_dump( $fake_info );			
			// die( 0 );
		}
	}
}


function receive_test_server_request( $ip_packet ){
	global $g_real_map, $g_fake_map, $g_real_request_info, $g_fake_request_info;	
	global $g_send_socket;
	global $g_remote_test_ip;
	global $g_cur_version;

	$cur_time = microtime( true );
	
	// echo "receive_test_server_response.....................test_server\n";

	$TCP_SYN = 1<<1;
	$TCP_ACK = 1<<4;
	$TCP_FIN = 1;

	$src_ip = $ip_packet['src_ip'];		$src_port = $ip_packet['tcp']['src_port'];
	$dest_ip = $ip_packet['dest_ip'];	$dest_port = $ip_packet['tcp']['dest_port'];
	$tcp_hdr = $ip_packet['tcp'];
	$flag_syn = $tcp_hdr['SYN'];		$flag_ack = $tcp_hdr['ACK'];
	$flag_fin = $tcp_hdr['FIN'];		$flag_rst = $tcp_hdr['RST'];
	$seq_num = $tcp_hdr['seq_num'];		$ack_num = $tcp_hdr['ack_num'];
	$tcp_data = $tcp_hdr['tcp_data'];
	$tcp_data_len = $tcp_hdr['data_len'];

	if( $src_ip != $g_remote_test_ip ){
		return;
	}

	$fake_ip_port_key = $dest_ip . ":" . $dest_port;
	if( !isset( $g_fake_map[ $fake_ip_port_key ] ) ){
		echo "NOTICE: not set g_fake_map[ fake_ip_port_key ]\n";
		return;
	}

	$fake_info  = & $g_fake_map[ $fake_ip_port_key ];
	$status = $fake_info['state'];

	// 建立连接
	if( $flag_syn === true ){
		if( $status == 'SYN_SEND' ){			
			$fake_info['next_ack'] = $seq_num;	
			$fake_info['latest_ack'] = $ack_num;
			$fake_info['test_init_seq'] = $seq_num;
			update_next_ack( $fake_info, 1 );
			$fake_info['state'] = 'ESTABLISHED';

			echo "--- receive test syn ----------------- SEND SYN ACK\n";
			echo sprintf("seq:%d, ack:%d\n", $fake_info['next_seq'], $fake_info['next_ack'] );
			$g_send_socket->socket_send(	$fake_info['fake_ip'],  $fake_info['fake_port'], $src_ip, $src_port, 
						$fake_info['next_seq'], $fake_info['next_ack'], $TCP_ACK,
						'', null, null);			
			deal_delay_data( $fake_info );
			return;
		}else if( $status == 'ESTABLISHED' ){
			echo "WARNING: receive dumplicated syn from test server.\n";
			deal_delay_data( $fake_info );
			return;
		}
		/*
		log2( sprintf("line[%d],%s", __LINE__ , "WARNING: syn from test server, status not match:\nfake_info:" . json_encode( $fake_info ) 
		. ";\nip_packet:" . json_encode($ip_packet)) );
		packet_debug( $ip_packet );
		*/
		return;
	}

	if( is_after_or_equal( $ack_num, $fake_info['latest_ack'] ) ){
		$fake_info['latest_ack'] = $ack_num;
	}
	// $fake_info['latest_ack'] = $ack_num;

	//服务端返回数据.
	if( $flag_ack 
		&& ! $flag_fin
		&& ! $flag_rst
		&& $tcp_data_len > 0 ){
		// && is_before_or_equal( $ack_num, $fake_info['next_seq'] )			// 过时的ack
		// && is_before_or_equal( $seq_num, $fake_info['next_ack'] ) ){		// 过时的seq
		
			if( $seq_num == $fake_info['next_ack'] ){
				update_next_ack( $fake_info, $tcp_data_len );
			}			
			$g_send_socket->socket_send(  $fake_info['fake_ip'],  $fake_info['fake_port'], $src_ip, $src_port, 
					$fake_info['next_seq'], $fake_info['next_ack'], $TCP_ACK,
					'', null, null);
			deal_delay_data( $fake_info );
			return;
	}

	// 测试服务器返回数据包无序, 返回数据包的ack_num可以比fake_info的seq小,因为fake_client 可以在没有收到ack的情况下继续发数据包.
	if( $seq_num != $fake_info['next_ack'] ){
		// echo ( sprintf("line[%d],%s", __LINE__ , "WARNING: receive unsorted packet from test server :\nfake_info:" . json_encode( $fake_info ) 
		// . ";\nip_packet:" . json_encode($ip_packet)) );

		deal_delay_data( $fake_info );
		return;
	}

	// 收到FIN
	if( $flag_fin ){

		// fake client 被动关闭		
		if( $fake_info['state'] == 'ESTABLISHED' ){	
			$fake_info['state'] = 'CLOSE_WAIT';
			//$fake_info['next_ack'] += (1 + $tcp_data_len);
			update_next_ack( $fake_info, 1+$tcp_data_len );

			$fake_info['state'] = 'LAST_ACK';
			$g_send_socket->socket_send(  $fake_info['fake_ip'],  $fake_info['fake_port'], $src_ip, $src_port, 
				$fake_info['next_seq'], $fake_info['next_ack'], $TCP_FIN | $TCP_ACK,
				'', null, null);
			//$fake_info['next_seq']++;	
			update_next_seq( $fake_info, 1 );
			return;

		// fake client 主动关闭
		} else if( $fake_info['state'] == 'TIME_WAIT' ) {

			echo ("WARNING: receive FIN from test server, fake_info state is TIME_WAIT, " . json_encode( $fake_info ) 
		. ";\nip_packet:" . json_encode($ip_packet) ); 
			return;

		// 同时关闭
		}else if( $fake_info['state'] == 'FIN_WAIT_1'){
			// $fake_info['next_ack'] += 1 + $tcp_data_len;
			update_next_ack( $fake_info, 1+$tcp_data_len );
			$g_send_socket->socket_send(  $fake_info['fake_ip'],  $fake_info['fake_port'], $src_ip, $src_port, 
				$fake_info['next_seq'], $fake_info['next_ack'], $TCP_ACK,
				'', null, null);
			$fake_info['state'] = 'CLOSING';			
			return;		

		// 主动关闭对方ACK 先于FIN到达
		}else if( $fake_info['state'] == 'FIN_WAIT_2'){
			//$fake_info['next_ack'] += 1 + $tcp_data_len;
			update_next_ack( $fake_info, 1+$tcp_data_len );
			$g_send_socket->socket_send(  $fake_info['fake_ip'],  $fake_info['fake_port'], $src_ip, $src_port, 
				$fake_info['next_seq'], $fake_info['next_ack'], $TCP_ACK,
				'', null, null);
			$fake_info['state'] = 'TIME_WAIT';			
			return;
		}

		log2( "WARNING: fin from test server, status not match, fake_info:" . json_encode( $fake_info ) );
		packet_debug( $ip_packet );
		return;
	}

	if( $flag_rst ){
		$msg = "recv RST from test server";
		log2( $msg );		
		return;
	}

	
	if( $flag_ack 
		&& ! $flag_fin
		&& ! $flag_rst
		&&  in_array($status,  array('FIN_WAIT_1', 'LAST_ACK', 'CLOSING'))
		&& $tcp_data_len == 0 ){
		// 主动关闭,收到FIN的ack.
		if( $status == 'FIN_WAIT_1' ){
			// echo "receive FIN_WAIT_1 ACK !!!!!!!!!!!!!!!!!!!!!!!!\n";
			$fake_info['state'] = 'FIN_WAIT_2';	
			return;
		}
		// 被动关闭,收到FIN的ack.
		if( $status == 'LAST_ACK' ){
			$fake_info['state'] = 'CLOSED';		
			return;
		}	
		// 同时关闭.
		if( $status == 'CLOSING' ){
			$fake_info['state'] = 'TIME_WAIT';		
			return;
		}	
	}

	if( $flag_ack 
		&& ! $flag_fin
		&& ! $flag_rst
		&&  in_array($status , array( 'ESTABLISHED', 'TIME_WAIT', 'FIN_WAIT_2') ) 
		&& $tcp_data_len == 0 ){
			if( $ack_num <= $fake_info['next_seq'] ){
			}else{
				echo "Notice, receive test server ack num bigger than next_seq:\n" . "fake_info:" . json_encode( $fake_info ) 
				. ";\nip_packet:" . json_encode($ip_packet) . "\n";
			}

			deal_delay_data( $fake_info );
			return;
	}

	if( is_before( $seq_num, $fake_info['next_ack'] )
		&& $tcp_data_len == 0
		&& ! $flag_fin
		&& ! $flag_rst ){
		deal_delay_data( $fake_info );
		return;
	}

	log2( sprintf("line[%d],%s", __LINE__ , "WARNING: can't deal packet from test server, status not match:\nfake_info:" . json_encode( $fake_info ) 
		. ";\nip_packet:" . json_encode($ip_packet)) );
	return;
}


function receive_real_request( & $ip_packet, $type = "" ){
	global $g_real_map, $g_fake_map, $g_real_request_info, $g_fake_request_info;
	global $g_remote_test_ip;
	global $g_send_socket;
	global $g_cur_version;
	global $g_fake_port_indx;
	
	$deal_real_time_request = true;
	$deal_retry = false;

	$TCP_SYN = 1<<1;
	$TCP_ACK = 1<<4;	
	$TCP_PSH = 1<<3;
	$TCP_FIN = 1;

	if( strlen($type) > 0 ){
		echo "Notice: " . $type . "\n";
		$deal_real_time_request = false;
		$deal_retry = true;
	}

	echo sprintf("DEBUG: %d : %d\n", 
		$deal_real_time_request, isset($ip_packet['cur_version']) ? $ip_packet['cur_version'] : -1);

	$src_ip = $ip_packet['src_ip'];		$src_port = $ip_packet['tcp']['src_port'];
	$dest_ip = $ip_packet['dest_ip'];	$dest_port = $ip_packet['tcp']['dest_port'];
	$tcp_hdr = $ip_packet['tcp'];
	$flag_syn = $tcp_hdr['SYN'];		$flag_ack = $tcp_hdr['ACK'];
	$flag_fin = $tcp_hdr['FIN'];		$flag_rst = $tcp_hdr['RST'];
	$seq_num = $tcp_hdr['seq_num'];		$ack_num = $tcp_hdr['ack_num'];
	$tcp_data = $tcp_hdr['tcp_data'];
	$tcp_data_len = strlen( $tcp_data ); 

	$flag_str = '';
	if( $flag_ack ){ $flag_str .= "ACK"; }
	if( $flag_syn ){ $flag_str .= "SYN"; }
	if( $flag_fin ){ $flag_str .= "FIN"; }
	if( $flag_rst ){ $flag_str .= "RST"; }

	$ip_port_key = $src_ip . ":" . $src_port;
	$cur = intval( microtime(true)*1000 );	

	// 建立连接
	if( $flag_syn === true && $flag_ack === false ){
		if( isset($g_real_map[ $ip_port_key ]) ){			
			$fake_info = & $g_real_map[ $ip_port_key ];					
			if( $fake_info['real_init_seq'] != $seq_num ){
				$fake_ip_port_key = $fake_info['fake_ip'] .":" . $fake_info['fake_port'];	
				unset( $g_real_map[ $ip_port_key ] );	
				unset( $g_fake_map[ $fake_ip_port_key ] );
			}else{
				return;
			}
		}

		echo "receive REAL_SYN ,ip_packet:" . json_encode($ip_packet) . "\n";

		//$fake_ip = $src_ip;		$fake_port = $src_port;
		//$fake_ip = "11.11.11.11";		
		$fake_ip = "193.168.56.121";


		$g_fake_port_indx ++;
		if( $g_fake_port_indx > 60000 ){
			$g_fake_port_indx = 20000;
		}
		$fake_port = $g_fake_port_indx;


		$real_ip_port_key = $src_ip .":" . $src_port;
		$fake_ip_port_key = $fake_ip .":" . $fake_port;
		$g_fake_map[ $fake_ip_port_key ] = $g_fake_request_info;
		$g_real_map[ $real_ip_port_key ] = & $g_fake_map[ $fake_ip_port_key ];
		
		
		$fake_info  = & $g_fake_map[ $fake_ip_port_key ];
		$fake_info['real_ip'] = $src_ip;
		$fake_info['real_port'] = $src_port;
		$fake_info['fake_ip'] = $fake_ip;
		$fake_info['fake_port'] = $fake_port;
		$fake_info['real_next_seq'] = $seq_num + 1;
		$fake_info['relative_seq'] = $fake_info['next_seq'] = $fake_info['init_seq'] = 0xffffffff;
		$fake_info['relative_seq'] = $fake_info['next_seq'] = $fake_info['init_seq'] = $seq_num;
		$fake_info['real_relative_seq'] = $fake_info['real_next_seq'] = $fake_info['real_init_seq'] = $seq_num;
		$fake_info['state'] = 'INIT';
		$fake_info['update_time'] = $cur;

		// 发送syn数据包,建立连接.
		/*		
		RawSocket::socket_send( $src_ip, $src_port, $dest_ip, $dest_port, 
					$seq_num, $ack_num, $tcp_flag,
                            $tcp_data,$timestamp, $ts_echo  );
		*/
		$g_send_socket->socket_send( $fake_ip, $fake_port, $g_remote_test_ip, $dest_port, 
					$fake_info['next_seq'], 0, $TCP_SYN,
					'', null, null);
		$fake_info['state'] = 'SYN_SEND';
		/*
		$fake_info['next_seq'] += 1;
		$fake_info['real_next_seq'] += 1;
		*/
		update_next_seq( $fake_info, 1 );
		echo "### receive real syn ################## SEND SYN\n";
		/*
		var_dump( $fake_info );
		var_dump( $g_remote_test_ip );
		die( 0 );
		*/
		
		return;
	}

	$real_ip_port_key = $src_ip .":" . $src_port;
	if( ! isset( $g_real_map[ $real_ip_port_key ] ) ){
		echo "WARNING:  real_ip_port_key, no fake_info, "  . ", ip_packet:" . json_encode($ip_packet);
		return;
	}
	$fake_info  = & $g_real_map[ $real_ip_port_key ];
	$fake_ip = $fake_info['fake_ip'];
	$fake_port = $fake_info['fake_port'];
	
	
	$real_offset =get_offset( $seq_num, $fake_info['real_next_seq'] );

	if( $tcp_data_len > 0 || true ){
		echo sprintf("DEBUG: type[$type], real_offset, data_len, flags, [ %d ][ %d ][ %s ]\n", 
			$real_offset, $tcp_data_len, $flag_str); 
	}

	// fake client 处理较慢 
	//if( is_real_after($fake_info, $seq_num) && $tcp_data_len > 0 ){
	if( $real_offset > 0 && $tcp_data_len > 0 ){
		echo sprintf("NOTICE: test is slow, real_offset[%d], tcp_data_len[%d]\n", $real_offset, $tcp_data_len);
		echo sprintf("line[%d], %s", __LINE__ , "ADD NEED DEAL DATA\n"); 
		/*
		$ip_packet['cur_version'] = $g_cur_version;
		$fake_info['need_deal'][] = $ip_packet;
		*/

		add_need_deal_packet( $fake_info, $ip_packet, $deal_real_time_request );

		if( $deal_real_time_request ){
			echo "will deal_delay_data\n";
			deal_delay_data( $fake_info );
		}
		return;
	}
	if( $real_offset < 0 ){
		echo "REAL_OFFSET: real_offset: $real_offset\n";		
		echo sprintf("WARNING: real_offset < 0, ignore, tcp_data_len[%d]\n", $tcp_data_len);
		echo "FAKE_INFO:" . json_encode( $fake_info ) . "\n";
		echo "IP_PACKET:" . json_encode( $ip_packet ) . "\n";
		// $fake_info['need_deal'][] = $ip_packet;
		return;
	}

	if( $flag_fin ){
		// return;

		if( $tcp_data_len > 0 ){
			echo "FATAL: fin packet with tcp_data_len > 0, ip_packet: " . json_encode( $ip_packet ) . "\n";
			die( 255 );
		}

		if( $fake_info['state'] == 'ESTABLISHED' 
			&& $real_offset == 0 && is_after_or_equal($fake_info['latest_ack'], $fake_info['next_seq']) 
			&& $cur - $fake_info['update_time'] > 30 * 1000												// 30s
			){	
			
			$g_send_socket->socket_send( $fake_ip, $fake_port, $g_remote_test_ip, $dest_port, 
					$fake_info['next_seq'], $fake_info['next_ack'], $TCP_FIN | $TCP_ACK,
					'', null, null);
			$fake_info['state'] = 'FIN_WAIT_1';
			
			// 更新next_seq, real_next_seq
			update_next_seq( $fake_info, 1 );
			echo "### receive real fin ################## SEND FIN\n";
			return;
		}else{		
			// return;
			
			/*
			$tmp = $fake_info;
			$tmp['need_deal'] = count( $tmp['need_deal'] );
			echo  sprintf( "line[%d],%s", __LINE__ , "WARNING: receive FIN from real client, fake info state is not ESTABLISHED, fake_info:" 
				. json_encode( $tmp )
				. ", ip_packet:" . json_encode($ip_packet)  );
			*/			
			
			add_need_deal_packet( $fake_info, $ip_packet, $deal_real_time_request );

			if( $deal_real_time_request ){
				echo "will deal_delay_data\n";
				deal_delay_data( $fake_info );
			}
			return;
		}
		return;
	}

	if( $flag_rst ){
		$msg = "WARNING, recv RST from real client";
		log2( $msg );		
		return;
	}

	if( $flag_ack && $tcp_data_len > 0 ){
		//if( $fake_info['state'] == 'ESTABLISHED' && is_after_or_equal($fake_info['latest_ack'], $fake_info['next_seq']) ){	
		if( $fake_info['state'] == 'ESTABLISHED'  ){	
			echo "### receive real data ################## SEND DATA\n";
			$g_send_socket->socket_send( $fake_ip, $fake_port, $g_remote_test_ip, $dest_port, 
					$fake_info['next_seq'], $fake_info['next_ack'], $TCP_ACK | $TCP_PSH,
					$tcp_data, null, null);		

			$fake_info['send_data_len'] += $tcp_data_len;
			$fake_info['send_data_count'] ++;
			// 更新next_seq, real_next_seq
			update_next_seq( $fake_info, $tcp_data_len );
			if( isset($ip_packet['cur_version']) ){
				echo "NOTICE: deal backup data success\n";
			}
			
		}else{
			echo sprintf("line[%d], %s", __LINE__ , "ADD NEED DEAL DATA\n"); 

			add_need_deal_packet( $fake_info, $ip_packet, $deal_real_time_request );			
		}

		if( $deal_real_time_request ){
			echo "will deal_delay_data 2\n";
			deal_delay_data( $fake_info );
		}
		return;
	}

	if( $flag_ack 
		&& ! $flag_fin
		&& ! $flag_rst
		&& 0 == $tcp_data_len ){
		echo "### receive real ack #################### IGNORE ACK\n";
		return;
	}

	/*
	log2( sprintf("line[%d],%s", __LINE__ , "WARNING: no default, receive packet from client, status not match, fake_info:" 
		. json_encode( $fake_info ) )
		. ", ip_packet:" . json_encode($ip_packet) );
	packet_debug( $ip_packet );
	echo "\n";
	*/

	$tmp = $fake_info;
		$tmp['need_deal'] = count( $tmp['need_deal'] );
		echo  sprintf( "WARNING, need die, line[%d],%s", __LINE__ , ",   fake_info:" 
				. json_encode( $tmp )
				. ",   ip_packet:" . json_encode($ip_packet) . "\n"  );

		return;

	return;
}

function log2( $msg ){
	echo $msg . "\n";
	echo "back_trace: " . json_encode( debug_backtrace() ) . "\n";
}

function packet_debug( $ip_packet ){
	$src_ip = $ip_packet['src_ip'];		$src_port = $ip_packet['tcp']['src_port'];
	$dest_ip = $ip_packet['dest_ip'];	$dest_port = $ip_packet['tcp']['dest_port'];
	$tcp_hdr = $ip_packet['tcp'];
	$flag_syn = $tcp_hdr['SYN'];		$flag_ack = $tcp_hdr['ACK'];
	$flag_fin = $tcp_hdr['FIN'];		$flag_rst = $tcp_hdr['RST'];
	$seq_num = $tcp_hdr['seq_num'];		$ack_num = $tcp_hdr['ack_num'];
	$tcp_data = $tcp_hdr['tcp_data'];
	$ip_port_key = $src_ip . ":" . $src_port;	$cur = time();		
	echo sprintf("read_raw_tcp: %s:%s --> %s:%s\n", $src_ip, $src_port, $dest_ip, $dest_port );
	echo sprintf("\t: seq_num[%d], ack_num[%d], SYN:%d, ACK:%d, FIN:%d, RST:%d\n", 
		$tcp_hdr['seq_num'], $tcp_hdr['ack_num'],
		$tcp_hdr['SYN'], $tcp_hdr['ACK'], $tcp_hdr['FIN'], $tcp_hdr['RST']);
}


$select_read = array( $s2, $s3 );

// $select_read = array( $s3 );

$mq_key = ftok( dirname(__FILE__), 'a');
$mq = msg_get_queue($mq_key, 0666);
msg_remove_queue( $mq );
$mq = msg_get_queue($mq_key, 0666);


$pid = pcntl_fork();
if( $pid == 0 ){
	// tcpcopy进程,从mq里拿数据包,发送伪造请求报文 或 回复test server报文。
	
	while( true ){		
		$ret = msg_receive($mq, 0, $message_type=1, 10240, $m, true, MSG_IPC_NOWAIT);
		if( $ret == false ){
			crontab_task( );
			continue;
		}
		$ip_packet = json_decode( $m, true );

		switch( $ip_packet['from'] ){
					case 'read_raw':
						receive_real_request( $ip_packet );
						break;
					case 'send_raw':
						// echo "need send_raw\n";
						break;
					case 'read_udp':						
						// receive_test_server_packet
						receive_test_server_request( $ip_packet );							
						break;
					default:
						echo "NOTICE:error\n";
						break;
			}		
	}
      
}else{
	// 抓包进程,单纯从socket接收IP报文,防止进程过慢而丢包.

	$need_send = array();
	while( true ){
		$need_read = $select_read;
		
		$select_ret = socket_select( $need_read, $need_write = null, $expect, 1, 0 );
		if( false === $select_ret ){
			echo "socket_last_error_str:" . socket_strerror(socket_last_error()) . "\n";
			continue;
		}
		
		if( $select_ret < 1 ){
			// echo "no need read\n";
			continue;
		}
		// echo "after select\n";
		// echo "need_read:\n";
		if( is_array($need_read) && count($need_read) > 0 ){
			foreach( $need_read as $s ){
				$type = $socket_map[ (string)$s ]['type'];
				$obj = $socket_map[ (string)$s ]['obj'];

				switch( $type ){
					case 'read_raw':
						// echo "need_read_raw\n";
						$read_ret = $obj->socket_read_raw( );
						if( $read_ret === false || null === $read_ret ){
							continue;
							// echo json_encode( $read_ret ) . "\n\n";						
						}else{						
							// echo json_encode( $read_ret ) . "\n\n";
						}

						// receive_real_packet
						// receive_real_request( $read_ret );		
						$ip_packet = & $read_ret;
						$ip_packet['from'] = 'read_raw';
						// $ret = msg_send($mq, 1, json_encode( $ip_packet), true, false, $msg_err );
						$need_send[] = $ip_packet;


						break;
					case 'send_raw':
						// echo "need send_raw\n";
						break;
					case 'read_udp':
						// echo "need read udp --------------------- read udp packet\n";
						$read_ret = $obj->socket_read_udp( );
						if( $read_ret === false || null === $read_ret ){
							continue;
						}else{						
							// echo json_encode( $read_ret ) . "\n\n";
						}

						// receive_test_server_packet
						// receive_test_server_request( $read_ret );	
						$ip_packet = & $read_ret;
						$ip_packet['from'] = 'read_udp';
						// $ret = msg_send($mq, 1, json_encode( $ip_packet), true, false, $msg_err );
						$need_send[] = $ip_packet;

						break;
					default:
						echo "NOTICE:error\n";
						break;
				}
				if( false && $ret == false ){
					$mq_stat = msg_stat_queue( $mq );					
					echo "NOTICE:" . $mq_stat['msg_qnum'] . "\n";
					echo "FATAL: msg_send_ret false\n";
					var_dump( $msg_err );
					//die( "FATAL: msg_send_error" );

					$need_send[] = $ip_packet;
				}
			}//foreach
		}//if
		
		$mq_stat = msg_stat_queue( $mq );		
		if( $mq_stat['msg_qnum'] > 100 ){
			continue;
		}
		
		if( count($need_send) > 0 ){
			foreach( $need_send as $k => $ip_packet ){
				$ret = msg_send($mq, 1, json_encode( $ip_packet), true, false, $msg_err );
				
				if( $ret ==true ){
					unset( $need_send[$k] );

					$mq_stat = msg_stat_queue( $mq );		
					if( $mq_stat['msg_qnum'] > 100 ){
						break;
					}

				}else{					
					break;					
				}
			}
		}

	}//while


    die( 0 );
}





while( true ){
	$need_read = $select_read;
	
	$select_ret = socket_select( $need_read, $need_write = null, $expect, 1, 0 );
	if( false === $select_ret ){
		echo "socket_last_error_str:" . socket_strerror(socket_last_error()) . "\n";
		continue;
	}
	
	if( $select_ret < 1 ){
		// echo "no need read\n";
		continue;
	}
	// echo "after select\n";
	// echo "need_read:\n";
	if( is_array($need_read) && count($need_read) > 0 ){
		foreach( $need_read as $s ){
			$type = $socket_map[ (string)$s ]['type'];
			$obj = $socket_map[ (string)$s ]['obj'];

			switch( $type ){
				case 'read_raw':
					// echo "need_read_raw\n";
					$read_ret = $obj->socket_read_raw( );
					if( $read_ret === false || null === $read_ret ){
						continue;
						// echo json_encode( $read_ret ) . "\n\n";						
					}else{						
						// echo json_encode( $read_ret ) . "\n\n";
					}

					// receive_real_packet
					receive_real_request( $read_ret );					

					break;
				case 'send_raw':
					// echo "need send_raw\n";
					break;
				case 'read_udp':
					// echo "need read udp --------------------- read udp packet\n";
					$read_ret = $obj->socket_read_udp( );
					if( $read_ret === false || null === $read_ret ){
						continue;
					}else{						
						// echo json_encode( $read_ret ) . "\n\n";
					}

					// receive_test_server_packet
					receive_test_server_request( $read_ret );		

					break;
				default:
					echo "NOTICE:error\n";
					break;
			}
		}
	}
}


die( 0 );





function get_asc( $str ){
	echo "get_asc:\n";
	$arr = str_split( $str,1 );	 
	foreach( $arr as $v ){
		echo ord( $v ) . "," . bin2hex($v) . "\n";
	}
	echo "\n";
}

function str2int( $str, $size=4 ){
	$ret = 0;	
	for( $i=0; $i<$size; $i++){
		$ret = ($ret << 8) + ord($str[$i ]);
	}
	return $ret;
}
ログイン後にコピー

2.intercept.py

テストサーバーから返されたデータを取得し、tcpcopy プロセスに送信します。

#!/usr/bin/python

import socket
import struct
import binascii

remote_addr = ("192.168.56.101", 20000)
address=('localhost',20000)
udp_s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
#udp_s.bind( address )
#print udp_s.getsockname()

s=socket.socket(socket.PF_PACKET,socket.SOCK_RAW,socket.htons(0x0800))


while True: 
    pkt = s.recvfrom(65532)
    #print pkt[1]
 
    ethernetHeader=pkt[0][0:14]
 
    eth_hdr = struct.unpack("!6s6s2s",ethernetHeader)
    #print eth_hdr
<span style="white-space:pre">	</span>
    eth_hdr2 = struct.unpack("!6s6sH",ethernetHeader)


    if( eth_hdr2[2] != 0x0800 ):
        continue
    print 'IP:'
    print len(pkt[0]) 
    ipHeader = pkt[0][14:34]
 
    ip_hdr = struct.unpack("!12s4s4s",ipHeader)
    #ip_header_len = 4* (ord(ip_hdr[0][0]) & 0xf)
    ip_header_len = (struct.unpack("!1B11x", ip_hdr[0])[0] & 0x0f) << 2;
    print ip_header_len
    ip_packet_len = (struct.unpack("!2xH8x", ip_hdr[0])[0] & 0xffff);
    print ip_packet_len;
    
    proto = struct.unpack("!9x1B2x", ip_hdr[0])[0]
    
    if proto != 0x06:
        continue;
      
    print proto
    proto_map={1:"ICMP", 2:"IGMP", 6:"TCP", 17:"UDP", 89:"OSPF" }
    print proto_map[proto]


    print ip_header_len
    print ip_packet_len
    
    tcp_packet = pkt[0][14+ip_header_len:14+ip_packet_len]
    print len(tcp_packet)
    tcp_header = tcp_packet[0:20]
    tcp_hdr_len  = 4*(struct.unpack("!12x1H6x", tcp_header)[0] >> 12)
    print tcp_hdr_len 


    ip_tcp_header = pkt[0][14:14+ip_header_len+tcp_hdr_len]
    print len(ip_tcp_header)
    print "real_len:%d, ip:%d, tcp:%d" % ((ip_header_len + tcp_hdr_len), ip_header_len, tcp_hdr_len )
    print remote_addr
    ret = udp_s.sendto( ip_tcp_header, remote_addr )
    print ret
 
    print "Source IP address:"+socket.inet_ntoa(ip_hdr[1])
 
    print "Destination IP address:"+socket.inet_ntoa(ip_hdr[2])
 
    # tcpHeader = pkt[0][34:54]
 
    # tcp_hdr = struct.unpack("!HH16s",tcpHeader)

ログイン後にコピー

3. 環境設定:

1) 環境の紹介:
101: centos4、オンラインサーバー。
104: centos5、偽ルーター。
102: centos4-1、テストサーバー。
103: centos4-2、実際のクライアント。

2) 102 ルーティングを設定します:
ルート追加 -ホスト 193.168.56.121 GW 192.168.56.104

4. 注:

104 で ICMP リダイレクトを無効にします。

もともと、趣味で PHP を使用して生のソケットを書きたかったのですが、tcpcopy コードを見て PHP で書いてみました...


このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

PHPおよびPython:さまざまなパラダイムが説明されています PHPおよびPython:さまざまなパラダイムが説明されています Apr 18, 2025 am 12:26 AM

PHPは主に手順プログラミングですが、オブジェクト指向プログラミング(OOP)もサポートしています。 Pythonは、OOP、機能、手続き上のプログラミングなど、さまざまなパラダイムをサポートしています。 PHPはWeb開発に適しており、Pythonはデータ分析や機械学習などのさまざまなアプリケーションに適しています。

PHPとPythonの選択:ガイド PHPとPythonの選択:ガイド Apr 18, 2025 am 12:24 AM

PHPはWeb開発と迅速なプロトタイピングに適しており、Pythonはデータサイエンスと機械学習に適しています。 1.PHPは、単純な構文と迅速な開発に適した動的なWeb開発に使用されます。 2。Pythonには簡潔な構文があり、複数のフィールドに適しており、強力なライブラリエコシステムがあります。

Windows 8でコードを実行できます Windows 8でコードを実行できます Apr 15, 2025 pm 07:24 PM

VSコードはWindows 8で実行できますが、エクスペリエンスは大きくない場合があります。まず、システムが最新のパッチに更新されていることを確認してから、システムアーキテクチャに一致するVSコードインストールパッケージをダウンロードして、プロンプトとしてインストールします。インストール後、一部の拡張機能はWindows 8と互換性があり、代替拡張機能を探すか、仮想マシンで新しいWindowsシステムを使用する必要があることに注意してください。必要な拡張機能をインストールして、適切に動作するかどうかを確認します。 Windows 8ではVSコードは実行可能ですが、開発エクスペリエンスとセキュリティを向上させるために、新しいWindowsシステムにアップグレードすることをお勧めします。

VSCODE拡張機能は悪意がありますか? VSCODE拡張機能は悪意がありますか? Apr 15, 2025 pm 07:57 PM

VSコード拡張機能は、悪意のあるコードの隠れ、脆弱性の活用、合法的な拡張機能としての自慰行為など、悪意のあるリスクを引き起こします。悪意のある拡張機能を識別する方法には、パブリッシャーのチェック、コメントの読み取り、コードのチェック、およびインストールに注意してください。セキュリティ対策には、セキュリティ認識、良好な習慣、定期的な更新、ウイルス対策ソフトウェアも含まれます。

Visual StudioコードはPythonで使用できますか Visual StudioコードはPythonで使用できますか Apr 15, 2025 pm 08:18 PM

VSコードはPythonの書き込みに使用でき、Pythonアプリケーションを開発するための理想的なツールになる多くの機能を提供できます。ユーザーは以下を可能にします。Python拡張機能をインストールして、コードの完了、構文の強調表示、デバッグなどの関数を取得できます。デバッガーを使用して、コードを段階的に追跡し、エラーを見つけて修正します。バージョンコントロールのためにGitを統合します。コードフォーマットツールを使用して、コードの一貫性を維持します。糸くずツールを使用して、事前に潜在的な問題を発見します。

ターミナルVSCODEでプログラムを実行する方法 ターミナルVSCODEでプログラムを実行する方法 Apr 15, 2025 pm 06:42 PM

VSコードでは、次の手順を通じて端末でプログラムを実行できます。コードを準備し、統合端子を開き、コードディレクトリが端末作業ディレクトリと一致していることを確認します。プログラミング言語(pythonのpython your_file_name.pyなど)に従って実行コマンドを選択して、それが正常に実行されるかどうかを確認し、エラーを解決します。デバッガーを使用して、デバッグ効率を向上させます。

Python vs. JavaScript:学習曲線と使いやすさ Python vs. JavaScript:学習曲線と使いやすさ Apr 16, 2025 am 12:12 AM

Pythonは、スムーズな学習曲線と簡潔な構文を備えた初心者により適しています。 JavaScriptは、急な学習曲線と柔軟な構文を備えたフロントエンド開発に適しています。 1。Python構文は直感的で、データサイエンスやバックエンド開発に適しています。 2。JavaScriptは柔軟で、フロントエンドおよびサーバー側のプログラミングで広く使用されています。

vscodeはMacに使用できますか vscodeはMacに使用できますか Apr 15, 2025 pm 07:36 PM

VSコードはMacで利用できます。強力な拡張機能、GIT統合、ターミナル、デバッガーがあり、豊富なセットアップオプションも提供しています。ただし、特に大規模なプロジェクトまたは非常に専門的な開発の場合、コードと機能的な制限がある場合があります。

See all articles