sábado, 15 de octubre de 2011

sencillo servidor telnet con node.js

Completamente funcional, admite conexiones simultaneas; eso si, no autenticado...
Únicamente se conectan entradas y salidas del socket y del proceso shell convenientemente y ya está.


var net = require('net');
var spawn = require('child_process').spawn;
var server = net.createServer(function(sock){
   console.log("nuevo cliente: " + sock.remoteAddress + ":" + sock.remotePort);
   var bash  = spawn('/bin/bash', ["-i"], { env: process.env, setsid: true});//unix
   //var bash  = spawn('cmd.exe', []);//guindors
   bash.stdout.on('data', function (data) {
      sock.write(data);
   });
   bash.stderr.on('data', function (data) {
      sock.write(data);
   });
   bash.on('exit', function (code) {
      sock.end(code);
   });
   sock.on('data', function(dd){
      bash.stdin.write(dd);
   });
});
server.listen(5555);
console.log("escuchando en el puerto 5555");

jueves, 17 de diciembre de 2009

Desconectar a un usuario de RRAS (usando MPRAPI y C#)

Lo más tedioso es definir las estructuras y los métodos externos;
Una vez hecho, solo hay que articular las llamadas.
No me he entretenido en el control de errores ni en hacer Dispose de las estructuras, para lo que habría que usar ¿Marshal.FreeHGlobal?;
Son aconsejables estos dos puntos si se quiere usar en producción.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace Mprapi
{
public enum ROUTER_INTERFACE_TYPE : uint
{

ROUTER_IF_TYPE_CLIENT,

ROUTER_IF_TYPE_HOME_ROUTER,

ROUTER_IF_TYPE_FULL_ROUTER,

ROUTER_IF_TYPE_DEDICATED,

ROUTER_IF_TYPE_INTERNAL,

ROUTER_IF_TYPE_LOOPBACK,

ROUTER_IF_TYPE_DIALOUT

}

public enum ROUTER_CONNECTION_STATE : uint
{

ROUTER_IF_STATE_UNREACHABLE,

ROUTER_IF_STATE_DISCONNECTED,

ROUTER_IF_STATE_CONNECTING,

ROUTER_IF_STATE_CONNECTED

}
public enum RAS_PORT_CONDITION :uint
{
RAS_PORT_NON_OPERATIONAL,
RAS_PORT_DISCONNECTED,
RAS_PORT_CALLING_BACK,
RAS_PORT_LISTENING,
RAS_PORT_AUTHENTICATING,
RAS_PORT_AUTHENTICATED,
RAS_PORT_INITIALIZING
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct RAS_CONNECTION_0
{
public IntPtr hConnection;
public IntPtr hInterface;
public uint dwConnectDuration;
public ROUTER_INTERFACE_TYPE dwInterfaceType;
public uint dwConnectionFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)257)]
public string wszInterfaceName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)257)]
public string wszUserName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)16)]
public string wszLogonDomain;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)17)]
public string wszRemoteComputer;

};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct RAS_PORT_0
{
public IntPtr hPort;
public IntPtr hConnection;
public RAS_PORT_CONDITION dwPortCondition;
public uint dwTotalNumberOfCalls;
public uint dwConnectDuration;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)17)]
public string wszPortname;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)17)]
public string wszMedianame;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)129)]
public string wszDevicename;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)17)]
public string wszDeviceType;

};



