首页  >  文章  >  后端开发  >  PHP实现流量复制tcpcopy ( php + python)

PHP实现流量复制tcpcopy ( php + python)

WBOY
WBOY原创
2016-06-20 13:04:531689浏览

看了tcpcopy的源码,php 和 python 都可以操作raw socket,因此,用php 和 python 实现了tcpcopy,代码比较简单

tcpcopy关键点,理认上来说,只要得到tcp请求报文中数据部分,在ip层转发到测试服务器,即可实现流量复制。

因此,只需要维护tcp会话即可维护一个假的tcp连接,骗过测试服务器即可。

代码实现,主要以TCP状态机为基础,同时考虑抓包的无序问题,结合tcpdump调试,用php实现tcpcopy,用python实现intercept。

以下代码,经测试,客户机发起5000个http请求(大约50个长连接),流量全部会复制到测试机上。

其实,tcpcopy与lvs、nat、运营商流量劫持工作原理类似,都是通过欺骗tcp协议栈达到目的; 同理,通过在应用层处理原始套接字,也可以实现lvs负载均衡功能(后续可能会尝试)。

Code:

1. tcpcopy.php

两个进程工作,一个进程负责抓包并放入queue中; 另一个进程负责消费抓到的数据包,因此对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) 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 ) >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 > 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 = 1create_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  (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 >1) ) return true;
	return false;	
}

function is_after_or_equal( $cur, $old ){
	$diff = $cur - $old;
	if( $diff >= 0 ) return true;	
	if( $diff >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  (0xffffffff>>1) ) return true;
	if( $diff >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']) = $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 = 1socket_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  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 "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  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  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

<p> </p>

<p> </p>

<p> </p>

<p>2. intercept.py</p>

<p>抓到测试服务器返回的数据,将其发送到tcpcopy进程。</p>

<pre class="brush:php;toolbar:false">#!/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) > 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, online server.
104: centos5, fake router.
102: centos4-1, test server.
103: centos4-2, real client.

2). 102设置路由:
route add -host 193.168.56.121 gw 192.168.56.104

4. 注意:

104 上禁用 ICMP redirects。

原本想用PHP写raw socket玩玩的,原来看过tcpcopy代码,用php写了个试试,没想还跑通了...


声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn