Acceso HttpListener denegado

180

Estoy escribiendo un servidor HTTP en C #.

Cuando intento ejecutar la función HttpListener.Start()me sale un HttpListenerExceptiondicho

"Acceso denegado".

Cuando ejecuto la aplicación en modo administrador en Windows 7, funciona bien.

¿Puedo hacer que se ejecute sin el modo de administrador? si si como? Si no, ¿cómo puedo hacer que la aplicación cambie al modo administrador después de comenzar a ejecutar?

using System;
using System.Net;

namespace ConsoleApplication1
{
    class Program
    {
        private HttpListener httpListener = null;

        static void Main(string[] args)
        {
            Program p = new Program();
            p.Server();
        }

        public void Server()
        {
            this.httpListener = new HttpListener();

            if (httpListener.IsListening)
                throw new InvalidOperationException("Server is currently running.");

            httpListener.Prefixes.Clear();
            httpListener.Prefixes.Add("http://*:4444/");

            try
            {
                httpListener.Start(); //Throws Exception
            }
            catch (HttpListenerException ex)
            {
                if (ex.Message.Contains("Access is denied"))
                {
                    return;
                }
                else
                {
                    throw;
                }
            }
        }
    }
}
Randall Flagg
fuente
Si alguien quiere evitar ese error, puede intentar escribirlo con TcpListener. No requiere privilegios de administrador
Vlad
Me enfrento al mismo problema, en Visual Studio 2008 + Windows 7, produce el error 'Acceso denegado', para contrarrestar esto es ejecutar Visual Studio 2008 en modo Admin
Mark Khor

Respuestas:

307

Sí, puede ejecutar HttpListener en modo no administrativo. Todo lo que necesita hacer es otorgar permisos a la URL particular. p.ej

netsh http add urlacl url=http://+:80/MyUri user=DOMAIN\user

La documentación está aquí .