class Program
{
[DllImport("mprapi.dll", CharSet = CharSet.Unicode)]
static extern int MprAdminServerConnect(string lpwsServerName, ref IntPtr phMprServer);
//
[DllImport("Mprapi.dll")]
public static extern uint MprAdminConnectionEnum(IntPtr hRasServer,
uint dwLevel, ref IntPtr connectionsStartPoint , int dwPrefMaxLen,
ref IntPtr lpdwEntriesRead, ref IntPtr lpdwTotalEntries,
ref ulong lpdwResumeHandle

);
[DllImport("Mprapi.dll")]
public static extern uint MprAdminPortEnum(IntPtr hRasServer,
uint dwLevel, IntPtr hconnection, ref IntPtr buf,
int dwPrefMaxLen,
ref IntPtr lpdwEntriesRead, ref IntPtr lpdwTotalEntries,
ref ulong lpdwResumeHandle

);
[DllImport("Mprapi.dll")]
public static extern uint MprAdminPortDisconnect(IntPtr hRasServer,
IntPtr hport);
static void Main(string[] args)
{
if (args.Length == 0)
{
Console.WriteLine("Hay que pasar el usuario a desconectar.");
return;
}
try
{
string usuario_desconectar = args[0];
IntPtr phMprServer = IntPtr.Zero;
MprAdminServerConnect(null, ref phMprServer);
//Console.WriteLine(phMprServer.ToString());

IntPtr connectionsStartPoint = new IntPtr();
List connections = new List();


IntPtr EntriesRead = IntPtr.Zero;
IntPtr TotalEntries = IntPtr.Zero;
ulong ResumeHandle = 0;
uint retConEnum = MprAdminConnectionEnum(phMprServer, 0,
ref connectionsStartPoint, -1, ref EntriesRead, ref TotalEntries, ref ResumeHandle);

//Console.WriteLine(TotalEntries.ToInt32().ToString());
for (int i = 0; i < EntriesRead.ToInt32(); i++)
{
connections.Add((RAS_CONNECTION_0)Marshal.PtrToStructure(connectionsStartPoint, typeof(RAS_CONNECTION_0)));
connectionsStartPoint = new IntPtr(connectionsStartPoint.ToInt32() + Marshal.SizeOf(connections.First()));
//Console.WriteLine(connections[i].wszUserName);
}
foreach (var it in connections)
{
if (it.wszUserName == usuario_desconectar)
{
desconectar(it.hConnection, phMprServer);
//Console.WriteLine(it.hConnection);
}
}
}
catch (Exception e)
{
Console.WriteLine("Se produjo el error: " + e.Message);
}
}

private static void desconectar(IntPtr hcon, IntPtr hserver)
{
RAS_PORT_0 est = new RAS_PORT_0();
IntPtr buf = new IntPtr();
IntPtr EntriesRead = IntPtr.Zero;
IntPtr TotalEntries = IntPtr.Zero;
ulong ResumeHandle = 0;
uint retConEnum = MprAdminPortEnum(hserver,0,hcon,
ref buf, -1, ref EntriesRead, ref TotalEntries, ref ResumeHandle);
est = (RAS_PORT_0)Marshal.PtrToStructure(buf, typeof(RAS_PORT_0));
Console.WriteLine(est.wszDevicename + " -> " + est.hPort.ToString());
uint r = MprAdminPortDisconnect(hserver, est.hPort);
Console.WriteLine("Cerrar " + est.hPort.ToString() + ":" + r.ToString());
}
}
}

miércoles, 28 de enero de 2009

Clase timeout c# - mejoras

La clase para controlar el timeout expuesta hace casi un año (aquí) en este blog tiene un problemilla; el rendimiento no es bueno llegado el caso de que este sea un requerimiento ó se use de forma masiva. La razón es que se crea una nueva hebra por cada llamada. Para mejorarla se puede hacer un cambio en cada una de las clases envoltura; en lugar de crear una nueva hebra podemos hacer una llamada asíncrona, que utiliza en el fondo el ThreadPool de .net. De esta forma no tenemos la penalización de crear/destruir hebras, pues el framework recicla en cada llamada una hebra del ThreadPool para atender nuestra llamada.
Como no es cuestión de escribir aquí de nuevo todo el código, se expone como quedaría la clase envoltura para el caso de recibir un parámetro: Envoltura<T, U>
Habría que hacer el correspondiente cambio en las demás clases envoltura.

