Al igual que se usan estas funciones, windows tiene infinidad (bueno, no lo tomes literal...
muchas, muchisimas) funciones para hacer una cantidad impresionante de cosas. Todo lo que hacen
los archivos OCX es llamando a apis asi que es posible sustituir estos archivos por código
hecho por nosotros con llamadas a apis. Lo que voy a explicar es como usar apis para evitar
el uso del Microsoft winsock Control 6.0 (MSWINSCK.OCX) con no mas de 11 apis WOW!!!! XDD
***********************************************************************************************
UN POCO DE TEORIA EMINENTEMENTE PRACTICA ¿?
(la música de fondo es "Children of the Grave" de Black Sabbath)
***********************************************************************************************
Acá es donde la cosa se complica... porque yo estudio Ingeniería Mecánica XDDDD
Mejor no me voy a poner a explicar que es un socket porque sino la cago
supongo que si
alguien venia usando el control winsock de VB debe conocer algo de esto... personalmente conocí
winsock vía api y nunca use dicho control :/ .Todas estas funciones explicadas de acá en
adelante están en wsock32.dll que esta dentro de la carpeta System32
Creo que lo voy a explicar mejor con un diagrama
así funciona un cliente:
1) iniciamos una sesión Winsock (avisamos que vamos a usar winsock)
Esto se hace con la funcion WSAStartp
2) creamos un socket (digamos una terminal de entrada y salida de datos)
Esto se hace con la funcion soket
3) conectamos el socket a una dirección remota en un cierto puerto
Esto se hace con la función Connect, pero son necesarias un par
de funciones mas para condimentar un poco el asunto
4) si la conexión se realizo ya se pueden enviar mensajes, pero
también hay que verificar si hay mensajes entrantes para leer
Leer se hace con la función recv
Escribir se hace con la función send
5) para terminar se puede cerrar el socket y terminar la sesión winsock
o directamente terminar la sesion winsock y esta elimina todos los
sockets creados por el proceso que llame a esta función
Un socket se cierra con la función closesocket
Una sesión se termina con WSACleanup
Un servidor es levemente distinto, el paso 3 no es conectarse, sino poner el socket en modo escucha
para esto se usa la función bind para enlazar el socket con la IP local y el puerto donde se quiere
dar el servicio y luego la función listen para poner el socket a la espera de una conexión
A explicar un poco como funcionan estas funciones
("FUNCINAN ESTAS FUNCIONES" que recursivo!!!XDDD ah a propósito de esto leí que la RAE no acepta este
termino tan utilizado en informática, por ejemplo GNU es un acrónimo recursivo o sea una especie de
sigla que hace referencia a si misma --> GNU = GNU is Not Unix, la RAE propone otra palabra para
sustituir por recursivo que ahora no recuerdo)
* WSAStartup: (avisa que este proceso inicia una sesión winsock)
Declare Function WSAStartup Lib "WSOCK32" (ByVal wVersionRequired As Long, _
lpWSADATA As WSAData) As Long
wVersionRequired es la versión mas alta que soporta quien llama a esta función y esta se pasa
teniendo en cuenta lo siguiente supongamos la versión ficticia v5.7, 5 es la
versión y 7 la revisión, bueno como las variables que empezaban con lp eran
punteros ahora puede verse que wVersionRequired empieza con w es un word, o
sea, un par de bytes de los cuales el de mayor orden va a representar la
revisión y el de menor orden la versión casi nunca vas a tener error si usas
la versión de de Windows Sockets 2.2 o sea pasarle el valor &H202 como
primer argumento a esta función
lpWSADATA Seria un puntero a una estructura tipo WSAData, pero como en VB es mas fácil
directamente pasamos el nombre de la estructura
este tipo se declara de este modo
Type WSAData
wVersion As Integer
wHighVersion As Integer
szDescription As String * 257
szSystemStatus As String * 129
iMaxSockets As Integer
iMaxUdpDg As Integer
lpVendorInfo As Long
End Type
Generalmente todo esto no nos va a importar mucho.. digo, si estas por crear un cliente de
IRC solo queres iniciar winsock y nada más
Si la llamada a esta función es satisfactoria, el valor de retorno será CERO
* WSACleanup: (avisa que este proceso termina una sesión winsock)
Declare Function WSACleanup Lib "WSOCK32" () As Long
no tiene argumentos
* socket: (crea un socket)
Declare Function socket Lib "wsock32.dll" (ByVal af As Long, _
ByVal s_type As Long, _
ByVal protocol As Long) As Long
af es AddressFamily (la familia de direccion)
s_type es el tipo de socket
protocol es el protocolo que operara con el socket
valores comunes (troyanos, bots, clientes para keyloggers remotos, no se.. cosas comunes) son
af = AF_INET = 2
s_type = SOCK_STREAM = 1
protocol = IPROTO_TCP = 6
Si la llamada a la función es satisfactoria retorna un valor mayor a cero que es el manejador
del socket (ese valor para operar sobre el socket como con los archivos y las ventanas) y si
hay algún error y no se puede crear retorna -1
* closesocket: (cierra un socket)
Declare Function closesocket Lib "wsock32.dll" (ByVal s As Long) As Long
s es el manejador del socket que se quiere cerrar
ahora viene una explicación peque peque para la conexión
esta es la declaración de la función connect, la que establece la conexión, así
esta en la Api Guide (después voy a dar la dirección para que busque quien quiera la api
que quiera... también toda la info sobre esto esta en el msdn de microsoft pasen por ahí que
siempre atienden bien con café, torta, constantes, estructuras, funciones y códigos fuente
Declare Function Connect Lib "wsock32.dll" Alias "connect" (ByVal s As Long, addr As sockaddr, _
ByVal namelen As Long) As Long
que tenemos... s = manejador del socket (ya usamos manejadores como si en lugar de nombres
tuviesemos manejadores no?)
despues... addr = es otra estructura... muchas veces se usan estructuras con las apis tanto
para pasar info a la función como para recibir info. A veces esta info no
es importante y por ejemplo en masm32 suelo crear un buffer sin nada y
y en lugar de declarar tantas estructuras directamente paso la dirección
de memoria del buffer... dirección de memoria... mmm si!! punteros!!!
esta estructura se declara de este modo
Type sockaddr
sin_family As Integer
sin_port As Integer
sin_addr As Long
sin_zero As String * 8
End Type
en sin_family va la familia del socket, ¿como que socket? el que vamos a conectar, el que creamos
con la función socket mas arriba y como argumento AddressFamily le pasamos 2 que es AF_INET, acá
también. Que dolores de cabeza me estas dando niño!!! tu madre que dice de todo esto???!!! XD!
en sin_port va el puerto, pero acá empieza a haber algunos detalles extras, porque si te vas a
conectar al puerto 6667 por ejemplo de un servidor de IRC no hay que pasar el numero así como así.
hay ciertos tipos de ordenamientos de bytes (byte order) uno es el de red (network) y el otro
es el de host (JOST XDDD, lo de este paréntesis es broma no se confundan)
para pasar de ordenamiento de red al de host se usa la función ntohs y para lo opuesto htons
hey siempre me habían parecido letras a azar pero miren ntohs = Network to Host.. y la s? :S
nosotros vamos a hacer que estos datos viajen a través de la red para hacer una petición de
conexión y tenemos que cambiar el ordenamiento a NetWork con htons, pero antes viene uno de esos
problemitas con el tamaño de la variable que no sucede en otros lenguajes, antes de usar la
función htons hay que modificar el valor del puerto del siguiente modo
If Not Valor <= 32767 Then
Valor = Valor - 65536
End If
o sea la función para cambiar el ordenamiento podría ser así (sin la declaración de htons)
Private Function CambiarOrdenamiento(ByVal Valor As Long) As Integer
If Not Valor <= 32767 Then
Valor = Valor - 65536
End If
CambiarOrdenamiento = htons(Valor)
End Function
continuando con la estructura sockaddr tenesmos la variable sin_addr, para la cual también
hay que hacer ciertas cositas. Como se sabe una maquina en una red puede ser referida mediante
un Hostname o una direccion IP, el primero es algo como achernar.com y el segundo es algo como
207.46.104.20.
1) partiendo de una ip: suponiendo 207.46.104.20 -->
--> sin_addr = 20 * 256^0 + 104 * 256^1 + 46 * 256^2 + 207 * 256^3
2) partiendo de un hostname:
aca hay que usar una api para manipular memoria (RtlMoveMemory) esta funcion guarda en una
variable, una cierta cantidad de bytes copiados a partir de una direccion de memoria. Vamos
a tener que usar esto porque hay funciones que retornan punteros y ahí es donde los VBoys estamos
medio en p#lotas.
la función gethostbyname toma una cadena con el hostname y retorna un puntero a una estructura
HOSTENT, o sea los datos que queremos estan en alguna parte de la memoria y esta función nos
da dicha dirección... después con RtlMoveMemory copiamos estos datos a una variable para poder
manipular mejor estos datos.
la estructura HOSTENT es esta
Private Type HOSTENT
Nombre As Long
Alias As Long
TipoDeDireccion As Integer
Longitud As Integer
ListaDeDirecciones As Long
End Type
en ListaDeDirecciones hay un puntero (una dirección de memoria) que apunta a un lugar donde
hay otro puntero mas... si es una locura... y este ultimo puntero apunta a un lugar
de la memoria donde esta la IP del host que estamos buscando lista para guardar en
la variable sin_addr de la estructura sockaddr porque es para eso que estamos haciendo esto..
ya te habías olvidado... no te culpo... todo esto es para poder hacer una simple conexión TCP
A causa de tener que manipular tantos punteros se usa varias veces RtlMoveMemory.
NOTA: creo que, no estoy muy seguro, gethostbyname puede tomar también cadenas tipo IP
(xxx.xxx.xxx.xxx) pero quería mostrar la transformación por potencias que puse antes.
(a la IP 127.0.0.1 si la acepta perfectamente)
volviendo a la estructura sockaddr queda ver el ultimo termino que es sin_zero y esto es fácil
sencillamente no se toca... porque esta reservado.
las declaraciones de estas funciones son las siguientes
Declare Function Connect Lib "wsock32.dll" Alias "connect" (ByVal s As Long, _
addr As sockaddr, _
ByVal namelen As Long) As Long
Declare Function htons Lib "wsock32.dll" (ByVal hostshort As Long) As Integer
Declare Function gethostbyname Lib "WSOCK32" (ByVal szHost As String) As Long
Private Declare Sub RtlMoveMemory Lib "kernel32" (hpvDest As Any, _
ByVal hpvSource As Long, _
ByVal cbCopy As Long)
con esto ya podemos conectarnos por ejemplo a un servidor telnet... o por ejemplo cuando el
netcat da una shell directa (no inversa) nos podemos conectar y hacernos una interfaz con letras
tipo Papyrus (me encanta, me imagino revisando el correo en una carabela
)
ahora luego de conectarnos tenemos que tener algún método para ver si hay algún mensaje entrante
o si el host remoto se desconecto o si podemos escribir Hello Internet!
la función que se utiliza para esto es select
* select: ( retorna el estado del socket en cuestión [lectura/escritura/error] )
Declare Function vbselect Lib "ws2_32.dll" Alias "select" (ByVal nfds As Long, _
ByRef readfds As Any, _
ByRef writefds As Any, _
ByRef exceptfds As Any, _
ByRef timeout As Long) As Long
esta función se usa de la siguiente manera...
primero los 3 argumentos declarados tipo any son 3 estructuras fd_set que son así
Private Type fd_set
Contador As Long 'Cuantos sockets están en este estado?
Arreglo(1 To 64) As Long 'Arreglo con los manejadores en cuestión.
End Type
son 3 estructuras una para información de lectura otra para escritura y otra para error
cuando llamamos la función en el arreglo de cada una de estas estructuras metemos el manejador
de nuestro socket (supongo que se pueden meter mas) y después de llamar la función leemos
los contadores de estas estructuras... si el contador de la estructura de escritura esta en
1 (uno) es que hay datos para leer en el socket, si esta en 0 (cero es que no hay nada para
leer) igual con la estructura de escritura o error.
esta función me ha dado buenos resultados con error... cuando el host remoto se desconecta
me responde como si tuviera un mensaje para leer, pero con la diferencia que cuando lo leo
me dice que la longitud del mensaje es cero bytes... así que así hago para ver si se terminó
la conexión con el host remoto.
a los otros dos parámetro que quedan para llamar a esta función les pasamos cero como valor
bueno ahora tenemos una conexión y sabemos fehacientemente que tenemos un mensaje para leer
como hacemos? ahora lo explico, pero esperen unos minutos que tengo un llamado urgente de la
naturaleza que no puedo eludir..................
...
...
..
.
..
.
...
.
listo... uhh que alivio
bueno acá vamos a terminar con la recepción y envío de datos con send y recv
* recv: (recibe la data y la guarda en un buffer)
Declare Function recv Lib "wsock32.dll" (ByVal s As Long, _
buf As Any, _
ByVal buflen As Long, _
ByVal flags As Long) As Long
s = el manejador del socket
buf = un buffer, una matriz tipo byte que yo suelo usar de 1 a 8192
buflen = tamaño del buffer... 8192 bytes
flags = se pone a cero
si todo sale bien retorna un valor que es la cantidad de bytes leídos, si retorna
cero hubo algún error y ya explique que es como yo verifico si se corto la conexión
con el host remoto. fácil no?
hay un detallito... como la data la recibimos en un buffer tipo bytes hay que pasarla
a una cadena de texto... y se hace asi
StringBuffer = StrConv(ByteBuffer, vbUnicode)
StringBuffer = Left$(StringBuffer, BytesRecibidos)
acá no hay ninguna api StrConv, Left$ y vbUnicode son dos funciones y una constante
todas de VB6
ufff y acá tomamos la recta final.... de esta TEORIA EMINENTEMENTE PRACTICA
* send: (envía datos al host remoto tomándolos de un buffer)
Declare Function Send Lib "wsock32.dll" Alias "send" (ByVal s As Long, _
buf As Any, _
ByVal buflen As Long, _
ByVal flags As Long) As Long
funciona igual que send
s = el manejador del socket
buf = un buffer, una matriz tipo byte que yo suelo usar de 1 a 8192, pero ahora
tomamos datos de este buffer....
buflen = tamaño del buffer... 8192 bytes
flags = se pone a cero
listo ya esta todo explicado como para hacer un cliente en una conexión TCP