2008-12-11 14 views
12

Necesito crear una solicitud para una página web entregada a nuestros sitios web, pero también debo poder establecer la información del encabezado de host. Lo he intentado usando HttpWebRequest, pero la información del encabezado es de solo lectura (o al menos es parte del host). Necesito hacer esto porque queremos realizar la solicitud inicial de una página antes de que el usuario pueda hacerlo. Tenemos 10 servidores web con equilibrio de carga, por lo que debemos solicitar el archivo desde cada uno de los servidores web.Solicitar página web en C# spoofing the Host

he intentado lo siguiente:

HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://192.168.1.5/filename.htm"); 
request.Headers.Set("Host", "www.mywebsite.com"); 
WebResponse response = request.GetResponse(); 

Obviamente, esto no funciona, ya que no puedo actualizar la cabecera, y no sé si esto es de hecho la forma correcta de hacerlo.

+0

Esta pregunta está muy relacionada con http://stackoverflow.com/ questions/323264/http-request-bypass-dns-net ¿Tal vez alguien debería cerrarlo como duplicado? –

Respuesta

9

He logrado encontrar una ruta más larga utilizando enchufes. La respuesta la encontré en la página de MSDN para IPEndPoint:

string getString = "GET /path/mypage.htm HTTP/1.1\r\nHost: www.mysite.mobi\r\nConnection: Close\r\n\r\n"; 
Encoding ASCII = Encoding.ASCII; 
Byte[] byteGetString = ASCII.GetBytes(getString); 
Byte[] receiveByte = new Byte[256]; 
Socket socket = null; 
String strPage = null; 
try 
{ 
    IPEndPoint ip = new IPEndPoint(IPAddress.Parse("10.23.1.93"), 80); 
    socket = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp); 
    socket.Connect(ip); 
} 
catch (SocketException ex) 
{ 
    Console.WriteLine("Source:" + ex.Source); 
    Console.WriteLine("Message:" + ex.Message); 
} 
socket.Send(byteGetString, byteGetString.Length, 0); 
Int32 bytes = socket.Receive(receiveByte, receiveByte.Length, 0); 
strPage = strPage + ASCII.GetString(receiveByte, 0, bytes); 

while (bytes > 0) 
{ 
    bytes = socket.Receive(receiveByte, receiveByte.Length, 0); 
    strPage = strPage + ASCII.GetString(receiveByte, 0, bytes); 
} 
socket.Close(); 
0

bien, poco de la investigación se convierte en imagen esto:

https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=384456

Parece MS puede hacer algo acerca de esto en algún momento.

+0

Obtengo la excepción "ArgumentException: el encabezado 'Host' no se puede modificar directamente. Nombre del parámetro: nombre". Supongo que solo se lee en la clase HttpWebRequest – Xetius

+0

Abajo voto, porque esta respuesta no funciona. –

+0

¿No hubiera sido mejor crear una nueva respuesta en lugar de recuperar y reescribir completamente la anterior? –

0

El encabezado "Host" está protegido y no se puede modificar mediante programación. Supongo que para solucionar esto, puedes intentar vincular mediante reflejo la propiedad privada "InnerCollection" del objeto WebRequest y llamar al método "Set" ar "Add" para modificar el encabezado del host. No he intentado esto, pero de una rápida mirada al código fuente en Reflector, creo que es fácil de lograr. Pero sí, el enlace a propiedades privadas de objetos marco no es la mejor manera de lograr cosas. :) Úselo solo si DEBE.

editar: O como el otro chico menciona en la pregunta vinculada, simplemente abre un socket y haz un "GET" rápido manualmente. Debería ser pan comido, si no necesita jugar con otras cosas, como cookies o cualquier otro detalle que HttpWebRequest proporcione.

2

Sé que esto es viejo, pero me encontré con este mismo problema, y ​​he encontrado una mejor solución para esto, entonces utilizando sockets o reflexión ...

lo que hice fue crear una nueva clase que durives de WebHeaderCollection y no pasa la validación de lo que usted se pega en su interior:

public class MyHeaderCollection:WebHeaderCollection 
{ 
    public new void Set(string name, string value) 
    { 
     AddWithoutValidate(name, value); 
    } 
    //or 
    public new string this[string name] 
    { 
     get { return base[name]; } 
     set { AddWithoutValidate(name, value); } 
    } 
} 

y aquí es cómo lo usa:

var http = WebRequest.Create("http://example.com/"); 
    var headers = new MyHeaderCollection(); 
    http.Headers = headers; 

    //Now you can add/override anything you like without validation: 
    headers.Set("Host", http.RequestUri.Host); 
    //or 
    headers["Host"] = http.RequestUri.Host; 

Espero que esto ayude a cualquiera que esté buscando esto!

+2

Esto no funciona en 3.5SP1: la colección que se pasa a los Encabezados no se conserva, se copia en un nuevo WebHeaderCollection, por lo que todos los encabezados se vuelven a validar. – nitzmahone

4