internal class Envoltura<T, U>
{
private Exception ex = null;
private U parametro;
private Func<T, U> metodo;
private T retorno;
Thread hebra_a_matar = null;

public Envoltura(U parametro)
{
this.parametro = parametro;
}
public T Run(Func<T, U> metodo, int milisegundos)
{
this.metodo = metodo;
ThreadStart delegado = run;
IAsyncResult result = delegado.BeginInvoke(null, null);
if (result.AsyncWaitHandle.WaitOne(milisegundos,true))
{
delegado.EndInvoke(result);
if (this.ex != null)
throw this.ex;
return this.retorno;
}
else
{
hebra_a_matar.Abort();
TimeoutSException to = new TimeoutSException();
to.Timeout = milisegundos;
throw to;
}
}
private void run()
{
try
{
hebra_a_matar = Thread.CurrentThread;
this.retorno = metodo(parametro);
}
catch (Exception e)
{
ex = e;
}
}
}

martes, 20 de mayo de 2008

Clase Timeout c#

Cuando desde nuestro código hacemos llamadas a determinados métodos externos, podemos llegar a perder el control, si el método llamado no termina por cualquier causa nuestro código se queda 'colgado' en esa llamada. Hace poco tuve un problema de este tipo con Oracle. El Listener alcanzaba un estado tal que aceptaba las conexiones y te dejaba enganchado por horas...
En determinados procesos como por ejemplo un proceso de monitorización, puede ser crítico que nunca se produzca este enganche. Aquí va mi propuesta de lo que podría ser una librería para controlar el Timeout. Se trata de hacer la llamada en una segunda hebra y esperar un tiempo máximo para la consecución de la misma.
Se plantean dos problemas; el paso de parámetros y la captura de excepciones; La solución expone entre otras una clase estática que a su vez contiene cuatro métodos estáticos genéricos:

public static void RunV(int milisegundos, ThreadStart metodo)
public static T RunG<T, U>(int milisegundos, U par, Func<T, U> metodo)
public static T RunG<T, U, V>(int milisegundos, U paru, V parv, Func<T, U, V> metodo)
public static T RunG<T, U, V, W>(int milisegundos, U paru, V parv, W parw,Func<T, U, V, W> metodo)

tambien se definen tres delegados genéricos:

public delegate T Func<T,U>(U parametro);
public delegate T Func<T, U, V>(U parametrou, V parametrov);
public delegate T Func<T, U, V, W>(U parametrou, V parametrov, W parametrow);
además del archiconocido delegado void ThreadStart().

Con lo cual contempla llamadas a métodos void funcion() y métodos con retorno cualquier tipo con hasta tres argumentos de cualquier tipo.

Estoy de acuerdo que hacer un Abort de un Thread no es una opción muy recomendable, pero no es tan mala si la única alternativa que queda es ir al Task Manager y matar el proceso.

A continuación se muestra el código que implementa la librería:


