URL redirection,或称网址重定向或URL重定向,是指当使用者浏览某个网址时,将他导向到另一个网址的技术。常用在把一串很长的网站网址,转成较短的网址。因为当要传播某网站的网址时,常常因为网址太长,不好记忆;又有可能因为换了网络的免费网页空间,网址又必须要变更,不知情的使用者还以为网站关闭了。这时就可以用网络上的转址服务了。这种方法还可以用在广告推送及拦截上, 最常见的就是电信使用的了。
在技术上, URL重定向可以很多种方法实现, 下面介绍其中常用的几种.
方法一:
通过返回一个简单的HTML页面, 采用自刷新方式,将页面重新引至新URL页面. 示例:
<html><head>
<meta http-equiv="Refresh" content="0; url=http://www.example.com/">
</head><body>
<p>Please follow <a href="http://www.example.com/">link</a>!</p>
</body></html>
方法二:
通过返回一个HTTP refresh header, 进行重定向. 示例:
HTTP/1.1 200 ok
Refresh: 0; url=http://www.example.com/
Content-type: text/html
Content-length: 78
Please follow <a href="http://www.example.com/">link</a>!
方法三:
通过HTTP 状态码301 Moved Permanently 永久重定向, 示例:
HTTP/1.1 301 Moved Permanently
Location: http://www.example.org/
Content-Type: text/html
Content-Length: 174
<html>
<head>
<title>Moved</title>
</head>
<body>
<h1>Moved</h1>
<p>This page has moved to <a href="http://www.example.org/">http://www.example.org/</a>.</p>
</body>
</html>
经过个人测试, 发现采用HTTP 状态码301 Moved Permanently方式速度比较快.
以下是内核实现源码, 测试的时候有个小bug 把mac地址填错了, 结果调试了N久,还发帖了, 最终自己才发现, 真是惭愧.
/*****************************************************************************/
#define OPTION_SACK_ADVERTISE (1 << 0)
#define OPTION_TS (1 << 1)
#define OPTION_MD5 (1 << 2)
struct tcp_out_options {
u8 options; /* bit field of OPTION_* */
u8 ws; /* window scale, 0 to disable */
u8 num_sack_blocks; /* number of SACK blocks to include */
u16 mss; /* 0 to disable */
__u32 tsval, tsecr; /* need to include OPTION_TS */
};
void _tcp_parse_options(struct tcphdr *th,
struct tcp_options_received *opt_rx,
int estab);
struct sk_buff* tcp_newpack( u32 saddr, u32 daddr,
u16 sport, u16 dport,
u32 seq, u32 ack_seq,
const struct tcp_out_options *opts,
u8 *msg, int len );
int _tcp_send_pack( struct sk_buff *skb, struct iphdr *iph,
struct tcphdr *th, gbuffer_t *p );
#ifndef MAX_URL_LEN
#define MAX_URL_LEN 253
#endif
#define DEFAULT_REDIRECT_URL "127.0.0.1/"
int http_build_redirect_url( const char *url, gbuffer_t *p );
int http_send_redirect(struct sk_buff *skb, struct iphdr *iph,
struct tcphdr *th, const char *url);
int _http_send_redirect(struct sk_buff *skb, struct iphdr *iph,
struct tcphdr *th );
int setup_redirect_url( const char *url );
void clear_redirect_url(void);
int redirect_url_init(void);
void redirect_url_fini(void);
char *get_redirect_url(void);
/*****************************************************************************/
static char fqdn_redirect_url[MAX_URL_LEN + 1] = {0};
static gbuffer_t *url_redirect_data = NULL;
static gbuffer_t *url_redirect_default = NULL;
static spinlock_t url_redirect_lock;
/*
* 初始化默认重定向DEFAULT_REDIRECT_URL HTML数据
*/
int redirect_url_init(void)
{
spin_lock_init( &url_redirect_lock );
url_redirect_default = __gbuffer_alloc();
if ( NULL == url_redirect_default ) {
DBG_ERROR( verbose,
"__gbuffer_alloc for default redirect URL failed.\n" );
return -1;
}
if ( http_build_redirect_url( DEFAULT_REDIRECT_URL,
url_redirect_default ) ){
_gbuffer_free( url_redirect_default );
url_redirect_default = NULL;
DBG_ERROR( verbose,
"http_build_redirect_url %s failed.\n",
DEFAULT_REDIRECT_URL );
return -1;
}
return 0;
}
/*
* 释放重定向数据
*/
void redirect_url_fini(void)
{
gbuffer_t *p = NULL;
_gbuffer_free( url_redirect_default );
url_redirect_default = NULL;
p = url_redirect_data;
rcu_assign_pointer( url_redirect_data, NULL );
_gbuffer_free( p );
}
/*
* 设置重定向URL, 构建重定向数据
*/
int setup_redirect_url( const char *url )
{
int len;
gbuffer_t *p = NULL, *ptr;
if ( NULL == url )
return -1;
len = strlen(url);
if ( len > MAX_URL_LEN )
return -1;
memset( fqdn_redirect_url, 0x0, MAX_URL_LEN );
memcpy( fqdn_redirect_url, url, len );
p = __gbuffer_alloc();
if ( NULL == p ) {
DBG_ERROR( verbose,
"__gbuffer_alloc failed.\n" );
return -1;
}
if ( http_build_redirect_url( fqdn_redirect_url,
p ) ) {
DBG_ERROR( verbose,
"http_build_redirect_url %s failed.\n",
fqdn_redirect_url );
_gbuffer_free( p );
return -1;
}
DBG_INFO( verbose,
"Setup Redirect URL http://%s\n", fqdn_redirect_url );
spin_lock_bh( &url_redirect_lock );
ptr = url_redirect_data;
rcu_assign_pointer( url_redirect_data, p );
spin_unlock_bh( &url_redirect_lock );
synchronize_rcu();
_gbuffer_free( ptr );
}
/*
* 清除重定向数据
*/
void clear_redirect_url(void)
{
gbuffer_t *ptr;
memset( fqdn_redirect_url, 0x0, MAX_URL_LEN );
spin_lock_bh( &url_redirect_lock );
ptr = url_redirect_data;
rcu_assign_pointer( url_redirect_data, NULL );
spin_unlock_bh( &url_redirect_lock );
synchronize_rcu();
_gbuffer_free( ptr );
}
/*
* 获取重定向数据缓冲
*/
char *get_redirect_url(void)
{
if ( 0 == *fqdn_redirect_url )
return DEFAULT_REDIRECT_URL;
return fqdn_redirect_url;
}
/*
* 重定向HTML的几种格式
*/
#if 0
const char *http_redirect_header =
"HTTP/1.1 200 OK\r\n"
//"Date: Fri, 23 Apr 2010 12:54:40 GMT\r\n"
//"Server: Apache/2.2.15 (Win32)\r\n"
"Content-Type: text/html; charset=iso-8859-1\r\n"
"Content-length: %d\r\n"
"\r\n";
const char *http_redirect_body =
"<html><head>\n"
"<meta http-equiv=\"Refresh\" content=\"0; url=http://%s\">\n"
"</head><body>\n"
"<p>Please follow <a href="\" mce_href="\""http://%s\">link</a>!</p>\n"
"</body></html>\n";
#elif 0
const char *http_redirect_header =
"HTTP/1.1 200 OK\r\n"
"Refresh: 0; url=http://%s\r\n"
"Content-Type: text/html; charset=iso-8859-1\r\n"
"Content-length: %d\r\n"
"\r\n";
const char *http_redirect_body =
"Please follow <a href="\" mce_href="\""http://%s\">link</a>!";
#else
const char *http_redirect_header =
"HTTP/1.1 301 Moved Permanently\r\n"
"Location: http://%s\r\n"
"Content-Type: text/html; charset=iso-8859-1\r\n"
"Content-length: %d\r\n"
"\r\n";
const char *http_redirect_body =
"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"
"<html><head>\n"
"<title>301 Moved Permanently</title>\n"
"</head><body>\n"
"<h1>Moved Permanently</h1>\n"
"<p>The document has moved <a href="\" mce_href="\""http://%s\">here</a>.</p>\n"
"</body></html>\n";
#endif
/*
* 构建一个重定向HTML缓冲
*/
int http_build_redirect_url( const char *url, gbuffer_t *p )
{
char *header = NULL;
char *body = NULL;
char *buf = NULL;
int header_len, body_len;
int rc = -1;
if ( NULL == p )
goto _out;
header = kzalloc( PATH_MAX, GFP_KERNEL );
if ( NULL == header ) {
goto _out;
}
body = kzalloc( PATH_MAX, GFP_KERNEL );
if ( NULL == body ){
goto _out;
}
body_len = snprintf( body, PATH_MAX,
http_redirect_body,
url
//,url
);
body_len = strlen( body );
//DBG_INFO( verbose, "Length=%d\nBody:%s\n", body_len, body );
header_len = snprintf( header, PATH_MAX,
http_redirect_header,
url,
body_len
);
//DBG_INFO( verbose, "Header:%s\n", header );
buf = kzalloc( header_len + body_len, GFP_KERNEL );
if ( NULL == buf ){
goto _out;
}
p->buf = buf;
p->len = header_len + body_len;
memcpy( buf, header, header_len );
memcpy( buf + header_len, body, body_len );
#if 0
{
int i = 0;
for( ; i < p->len; i ++ ){
printk( "%c", buf[i] );
}
printk( "\n" );
}
#endif
rc = 0;
_out:
if ( header ){
kfree( header );
}
if ( body ) {
kfree( body );
}
return rc;
}
/*
* 构建一个tcp数据包
*/
struct sk_buff* tcp_newpack( u32 saddr, u32 daddr,
u16 sport, u16 dport,
u32 seq, u32 ack_seq,
const struct tcp_out_options *opts,
u8 *msg, int len )
{
struct sk_buff *skb = NULL;
int total_len, eth_len, ip_len, header_len;
int tcp_opt_len, tcp_len;
struct tcphdr *th;
struct iphdr *iph;
__wsum tcp_hdr_csum;
__be32 *ptr = NULL;
tcp_opt_len = 0;
if ( likely(OPTION_TS & opts->options) ){
tcp_opt_len += TCPOLEN_TSTAMP_ALIGNED;
}
// 设置各个协议数据长度
tcp_len = len + sizeof( *th ) + tcp_opt_len;
ip_len = tcp_len + sizeof( *iph );
eth_len = ip_len + ETH_HLEN;
//
total_len = eth_len + NET_IP_ALIGN;
total_len += LL_MAX_HEADER;
header_len = total_len - len;
// 分配skb
skb = alloc_skb( total_len, GFP_ATOMIC );
if ( !skb ) {
dbg_err( "alloc_skb length %d failed.\n", total_len );
return NULL;
}
// 预先保留skb的协议首部长度大小
skb_reserve( skb, header_len );
// 拷贝负载数据
skb_copy_to_linear_data( skb, msg, len );
skb->len += len;
if ( likely(OPTION_TS & opts->options) ) {
ptr = (__be32 *)skb_push( skb, TCPOLEN_TSTAMP_ALIGNED );
#if 0
if (unlikely(OPTION_SACK_ADVERTISE & opts->options)) {
*ptr++ = htonl((TCPOPT_SACK_PERM << 24) |
(TCPOLEN_SACK_PERM << 16) |
(TCPOPT_TIMESTAMP << 8) |
TCPOLEN_TIMESTAMP);
} else {
*ptr++ = htonl((TCPOPT_NOP << 24) |
(TCPOPT_NOP << 16) |
(TCPOPT_TIMESTAMP << 8) |
TCPOLEN_TIMESTAMP);
}
#else
*ptr++ = htonl((TCPOPT_NOP << 24) |
(TCPOPT_NOP << 16) |
(TCPOPT_TIMESTAMP << 8) |
TCPOLEN_TIMESTAMP);
#endif
*ptr++ = htonl(opts->tsval);
*ptr++ = htonl(opts->tsecr);
}
// skb->data 移动到udp首部
skb_push( skb, sizeof( *th ) );
skb_reset_transport_header( skb );
th = tcp_hdr( skb );
memset( th, 0x0, sizeof( *th ) );
if ( tcp_opt_len ) {
th->doff = (tcp_opt_len + sizeof(*th)) >> 2;
}
else {
th->doff = 5;
}
th->source = sport;
th->dest = dport;
th->seq = seq;
th->ack_seq = ack_seq;
th->urg_ptr = 0;
th->psh = 0x1;
th->ack = 0x1;
th->window = htons( 63857 );
th->check = 0;
#if 1
tcp_hdr_csum = csum_partial( th, tcp_len, 0 );
th->check = csum_tcpudp_magic( saddr,
daddr,
tcp_len, IPPROTO_TCP,
tcp_hdr_csum );
#else
th->check = tcp_v4_check(tcp_len,
saddr, daddr,
csum_partial(th,
tcp_len, 0));
#endif
if ( th->check == 0 )
th->check = CSUM_MANGLED_0;
skb_iphdr_init( skb, IPPROTO_TCP, saddr, daddr, ip_len );
return skb;
}
/*
* 根据来源ip,tcp端口发送tcp数据
*/
int _tcp_send_pack( struct sk_buff *skb, struct iphdr *iph,
struct tcphdr *th, gbuffer_t *p )
{
struct sk_buff *pskb = NULL;
struct ethhdr *eth = NULL;
struct vlan_hdr *vhdr = NULL;
struct tcp_options_received opt_rx;
struct tcp_out_options opts;
int tcp_len = 0;
u32 seq = 0, ack_seq = 0;
u32 tcp_rcv_tsecr = tcp_time_stamp;
int rc = -1;
//
opt_rx.tstamp_ok = 1;
_tcp_parse_options( th, &opt_rx, 1 );
// 重新计算 Acknowledgement number
tcp_len = ntohs(iph->tot_len) - ((iph->ihl + th->doff) << 2);
ack_seq = ntohl(th->seq) + (tcp_len);
ack_seq = htonl(ack_seq);
//
get_random_bytes( &seq, sizeof(seq) );
//seq = common_seq;
memset( &opts, 0x0, sizeof(opts) );
if ( opt_rx.saw_tstamp ) {
opts.options |= OPTION_TS;
opts.tsecr = opt_rx.rcv_tsval;
opts.tsval = tcp_time_stamp - tcp_rcv_tsecr + opt_rx.rcv_tsval;
}
pskb = tcp_newpack( iph->daddr, iph->saddr,
th->dest, th->source,
th->ack_seq, ack_seq,
&opts,
p->buf, p->len );
if ( NULL == pskb ) {
goto _out;
}
// 复制VLAN 信息
if ( __constant_htons(ETH_P_8021Q) == skb->protocol ) {
vhdr = (struct vlan_hdr *)skb_push(pskb, VLAN_HLEN );
vhdr->h_vlan_TCI = vlan_eth_hdr(skb)->h_vlan_TCI;
vhdr->h_vlan_encapsulated_proto = __constant_htons(ETH_P_IP);
}
// skb->data 移动到eth首部
eth = (struct ethhdr *) skb_push(pskb, ETH_HLEN);
skb_reset_mac_header(pskb);
//
pskb->protocol = eth_hdr(skb)->h_proto;
eth->h_proto = eth_hdr(skb)->h_proto;
memcpy( eth->h_source, eth_hdr(skb)->h_dest, ETH_ALEN);
memcpy( eth->h_dest, eth_hdr(skb)->h_source, ETH_ALEN );
if ( skb->dev ) {
pskb->dev = skb->dev;
dev_queue_xmit( pskb );
rc = 0;
}
else {
kfree_skb( pskb );
dbg_err( "skb->dev is NULL\n" );
}
_out:
return rc;
}
/*
* 根据来源ip,tcp端口发送重定向HTML数据
*/
int _http_send_redirect(struct sk_buff *skb, struct iphdr *iph,
struct tcphdr *th )
{
int rc = -1;
gbuffer_t *p = NULL;
rcu_read_lock();
p = rcu_dereference( url_redirect_data );
if ( NULL == p ) {
p = url_redirect_default;
DBG_INFO( verbose,
"Send default redirect URL http://%s\n", DEFAULT_REDIRECT_URL );
}
if ( NULL != p && NULL != p->buf ) {
rc = _tcp_send_pack(skb, iph, th, p );
}
rcu_read_unlock();
return rc;
}
转自
http://blog.csdn.net/force_eagle/archive/2010/05/09/5572264.aspx