Darrel Miller
fuente
48
Esto es útil, pero para completar, la URL especificada en esta línea de código: httpListener.Prefixes.Add("http://*:4444/");debe coincidir EXACTAMENTE con la del netshcomando. Por ejemplo, tenía httpListener.Prefixes.Add("http://127.0.0.1:80/");y el mismo netshcomando que tiene, y la HttpListenerException todavía se lanzará. Necesitaba cambiar. httpListener.Prefixes.Add("http://+:80/");Gracias por su ayuda, @Darrel Miller, ¡porque me ayudó a encontrar el camino correcto!
psyklopz
10
Y no olvide la barra inclinada final si "MyUri" está vacío, de lo contrario obtendrá un The parameter is incorrecterror. Ejemplo:url=http://+:80/
Igor Brejc
14
¿Hay alguna manera de hacer esto para un usuario no administrativo, incluso para http://localhost:80/? Tengo una aplicación de escritorio que necesita recibir una solicitud en dicha URL, y parece una pena requerir que un administrador la instale en 50 escritorios, solo para este propósito.
John Saunders
55
Aparentemente no. Tampoco se menciona la elevación o los privilegios administrativos que se requieren en la página de documentación. Por lo tanto, es fácil suponer que esto actuará como un TCPListener "más inteligente", cuando en realidad hay una razón PRINCIPAL para no usarlo; sin embargo, esto no está documentado.
David Ford el
44
Ejecutar netsh requiere administrador; (
superfly71
27

¿Puedo hacer que se ejecute sin el modo de administrador? si si como? Si no, ¿cómo puedo hacer que la aplicación cambie al modo administrador después de comenzar a ejecutar?

No puede, tiene que comenzar con privilegios elevados. Puede reiniciarlo con el runasverbo, que le indicará al usuario que cambie al modo administrador

static void RestartAsAdmin()
{
    var startInfo = new ProcessStartInfo("yourApp.exe") { Verb = "runas" };
    Process.Start(startInfo);
    Environment.Exit(0);
}

EDITAR: en realidad, eso no es cierto; HttpListener puede ejecutarse sin privilegios elevados, pero debe otorgar permiso para la URL en la que desea escuchar. Ver la respuesta de Darrel Miller para más detalles.

Thomas Levesque
fuente
¿Puede explicar por qué tengo que comenzar con privilegios elevados?
Randall Flagg
También necesita agregar esta línea 'startInfo.UseShellExecute = false;' antes de 'Process.Start (startInfo);'
Randall Flagg
@Randall: porque así es como funciona Windows ... un proceso no puede cambiar al modo administrador mientras se está ejecutando. Con respecto a UseShellExecute: depende de lo que esté ejecutando. Probé mi código con "notepad.exe", funciona bien sin UseShellExecute = false
Thomas Levesque
Gracias. Acerca de UseShellExecute: intenté ejecutar el código que publiqué. Otro problema es que, por alguna razón, me preguntó una vez si quiero ejecutarme como administrador y en cualquier otro momento no me pregunta. Reinicié, depurado para asegurarme de que vaya allí y nada. ¿alguna sugerencia?
Randall Flagg
1
@Thomas No hay ningún problema al ejecutar HttpListener en modo no administrativo.
Darrel Miller el
23

Si lo usa http://localhost:80/como prefijo, puede escuchar las solicitudes http sin necesidad de privilegios administrativos.

Ehsan Mirsaeedi
fuente
2
¿Puedes publicar algún código de ejemplo? Acabo de probar esto http://localhost:80/y obtuve un "Acceso denegado".
John Saunders
2
Lo siento, eso no parece funcionar. Verifique si se está ejecutando como administrador, lo cual no debería ser el caso normal.
Jonno
Si esto funcionara, lo consideraría un error de Windows. No importa lo que pienses acerca de Windows, supongo que esto es de un nivel muy básico que muy, muy probablemente, no dejaron de manejar correctamente.
hoijui
26
Entonces, más de 2 años después, esto funciona para mí ahora en Windows Server 2008 R2 con .NET Framework 4.5. httpListener.Prefixes.Add("http://*:4444/");de hecho muestra un Access Deniederror pero httpListener.Prefixes.Add("http://localhost:4444/");funciona sin ningún problema. Parece que Microsoft está excluido localhostde estas restricciones. Curiosamente, httpListener.Prefixes.Add("http://127.0.0.1:4444/");todavía muestra un error de Acceso denegado, por lo que lo único que funciona eslocalhost:{any port}
Tony
1
@ Tony agradece que tu comentario sea realmente el más valioso para mí. Tenía "127.0.0.1" en varias configuraciones. QA informó que no funcionaba en Windows 8.1, pero estaba en Windows 10 bien (y lo probé yo mismo en Win10). Curiosamente, parece que Microsoft ha concedido a Todos usar 127.0.0.1 en Win10 pero no 8.1 (y presumiblemente versiones anteriores), pero efectivamente, localhost está bien. Gracias
Adam Plocher
20

La sintaxis fue incorrecta para mí, debes incluir las comillas:

netsh http add urlacl url="http://+:4200/" user=everyone

de lo contrario recibí "El parámetro es incorrecto"

Dave
fuente
44
"El parámetro es incorrecto" también se puede devolver si no especifica un seguimiento /en la URL
LostSalad
13

En caso de que desee utilizar el indicador "usuario = Todos", debe ajustarlo al idioma de su sistema. En inglés es como se menciona:

netsh http add urlacl url=http://+:80/ user=Everyone

En alemán sería:

netsh http add urlacl url=http://+:80/ user=Jeder
Christoph Brückmann
fuente
1
En sistemas en español, haga: user = Todos
Rodrigo Rodrigues
Además, necesitaba poner la URL entre comillas, como se menciona en otra respuesta.
Carsten Führmann
10

Como alternativa que no requiere elevación o netsh, también puede usar TcpListener, por ejemplo.

El siguiente es un extracto modificado de esta muestra: https://github.com/googlesamples/oauth-apps-for-windows/tree/master/OAuthDesktopApp

// Generates state and PKCE values.
string state = randomDataBase64url(32);
string code_verifier = randomDataBase64url(32);
string code_challenge = base64urlencodeNoPadding(sha256(code_verifier));
const string code_challenge_method = "S256";

// Creates a redirect URI using an available port on the loopback address.
var listener = new TcpListener(IPAddress.Loopback, 0);
listener.Start();
string redirectURI = string.Format("http://{0}:{1}/", IPAddress.Loopback, ((IPEndPoint)listener.LocalEndpoint).Port);
output("redirect URI: " + redirectURI);

// Creates the OAuth 2.0 authorization request.
string authorizationRequest = string.Format("{0}?response_type=code&scope=openid%20profile&redirect_uri={1}&client_id={2}&state={3}&code_challenge={4}&code_challenge_method={5}",
    authorizationEndpoint,
    System.Uri.EscapeDataString(redirectURI),
    clientID,
    state,
    code_challenge,
    code_challenge_method);

// Opens request in the browser.
System.Diagnostics.Process.Start(authorizationRequest);

// Waits for the OAuth authorization response.
var client = await listener.AcceptTcpClientAsync();

// Read response.
var response = ReadString(client);

// Brings this app back to the foreground.
this.Activate();

// Sends an HTTP response to the browser.
WriteStringAsync(client, "<html><head><meta http-equiv='refresh' content='10;url=https://google.com'></head><body>Please close this window and return to the app.</body></html>").ContinueWith(t =>
{
    client.Dispose();
    listener.Stop();

    Console.WriteLine("HTTP server stopped.");
});

// TODO: Check the response here to get the authorization code and verify the code challenge

Los métodos de lectura y escritura son:

private string ReadString(TcpClient client)
{
    var readBuffer = new byte[client.ReceiveBufferSize];
    string fullServerReply = null;

    using (var inStream = new MemoryStream())
    {
        var stream = client.GetStream();

        while (stream.DataAvailable)
        {
            var numberOfBytesRead = stream.Read(readBuffer, 0, readBuffer.Length);
            if (numberOfBytesRead <= 0)
                break;

            inStream.Write(readBuffer, 0, numberOfBytesRead);
        }

        fullServerReply = Encoding.UTF8.GetString(inStream.ToArray());
    }

    return fullServerReply;
}

private Task WriteStringAsync(TcpClient client, string str)
{
    return Task.Run(() =>
    {
        using (var writer = new StreamWriter(client.GetStream(), new UTF8Encoding(false)))
        {
            writer.Write("HTTP/1.0 200 OK");
            writer.Write(Environment.NewLine);
            writer.Write("Content-Type: text/html; charset=UTF-8");
            writer.Write(Environment.NewLine);
            writer.Write("Content-Length: " + str.Length);
            writer.Write(Environment.NewLine);
            writer.Write(Environment.NewLine);
            writer.Write(str);
        }
    });
}
Michael Olsen
fuente
Esto fue realmente útil ya que tuvimos el problema HttpListener al implementar un IBrowser para la biblioteca IdentityModel.OidcClient, por lo que es un caso de uso muy similar al anterior.
mackie
1
Sin embargo, vale la pena señalar que el código anterior tiene errores. En primer lugar, el uso de un StreamWriter con UTF8 causa problemas en algunos navegadores, supongo que debido a la salida de una lista de materiales o algo así. Lo cambié a ASCII y todo está bien. También agregué un código para esperar a que los datos estén disponibles para leer en la transmisión, ya que a veces no devolverían datos.
mackie
Gracias @mackie, esa parte fue tomada directamente de la propia muestra de Microsoft en realidad. Supongo que realmente no lo probaron correctamente. Si puede editar mi respuesta, continúe y corrija los métodos; de lo contrario, quizás envíeme los cambios y pueda actualizarlos.
Michael Olsen
2
En lugar de usar ASCII, se debe usar el siguiente constructor new UTF8Encoding(false), que inhabilita la emisión de una lista de materiales. Ya he cambiado esto en la respuesta
Sebastian
Prefiero esta solución sobre la respuesta aceptada. Ejecutar netsh parece un truco. Esta es una solución programática sólida. ¡Gracias!
Jim Gomes
7

Puede iniciar su aplicación como administrador si agrega Manifiesto de aplicación a su proyecto.

Simplemente agregue un nuevo elemento a su proyecto y seleccione "Archivo de manifiesto de la aplicación". Cambia el <requestedExecutionLevel>elemento a:

<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
R.Titov
fuente
7

De manera predeterminada, Windows define el siguiente prefijo que está disponible para todos: http: // +: 80 / Temporary_Listen_Addresses /

Para que pueda registrar su HttpListenervía:

Prefixes.Add("http://+:80/Temporary_Listen_Addresses/" + Guid.NewGuid().ToString("D") + "/";

Esto a veces causa problemas con software como Skype, que intentará utilizar el puerto 80 de forma predeterminada.

Sebastian
fuente
1
Skype fue el culpable. Cambié el puerto para que no sea ​​80 y funcionó.
Ir al
2

También me enfrenté a un problema similar: si ya ha reservado la URL, primero debe eliminar la URL para ejecutarla en modo no administrativo; de lo contrario, fallará con el error Acceso denegado.

netsh http delete urlacl url=http://+:80
vivek nuna
fuente