using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace Timeouts
{
public delegate T Func<T,U>(U parametro);
public delegate T Func<T, U, V>(U parametrou, V parametrov);
public delegate T Func<T, U, V, W>(U parametrou, V parametrov, W parametrow);
internal class Envoltura
{
private Exception ex = null;
private ThreadStart metodo;
public void Run(ThreadStart metodo, int milisegundos)
{
this.metodo = metodo;
Thread t = new Thread(run);
t.Start();
bool final = t.Join(milisegundos);
if (!final)
{
t.Abort();
TimeoutSException to = new TimeoutSException();
to.Timeout = milisegundos;
throw to;
}
else
{
if (this.ex != null)
throw this.ex;
return;
}
}
private void run()
{
try
{
metodo();
}
catch (Exception e)
{
ex = e;
}
}
}
internal class Envoltura<T,U>
{
private Exception ex=null;
private U parametro;
private Func<T, U> metodo;
private T retorno;
public Envoltura(U parametro)
{
this.parametro = parametro;
}
public T Run(Func<T, U> metodo, int milisegundos)
{
this.metodo = metodo;
Thread t = new Thread(run);
t.Start();
bool final = t.Join(milisegundos);
if (!final)
{
t.Abort();
TimeoutSException to = new TimeoutSException();
to.Timeout = milisegundos;
throw to;
}
else
{
if (this.ex != null)
throw this.ex;
return this.retorno;
}
}
private void run()
{
try
{
this.retorno = metodo(parametro);
}
catch (Exception e)
{
ex = e;
}
}
}
internal class Envoltura<T, U, V>
{
private Exception ex = null;
private U parametrou;
private V parametrov;
private Func<T, U, V> metodo;
private T retorno;
public Envoltura(U parametrou, V parametrov)
{
this.parametrou = parametrou;
this.parametrov = parametrov;
}
public T Run(Func<T, U, V> metodo, int milisegundos)
{
this.metodo = metodo;
Thread t = new Thread(run);
t.Start();
bool final = t.Join(milisegundos);
if (!final)
{
t.Abort();
TimeoutSException to = new TimeoutSException();
to.Timeout = milisegundos;
throw to;
}
else
{
if (this.ex != null)
throw this.ex;
return this.retorno;
}
}
private void run()
{
try
{
this.retorno = metodo(parametrou, parametrov);
}
catch (Exception e)
{
ex = e;
}

}
}
internal class Envoltura<T, U, V, W>
{
private Exception ex = null;
private U parametrou;
private V parametrov;
private W parametrow;
private Func<T, U, V, W> metodo;
private T retorno;
public Envoltura(U parametrou, V parametrov, W parametrow)
{
this.parametrou = parametrou;
this.parametrov = parametrov;
this.parametrow = parametrow;
}
public T Run(Func<T, U, V, W> metodo, int milisegundos)
{
this.metodo = metodo;
Thread t = new Thread(run);
t.Start();
bool final = t.Join(milisegundos);
if (!final)
{
t.Abort();
TimeoutSException to = new TimeoutSException();
to.Timeout = milisegundos;
throw to;
}
else
{
if (this.ex != null)
throw this.ex;
return this.retorno;
}
}
private void run()
{
try
{
this.retorno = metodo(parametrou, parametrov, parametrow);
}
catch (Exception e)
{
ex = e;
}
}
}
public class TimeoutSException : Exception
{
private int timeout;
public int Timeout
{
get { return timeout; }
set { timeout = value; }
}
}
public class Timeout
{
public static void RunV(int milisegundos, ThreadStart metodo)
{
Envoltura env = new Envoltura();
env.Run(metodo, milisegundos);
}
public static T RunG<T, U>(int milisegundos, U par, Func<T, U> metodo)
{
Envoltura<T, U> env = new Envoltura<T, U>(par);
return env.Run(metodo, milisegundos);

}
public static T RunG<T, U, V>(int milisegundos, U paru, V parv,
Func<T, U, V> metodo)
{
Envoltura<T, U, V> env = new Envoltura<T, U, V>(paru, parv);
return env.Run(metodo, milisegundos);

}
public static T RunG<T, U, V, W>(int milisegundos, U paru, V parv,
W parw,Func<T, U, V, W> metodo)
{
Envoltura<T, U, V, W> env = new Envoltura<T, U, V, W>(paru, parv, parw);
return env.Run(metodo, milisegundos);

}
}
}


Y un programilla que la prueba:

