在内核中实现URL重定向

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

你可能感兴趣的:(apache,html,.net,网络协议,电信)