Categories
.NET

Implementación de un pequeño Servidor Web

El siguiente código, originalmente publicado por Eric Carter, muestra la implementación de un pequeño "Servidor Web" con C#, para hacerlo funcionar necesitarán del .NET Framework 2.0 (podría correr en versiones anteriores haciendo ligeras modificaciones al código).

csharp:

using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web;
using System.Collections.Specialized;

namespace http
{
    public class FakeWebServer
    {
        private const string URL_REPLACE = "{URL}";
        private static readonly Regex urlRegex = new Regex(@"^(GET|POST) /(.*?) (HTTP[^\s]+)",
                                        RegexOptions.Compiled | RegexOptions.IgnoreCase);

        // Almacena las URLs verdaderas y falsas
        public static readonly Dictionary<string, string> FakeUrls;

        static FakeWebServer()
        {
            FakeUrls = new Dictionary<string, string>();
        }

        private TcpListener listener;
        string contents;

        public FakeWebServer(int port, string responseContents)
        {
            this.contents = responseContents;

            // 'Escuchar' en cualquier dirección
            listener = new TcpListener(IPAddress.Any, port);
            listener.Start();

            Thread t = new Thread(delegate()
            {
                AcceptClients();
            });
            t.Start();
        }
        public void AcceptClients()
        {
            while (true)
            {
                using (TcpClient client = listener.AcceptTcpClient())
                {
                    if (client.Connected) // Nuevo cliente
                    {
                        // Leer los datos enviados
                        NetworkStream stream = client.GetStream();
                        byte[] data = new byte[1024];

                        stream.Read(data, 0, data.Length);

                        string request = Encoding.UTF8.GetString(data);

                        // Sólo tomar en cuenta los datos presentes en el QueryString

                        // Obtener la versión del protocolo y la URL del 'Request'
                        MatchCollection matches = urlRegex.Matches(request);

                        string qs = matches[0].Groups[2].Value.TrimStart('?');
                        NameValueCollection paramArray = HttpUtility.ParseQueryString(qs);

                        foreach (string key in paramArray.AllKeys)
                        {
                            if (FakeUrls.TryGetValue(paramArray[key], out qs))
                                break;
                        }                       
                       
                        System.Diagnostics.Debug.WriteLine("Query String: " + matches[0].Groups[2].Value);

                        // Reemplazar las URLs
                        if (!string.IsNullOrEmpty(qs) && !string.IsNullOrEmpty(contents))
                            contents = contents.Replace(URL_REPLACE, qs);

                        // Enviar las cabeceras necesarias y el contenido
                        SendHeaders(matches[0].Groups[3].Value, null, contents.Length, "200 OK", client);
                        SendToBrowser(Encoding.UTF8.GetBytes(contents), client);
                    }
                }
            }
        }

        public void SendHeaders(string httpVersion, string mimeHeader, int totalBytes, string statusCode, TcpClient tcpClient)
        {
            StringBuilder responseBuilder = new StringBuilder();

            if (string.IsNullOrEmpty(mimeHeader))
                mimeHeader = "text/html";

            responseBuilder.Append(httpVersion);
            responseBuilder.Append(' ');
            responseBuilder.AppendLine(statusCode);
            responseBuilder.AppendLine("Server: Fake Web Server");
            responseBuilder.Append("Content-Type: ");
            responseBuilder.AppendLine(mimeHeader);
            responseBuilder.AppendLine("Accept-Ranges: bytes");
            responseBuilder.Append("Content-Length: ");
            responseBuilder.AppendLine(totalBytes.ToString());
            responseBuilder.AppendLine("");

            Byte[] bSendData = Encoding.UTF8.GetBytes(responseBuilder.ToString());
            SendToBrowser(bSendData, tcpClient);

            System.Diagnostics.Debug.WriteLine("Total Bytes : " + totalBytes.ToString());
        }

        public void SendToBrowser(Byte[] data, TcpClient tcpClient)
        {
            if (tcpClient.Connected)
            {
                NetworkStream stream = tcpClient.GetStream();

                stream.Write(data, 0, data.Length);
                stream.Flush();
            }
            else
            {
                System.Diagnostics.Debug.WriteLine("Connection Dropped....");
            }

        }
    }
}

Como habrán podido observar el "Servidor Web", entrega tontamente casi el mismo contenido, sólo reemplaza cada aparición de {URL} en la respuesta en base a los parámetros solicitados.

En una siguiente entrada explicaré la valiosa ayuda que presta ese pedazo de código, en la explotación de otro bug de una aplicación ya algo conocida por este blog.

Categories
PHP

Escribiendo código seguro – Manipulación de Tipos

PHP no soporta la definición explícita de tipos de datos, es decir que cada variable actúa de acuerdo a como es usada. Veamos un ejemplo:

Nota: Para los ejemplos vamos a usar la función var_dump, que nos muestra información acerca de una o mas variables.

PHP:

<?php
$cadena = "1";
$entero = $foo + 1;
var_dump($cadena, $entero);
?>
 

La salida será:

CODE:

string(1) "1" int(2)
 

Como vemos aunque hayamos definido la primera variable como "cadena", al sumarlo con un entero, nos dá otro entero. Para solucionar esto (aunque en realidad no es un problema tan grave), si es que queremos sumar la cadena con un entero, primero deberíamos convertir la cadena a un entero usando casting:

La solución

PHP:

<?php
$cadena = "1";
$entero = (int)$foo + 1;
var_dump((int)$cadena, $entero);
?>
 