que tenía un problema en el que el DNS URL utilicé tenía varias direcciones IP diferentes, que quería llamar cada dirección por separado utilizando el mismo nombre DNS en el huésped - la solución es utilizar un proxy:

string retVal = ""; 
      // Can't change the 'Host' header property because .NET protects it 
      // HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); 
      // request.Headers.Set(HttpRequestHeader.Host, DEPLOYER_HOST); 
      // so we must use a workaround 
      HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); 
      request.Proxy = new WebProxy(ip); 
      using (WebResponse response = request.GetResponse()) 
      { 
       using (TextReader reader = new StreamReader(response.GetResponseStream())) 
       { 
        string line; 
        while ((line = reader.ReadLine()) != null) 
         retVal += line; 
       } 
      } 
      return retVal; 

El encabezado del host se establece automáticamente desde 'url'.NET, y 'IP' contiene la dirección real del servidor web que desea ponerse en contacto con (se puede utilizar un nombre DNS aquí también)

+2

Obviamente, esto no funcionará cuando necesitemos usar un proxy :) –

10

Aunque se trata de una respuesta muy tarde, tal vez alguien puede obtener beneficio de ella

HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri("http://192.168.1.1")); 
request.Headers.GetType().InvokeMember("ChangeInternal", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, null, request.Headers, new object[] {"Host","www.mysite.com"}); 

la reflexión es su amigo :)

+0

LB - esto es genial, soy nuevo en C# y no sé qué es lo que refleja y cómo funcionó su respuesta para mí – xameeramir

+0

Nota: Esto cambiará el host, pero se sobrescribirá con el valor en la url a la que envía la solicitud. – User1

1

Sé que esta es una vieja pregunta, pero en estos días, usted puede hacer.

HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://192.168.1.5/filename.htm"); 
request.Host = "www.mywebstite.com"; 
WebResponse response = request.GetResponse(); 
1

Necromancing.
Para aquellos que todavía están en .NET 2.0
De hecho, es bastante fácil, si sabes cómo hacerlo.

El problema es que no puede establecer el encabezado de host porque el marco no le permitirá cambiar el valor en tiempo de ejecución. (.net framework 4.0+ le permitirá anular el host en una httpwebrequest).

El siguiente intento será configurar el encabezado con la reflexión, como se muestra en la respuesta arriba votada aquí, para evitarlo, lo que le permitirá cambiar el valor del encabezado. Pero en tiempo de ejecución, se sobrescribir este valor con la parte de host de la dirección URL , lo que significa que la reflexión le llevará nada, lo cual es por eso que No entender por qué la gente sigue votando hasta esto.

Si el nombre-dns no existe, lo que es francamente el único caso en el que desea hacer esto en primer lugar, no puede establecerlo, porque .NET no puede resolverlo, y no puede anular la resolución de DNS de .NET.

Pero lo que puede hacer es configurar un servidor web con la misma IP exacta que el servidor de destino.

tanto, si su IP del servidor es 28.14.88.71:

public class myweb : System.Net.WebClient 
{ 
    protected override System.Net.WebRequest GetWebRequest(System.Uri address) 
    { 
     System.Net.WebRequest request = (System.Net.WebRequest)base.GetWebRequest(address); 
     //string host = "redmine.nonexistantdomain.com"; 

     //request.Headers.GetType().InvokeMember("ChangeInternal", 
     // System.Reflection.BindingFlags.NonPublic | 
     // System.Reflection.BindingFlags.Instance | 
     // System.Reflection.BindingFlags.InvokeMethod, null, 
     // request.Headers, new object[] { "Host", host } 
     //); 

     //server IP and port 
     request.Proxy = new System.Net.WebProxy("http://28.14.88.71:80"); 

     // .NET 4.0 only 
     System.Net.HttpWebRequest foo = (System.Net.HttpWebRequest)request; 
     //foo.Host = host; 

     // The below reflection-based operation is not necessary, 
     // if the server speaks HTTP 1.1 correctly 
     // and the firewall doesn't interfere 
     // https://yoursunny.com/t/2009/HttpWebRequest-IP/ 
     System.Reflection.FieldInfo horribleProxyServicePoint = (typeof(System.Net.ServicePoint)) 
      .GetField("m_ProxyServicePoint", System.Reflection.BindingFlags.NonPublic | 
      System.Reflection.BindingFlags.Instance); 

     horribleProxyServicePoint.SetValue(foo.ServicePoint, false); 
     return foo; // or return request; if you don't neet this 
    } 


    } 

y listo, ahora

myweb wc = new myweb(); 
string str = wc.DownloadString("http://redmine.netexistantdomain.com"); 

y se obtiene la página correcta hacia atrás, si 28.14.88.71 es un servidor web con Virtual hosting basado en nombre (basado en http-host-header).

Ahora tiene la respuesta correcta a la pregunta original, tanto para WebRequest como para WebClient. Creo que usar sockets personalizados para hacer esto sería un enfoque equivocado, particularmente cuando se debe usar SSL, y cuando una solución real es así de simple ...