读书人

在内核中兑现URL重定向

发布时间: 2012-12-25 16:18:28 作者: rapoo

在内核中实现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

读书人网 >编程

热点推荐