La salida será:

CODE:

int(1) int(2)
 

Tratemos de tener siempre en cuenta los tipos de datos que estamos usando, alguien podría aprovechar uno de estos descuidos y podría enviar código malicioso concatenándolo con algún id númerico de nuestra base de datos por dar un ejemplo.

Mañana seguiremos con más 😉

Referencias

Categories
PHP Varios

Un pequeño consejo

Muchos de nosotros hemos aprendido a programar sólo con experiencia, copiando código, preguntando al amigo que era un trome (léase gurú o también Alex) en estas cosas, leyendo algun manualillo de por ahi. Pero pocos hemos leido algún libro o alguna publicación oficial.

Y no es que seamos vagos ni irresponsables, sino que talvez no tengamos los medios para pagarnos cursos o comprarnos libros.

Pero esa no es excusa para seguir métodos antiguos o "hardcode" para hacer nuestras aplicaciones web. Tenemos que esforzarnos por aprender más y mejor las cositas que estamos haciendo ya que la mayoría de nosotros vive de estas cosas... y si es así ¿por qué no hacerlo bien?

Tenemos la suerte en estos tiempos de que los gurus, autores de libros, especialistas y demás tienen un blog en los cuales comparten sus conocimientos y trucos sobre cosas innovadoras o talvez cosas viejas que recién estamos aprendiendo.

Tengamos una buena cultura de programación, seamos ordenados desde ahora esforcémonos por hacer código estándar y entendible para las próximas personas que vendrán.

Jeje, este cursi-post es una pequeña introducción a una serie de posts sobre buenas prácticas de programación con PHP, fué inspirado despues de leerme todita la presentación de PHP best practices, the dos and don'ts y también despues de leer todos los post sobre seguridad que últimamente está escribiendo mi amigo Alex.

La recomendación es para todos, especialmente para los que ya llevan buen tiempo en este mundo de la web.

Categories
Varios

Compuntoes

Se acaba de iniciar un nuevo concurso de posicionamiento, se trata de posicionar la palabra compuntoes en el buscador, y los primeros resultados... pues se ganarán algunos regalitos.

En versiones anteriores las palabras para posicionar fueron Telendro y Microsano recuerdo que me inscribí para participar con la palabra microsano, pero nunca hice nada al respecto, pero esta vez veremos que tal nos vá

El concurso termina el día 1 de febrero de 2007, espero estar vivo para esa fecha y recibir alguno de esos regalitos que tanta falta nos hacen a mortales de esta parte del planeta (especialmente para poder pagar nuestro hosting :().

Supongo que la suerte nos acompañará ya que la palabra compuntoes es tán díficil de posicionar como la palabra buayacorp.... y a propósito ¿Qué significa buayacorp? O_O

Actualización

Ya estamos segundo puesto 😛

Categories
.NET Expresiones Regulares Varios

Rendimiento de expresiones regulares

Luego de leer los artículos publicados por Manuel (a.k.a melkorcete), me parece que se olvidó comentar que no siempre es mejor usar Expresiones Regulares.

A continuación, un ejemplo -trivial- que pone en mayúsculas las letras que son precedidas por espacios en blanco. La diferencia en rendimiento, se nota más cuando el texto es más grande.

csharp:

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Diagnostics;

class Program
{
    delegate string DoCapitalize(string str);
    static Stopwatch clock = new Stopwatch();
    static Dictionary<string, double> stats = new Dictionary<string,double>();

    static void Run(string str, DoCapitalize method)
    {
        clock.Stop();
        clock.Reset();

        clock.Start();
        method(str);
        clock.Stop();

        if (stats.ContainsKey(method.Method.Name))
            stats[method.Method.Name] += clock.ElapsedTicks;
        else
            stats[method.Method.Name] = clock.ElapsedTicks;
    }
    static void Main(string[] args)
    {
        string str = @"Cada año y medio los computadores doblan su velocidad,
entonces sale otra versión de Windows, que los ralentiza a su velocidad original"
;       

        DoCapitalize regex = new DoCapitalize(RegexCapitalize);
        DoCapitalize normal = new DoCapitalize(Capitalize);
        Console.Read();
        int n = 1000;
        for (int i = 0; i < n; i++)
        {
            Run(str, regex);
            Run(str, normal);
        }
        Console.WriteLine("\tStats for {0} iterations\n", n);
        foreach(KeyValuePair<string, double> item in stats)
        {
            Console.WriteLine("{0} --> Tiempo: {1:.##}", item.Key, item.Value / n);
        }
    }
    static string RegexCapitalize(string str)
    {
        return Regex.Replace(str, @"\s[a-z]", new MatchEvaluator(ToUpper)); // @"\s."
    }
    static string ToUpper(Match m)
    {
        return m.Value.ToUpper();
    }
    static string Capitalize(string str)
    {       
        StringBuilder sb=new StringBuilder();
        sb.Append(str[0].ToString().ToUpper());
        for (int i = 1; i < str.Length; i++)
        {
            char c = str[i];
           
            if (char.IsWhiteSpace(str[i-1]) && char.IsLower(c))
                c = char.ToUpper(c);
            sb.Append(c);
        }
        return sb.ToString();
    }
}

Cabe destacar que implementar nuestros propios métodos, puede tener consecuencias no deseadas, como el tiempo necesario para implementar un algoritmo alternativo y más importante, la eficiencia del mismo.