using System;
using System.Collections.Generic;
using System.Text;
using Timeouts;
namespace Probe
{
public class Pruebas
{
public string Variable="";
public void metodov()
{
System.Threading.Thread.Sleep(500);
Variable += "hola,";
}
public string metodo(string par, string par2)
{
return par + par2;
}
public string metodo2(string par, string par2, int par3)
{
int i = 6 / par3;
return par + par2 + "-" + par3.ToString();
}
}
class Program
{
static void Main(string[] args)
{
//con void
Pruebas p = new Pruebas();
Timeout.RunV(1000, p.metodov);
Timeout.RunV(1000, p.metodov);
try
{
Timeout.RunV(100, p.metodov);
}
catch (TimeoutSException e) { Console.WriteLine("Timeout: " + e.Timeout.ToString() + " ms"); }
Console.WriteLine(p.Variable);
//inline con un parametro
string pp = Timeout.RunG<string, string>(10000, "pepe", delegate(string par)
{
string ret = par.ToUpper();
return ret;
});
Console.WriteLine(pp);
//otra prueba con dos parametros
string ret2 = Timeout.RunG<string, string, string>(1000, "Pp", "potamo", p.metodo);
Console.WriteLine(ret2);
//con tres
string st = Timeout.RunG<string,string,string,int>(1000, "Pp", "potamo",7, p.metodo2);
Console.WriteLine(st);
//las excepciones generadas en el delegado son capturadas.
try
{
Console.WriteLine(Timeout.RunG<string, string, string, int>(
1000, "Pp", "potamo", 0, p.metodo2));
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.ReadKey();
}

}
}

martes, 27 de febrero de 2007

Un servidor dns en ruby (43 lineas)

El código que sigue representa un servidor dns mínimo, resuelve direcciones locales del dominio elegido y tiene otro servidor (DNS) configurado para resolver las direcciones que no corresponden al dominio local, lo he probado en windows con la versión 1.8.5 [i386-mswin32], si funciona en windows, difícil es que no funcione en otros sistemas operativos...
Una vez que mandamos en la resolución, podríamos resolver distintas ips dependiendo del cliente que nos hace la petición, la hora del día ó implementar un Round robin...
No obstante, esto es solo una maqueta, la motivación para crearlo ha sido solo la de jugar...

require 'socket'
DNS_REENVIO="10.0.0.115"
MIS_IPS = {"\003www\011ficciones\002es" => [10,0,0,1],
"\002mx\011ficciones\002es" => [10,0,0,2],
"\006correo\011ficciones\002es" => [10,0,0,3]}
#en octal número de caracteres que siguen:
#'ficciones'.length =9, en octal => 11
def resolver(peticion)
peticion[12,peticion.length-12] =~ /([^\0]+)\0/
consulta=$1
resolv=''
if MIS_IPS.keys.include?(consulta)#resolución en local
puts "\tresolución en local"
ip=MIS_IPS[consulta]
resolv=peticion
resolv[2] = 133.chr
resolv[3] = 128.chr
resolv[7] = 1.chr
resolv +="\xC0\x0C\000\001\000\001\000\000" +
"\007\010\000\004"
resolv +=ip[0].chr + ip[1].chr +
ip[2].chr + ip[3].chr
else #resultado de consulta a servidor dns de reenvio
puts "\tresolución a traves de dns"
re=UDPSocket.open
re.connect(DNS_REENVIO, 53)
re.send(peticion,0)
resp=re.recvfrom(256)
resolv = resp[0]
end
resolv
end
##
server = UDPSocket.open
server.bind('0.0.0.0', 53)
puts 'servidor iniciado'
while true
kk=server.recvfrom(256)
pet,info = kk
puts "peticion: " + pet.inspect
puts "info cliente: " + info.inspect
puerto_cliente=info[1];ip_cliente=info[3]
resolucion=resolver(pet)
server.send(resolucion,0,ip_cliente.to_s,
puerto_cliente.to_i)
end

Un servidor dns escucha en el puerto UDP 53,
MIS_IPS = {"\003www\011ficciones\002es" => [10,0,0,1],
"\002mx\011ficciones\002es" => [10,0,0,2],
"\006correo\011ficciones\002es" => [10,0,0,3]}

representa la resolución para el dominio local y el formato, aunque raro, es conveniente por la codificación de los mensajes en el protocolo DNS:
3 caracteres (\003 en octal):www
9 caracteres (\011 en octal):ficciones
2 caracteres (\002):es ...
Creo que el código se explica solo por lo demás, la resolución local es un poco liosa, pero así son los formatos de mensaje.
(rfc-1035, ver también http://www.freesoft.org/CIE/RFC/1035/38.htm)
Para probarlo:
1) Poner DNS_REENVIO apuntando a tu servidor dns.
2a) Cambiar servidor dns en la configuración de tú máquina y hacer ping a www.ficciones.es, www.google.es ...
2b)si no quieres cambiar el dns de tu máquina; ejecutar nslookup,
server computername, > www.google.es, > www.ficciones.es...