InterNestor Lite 1.0 Por Konami Man, Diciembre de 2004 --------------------------------- 1. INTRODUCCION 1.1. ¿QUE ES INTERNESTOR LITE? InterNestor Lite (INL) es una pila TCP/IP para ordenadores MSX2 con 128K RAM. Este manual describre la versión de INL para interfaz RS232, que permite la conexión a Internet por medio de un ISP usando un modem, así como la conexión directa a otro ordenador por medio de un cable null-modem. Este manual se divide en tres partes: Introducción, donde se enumeran las características generales de INL; Guía del usuario, donde se explica cómo instalar y configurar INL, así como las opciones de uso de las aplicaciones incluídas; y Guía del programador, útil para aquellos que quieran desarrollar aplicaciones con acceso a Internet basadas en INL. INL para RS232 es compatible con INL para ObsoNET a nivel IP. Más adelante se mencionan explícitamente qué funciones y variables son compatibles con todas las implementaciones de INL. 1.2. CARACTERISTICAS GENERALES DE INTERNESTOR LITE INL es un programa residente: una vez instalado, actúa como tarea de fondo acoplada al gancho de la interrupción del reloj. Otros programas pueden hacer uso de las rutinas y variables expuestas por INL de forma similar a como usan rutinas de la BIOS o funciones del sistema operativo, consiguiendo así acceso a Internet. A su vez, INL se basa en el driver Fossil de Erik Maas para usar el puerto RS232. INL está pensado para poder conectar un MSX a Internet mediante una cuenta de acceso contratada con cualquier proveedor de acceso a Internet (ISP) usando un modem normal. Por este motivo, además de los protocolos TCP/IP también soporta PPP, que es el protocolo usado para establecer conexiones con ISPs; y por supuesto, INL también puede enviar comandos al modem para marcar el número de teléfono del ISP. Por otra parte, INL también puede ser usado para realizar una conexión directa con otro equipo. Para ello se necesita un cable null-modem, y que el otro equipo también soporte PPP y TCP/IP (por ejemplo otro MSX con INL o INS, o un PC con Linux). En este caso es necesario un poco de trabajo adicional antes de la conexión, ya que las direcciones IP a usar deben ser configuradas manualmente (en el caso de la conexión mediante ISP, las direcciones IP se obtienen automáticamente). INL está pensado para ser rápido (al menos más rápido que INS) y ocupar poca memoria. Por esta razón presenta una serie de limitaciones: * Generales - La velocidad máxima de transmisión es 9600 baudios para un Z80 y 19200 baudios para un R800. - No soporta ICMP, excepto para enviar y recibir mensajes de eco (PINGs). Se puede elegir el tamaño de estos paquetes cuando se envían, pero no su contenido. Análogamente, se puede consultar el tamaño de las respuestas recibidas pero no su contenido. - Soporta el envío y la captura de datagramas en bruto, pero con este procedimiento sólo puede capturar un datagrama a la vez. - La implementación del protocolo PPP es una simplificación de la especificación completa, que debería funcionar en circunstancias normales. Por ejemplo, se usa un autómata simplificado de sólo cinco estados (cerrado, negociación LCP, autentificación, negociación IPCP y abierto). - Aunque el estándar PPP dicta que se deben aceptar paquetes de datos de hasta 1500 bytes de longitud mientras no se haya negociado un tamaño menor, INL siempre descartará los paquetes mayores de 582 bytes que reciba. * IP - El tamaño máximo de datagrama soportado es 576 bytes. - No soporta fragmentación IP ni reensamblaje de datagramas fragmentados recibidos; los fragmentos de datagrama recibidos siempre se ignoran (excepto al enviar y recibir datagramas en bruto). - No es posible incluir opciones IP en los datagramas salientes; además, las opciones IP de los datagramas entrantes siempre se ignoran (excepto al enviar y recibir datagramas en bruto). - En esta versión, INL no soporta la dirección de loopback (127.x.x.x) ni puede enviarse datagramas a sí mismo. * UDP - INL puede almacenar un máximo de ocho paquetes UDP recibidos de hasta 556 bytes cada uno. Estos paquetes deben ser consumidos por alguna aplicación en un tiempo razonable; los paquetes nuevos que llegan cuando ya hay ocho almacenados se pierden. Lo mismo ocurre con los paquetes de respuesta de eco ICMP. * TCP - Sólo se pueden abrir cuatro conexiones TCP simultáneamente. - Cada conexión dispone de un búfer fijo de 1024 bytes para almacenar datos salientes, y de otro del mismo tamaño para almacenar datos entrantes. - No existe el estado TIME-WAIT. Las conexiones se cierran directamente cuando deberían entrar en dicho estado. - Sólo se reconoce la opción TCP MSS (aunque INL nunca enviará segmentos mayores de 448 bytes). Se envía siempre una opción TCP MSS=448 al iniciar una conexión. - No es posible enviar datos urgentes. Además, el bit URG y el puntero a datos urgentes de los segmentos recibidos siempre se ignoran. - Sólo es posible enviar datos a una conexión TCP cuando está en el estado ESTABLISHED o CLOSE-WAIT. - No es posible convertir una conexión pasiva en activa (ya sea reabriéndola como activa o enviándole datos). - El número de secuencia inicial de envío para las conexiones nuevas es siempre cero. - Sólo se envía un segmento de datos a la vez. Es decir, una vez que se ha enviado un segmento, no se envían más (excepto retransmisiones) hasta que se recibe un reconocimiento para los datos de dicho segmento, con independencia del valor de la ventana de transmisión. - El intervalo entre retransmisiones de datos está fijado en tres segundos. El intervalo entre comprobaciones de ventana cero está fijado en 10 segundos. - Se usa una aproximación simplificada del algoritmo "SWS Avoidance": el tamaño de ventana anunciado es igual al espacio libre en el búfer de recepción, con los siete bits bajos puestos a cero. Es decir, la ventana de recepción anunciada se incrementa y decrementa en pasos de 128 bytes. Cuando el espacio libre es inferior a 128 bytes, se anuncia el valor real. - Se usa una aproximación simplificada del algoritmo de Nagle. Si los datos a enviar tienen el bit PUSH establecido, se envían inmediatamente. En caso contrario, se envían cuando se acumulan suficientes bytes como para enviar un segmento de tamaño máximo completo o cuando pasan 0.5 segundos desde que se encolaron los primeros datos pendientes de envío, lo que ocurra antes. - No se han implementado los algoritmos Slow Start/Congestion Avoidance, pero se envía un segmento ACK inmediatamente cuando se reciben segmentos con número de secuencia inesperado, de forma que si el módulo TCP del otro extremo implementa dichos algoritmos podrá funcionar correctamente cuando interactúe con INL. Por otro lado, INL hereda de INS el resolver recursivo que sólo puede traducir nombres de host en direcciones IP, así como la posibilidad de autentificacion por PAP y CHAP durante el establecimiento de la conexión PPP. Se implementa además la compresion Van Jacobson de cabeceras TCP/IP y el mecanismo de reconocimientos retardados ("Delayed ACK") para TCP: se envía un segmento ACK cuando se acumulan 256 bytes nuevos recibidos o cuando pasan 0.1 segundos desde que llegaron los primeros datos nuevos, lo que ocurra antes. INL consiste en un único fichero, INL.COM, que empaqueta la funcionalidad que en INS estaba dividida en varios ficheros: INL.COM permite instalar/desinstalar INL (el propio código residente que se instala también está incrustado en INL.COM), abrir/cerrar conexiones PPP, consultar/establecer diversos parámetros de INL, consultar el estado de las conexiones TCP, e incluso resolver un nombre de host (obtener su dirección IP). INL se instala en dos segmentos de RAM del mapeador primario: uno para código y otro para variables y búferes de datos; y también reserva una zona de 256 bytes en TPA. Todo esto se detalla en la Guía del Programador. 2. INTERNESTOR LITE - GUIA DEL USUARIO 2.1. REQUISITOS INL requiere la siguiente configuración para funcionar: - Ordenador MSX2, 2+ o Turbo-R. - Soporta tanto MSX-DOS 1 como MSX-DOS 2 (se recomienda DOS 2). - 128K de memoria RAM en el mapeador primario. En caso de usar DOS 2, se recomiendan 256K y es necesario que haya dos segmentos libres. - Interfaz RS-232, o bien ACCNET o cualquier otro hardware reconocido por el driver Fossil. - Modem y cuenta de acceso para la conexión a internet vía ISP, o bien cable null-modem para la conexión directa a otro ordenador. INL funcionará en ordenadores con 128K RAM bajo DOS 2 a condición de que ningún otro programa haya reservado ningún segmento de RAM. Esto implica que con dicha configuración no es posible usar COMMAND 2.4x, crear RAM disk ni usar otros programas residentes. INL no funciona en MSX Turbo-R con ampliación de memoria externa bajo DOS 1. Si tienes esa configuración, para usar INL deberás arrancar en modo DOS 2 (recomendado) o quitar la ampliación de memoria. 2.2. FICHEROS INCLUIDOS La distribución actual de INL consta de los siguientes ficheros: - INL.COM: Programa de instalación, configuración y control de INL. - DRIVER.COM: Driver Fossil para acceso al puerto RS232, es necesario instalarlo antes de instalar INL. - INL10-S.TXT: Guía del usuario y del programador de INL, es español. - INL10-E.TXT: Guía del usuario y del programador de INL, es inglés. - PING.COM: Cliente sencillo de PING. - TFTP.COM: Cliente y servidor de TFTP. - TCPCON.COM: Cónsola TCP (cliente de Telnet simplificado). - TELNET.COM: Cliente de Telnet. - PING-S.ASC: Código fuente de PING.COM, comentado en español. - PING-E.ASC: Código fuente de PING.COM, comentado en inglés. - TFTP-S.ASC: Código fuente de TFTP.COM, comentado en español. - TFTP-E.ASC: Código fuente de TFTP.COM, comentado en inglés. - TCPCON-S.ASC: Código fuente de TCPCON.COM, comentado en español. - TCPCON-E.ASC: Código fuente de TCPCON.COM, comentado en inglés. 2.3. GUIA DE INICIO RAPIDO En esta sección se describen de forma breve los pasos necesarios para realizar una conexión a Internet usando INL (a través de un ISP con el que se supone que has contratado una cuenta de acceso, y usando un modem que se supone está conectado a la línea telefónica y al puerto RS232 de tu MSX). Las opciones de uso del programa INL.COM se describen con detalle en una sección posterior. 1) Instala el driver Fossil, simplemente ejecutando DRIVER.COM; vuelve al DOS con CALL SYSTEM 2) Instala INL ejecutando: INL I 3) Para abrir una conexión a Internet, ejecuta: INL PPP O /N: /U: /P: /I donde es el número de teléfono de tu ISP, y y son los datos da la cuenta de acceso que te proporcionó tu ISP. 4) Una vez conectado puedes usar programas que hagan uso de INL para acceder a Internet, por ejemplo los programas PING.COM, TFTP.COM y TCPCON.COM, incluidos en la distribución de INL. 5) Para cerrar la conexión a Internet, ejecuta: INL PPP C Puedes volver a conectar cuando quieras como se ha explicado en el paso 3. 6) Para desinstalar INL, ejecuta: INL U Puedes volver a instalar INL cuando quieras como se ha explicado en el paso 2. NOTA: Instalaciones y desinstalaciones sucesivas de INL pueden causar que aparezca el error "Not enough memory" al intentar ejecutar cualquier programa. Si eso ocurre, basta salir al BASIC (comando BASIC) y volver al DOS (comando CALL SYSTEM) para solucionar el problema. 2.4. EL PROGRAMA DE CONTROL DE INTERNESTOR LITE INL.COM es el programa de control unificado de InterNestor Lite. Con este programa puedes instalar/desinstalar INL, abrir/cerrar/comprobar la conexión a Internet y consultar/modificar diversos parámetros de INL. La siguiente es una lista resumida de todas las opciones de uso de INL.COM, más adelante se explican con detalle todas ellas. - Opciones generales: inl i [/s:] [] - Instala INL inl u [] - Desinstala INL inl p - Pausa INL inl r - Reanuda la ejecución de INL inl s - Muestra el estado de la conexión PPP y direcciones IP en uso inl v - Muestra el valor de las variables de configuración de INL inl d - Restablece la configuración de INL a los valores por defecto inl o - Establece el vector de cálculo de checksums de INL inl f [] - Lee un fichero de configuración - Opciones de modem y conexión PPP: inl ppp m - Envía un comando al modem inl ppp b - Establece la velocidad de transmisión del RS232 inl ppp n [] - Establece el número de teléfono del ISP inl ppp r - Establece la respuesta positiva del modem inl ppp u [] - Establece el nombre de usuario PPP inl ppp p [] - Establece el password PPP inl ppp o [/n:[]] [/u:[]] [/p:[]] [/r:] [/i] - Abre una conexion PPP inl ppp c - Cierra la conexión PPP inl ppp e 0|1 - Activa o desactiva el envío de ecos PPP inl ppp v 0|1 - Activa o desactiva la negociacion de la compresión Van Jacobson - Opciones del protocolo IP: inl ip i - Inicializa todas las direcciones IP a 0.0.0.0 inl ip l - Establece la dirección IP local inl ip r - Establece la dirección IP remota inl ip p - Establece la dirección IP del servidor DNS primario inl ip s - Establece la dirección IP del servidor DNS secundario inl ip n 0|1 - Activa o desactiva la negociación de los servidores DNS inl ip e 0|1 - Activa o desactiva la respuesta automática a PINGs inl ip t - Establece el TTL de los datagramas salientes inl ip o - Establece el TOS de los datagramas salientes inl ip z - Establece el tamaño por defecto de los PINGs salientes - Opciones para la resolución de nombres: inl dns r - Resuelve un nombre de host - Opciones para el protocolo TCP: inl tcp s [] - Muestra el estado de una conexión TCP o de todas inl tcp c - Cierra la conexión especificada inl tcp a - Aborta la conexión especificada 2.4.1. OPCIONES GENERALES * Instalación de InterNestor Lite inl i [/s:] [] Instala INL, dejándolo listo para su uso. El driver Fossil debe haber sido instalado previamente, y en caso de usar DOS 2 debe haber dos segmentos libres en el mapeador primario. El parámetro opcional /s indica que INL debe ser instalado en los segmentos y +1. Si no se especifica, en caso de usar DOS 1 se usarán los dos últimos segmentos del mapeador primario, y en caso de usar DOS 2 se reservarán dos segmentos, también en el mapeador primario. Esta opción sólo debería usarse en caso de conflictos con otros programas residentes y sólo en DOS 1. El parámetro opcional [] indica el comando DOS a ejecutar tras la instalación de INL. Sólo funciona en DOS 2. En caso de usar COMMAND 2.4x, se pueden especificar varios comandos, separados con "&". La velocidad de transmisión del RS232 se establece a 9600 baudios si usas un MSX2/2+, o un Turbo-R en modo Z80; si usas un Turbo-R en modo R800, se establece a 19200 baudios. Un Z80 sólo soporta hasta 9600 baudios, a más velocidad hay pérdidas de datos. Es posible automatizar el proceso de configuración de INL durante la instalación de la siguiente manera. Durante la instalación se comprobará la existencia de un fichero llamado INL.CFG en el mismo directorio de INL.COM, y si existe, se usará su contenido para configurar INL exactamente de la misma forma en que lo hace el comando INL F. Ver la descripción de dicho comando para más detalle * Desinstalación de InterNestor Lite inl u [] Desinstala INL, liberando los segmentos reservados en caso de usar DOS 2. [] funciona como en el caso de la instalación (INL I). * Pausa de la ejecución de InterNestor Lite inl p Esta opción desconecta INL del gancho de la interrupción del reloj del sistema. Lo que se consigue con ello es pausar INL, es decir, impedir que realice ningún proceso (no se ejecuta su código residente 50/60 veces por segundo). Esto puede ser útil cuando se necesita realizar alguna acción de larga duración (por ejemplo copiar un conjunto de ficheros grandes), pues cuando INL está instalado y activo ralentiza el funcionamiento global del ordenador. Hay que tener en cuenta que, lógicamente, los paquetes de datos que lleguen mientras INL esté pausado se perderán. Se puede reanudar la ejecución de INL con INL R, como se explica a continuación. * Reanudación de la ejecución de InterNestor Lite inl r Esta opción vuelve a conectar INL al gancho de la interrupción del reloj del sistema, restaurando así el modo normal de funcionamiento que había sido interrumpido previamente al pausar INL con INL P. No hace nada si INL ya está activo. * Información sobre la conexión PPP y direcciones IP inl s Esta opción muestra información sobre el estado de la conexión PPP, la causa del cierre de la última conexión PPP abierta y las direcciones IP establecidas (local, remota y de los servidores DNS). También indica si INL se encuentra activo o pausado. La información sobre las direcciones IP se muestra aunque no haya ninguna conexión abierta. En tal caso, dicha información se refiere a las direcciones IP que fueron usadas en la última conexión, o bien a las direcciones IP que han sido establecidas por el usuario para ser usadas en la próxima conexión. Ver la descripción de las opciones referentes al protocolo IP para más detalles. * Información sobre las variables de configuración de INL inl v Esta opción muestra información sobre las diversas variables que gobiernan el funcionamiento global de INL en lo referente a la conexión PPP y al protocolo IP. Estas variables se listan más adelante; para cada una de ellas existe una opción de INL.COM que permite su modificación. * Restablecimiento de las variables de configuración de INL a sus valores por defecto inl d Esta opción restablece todas las variables de configuración de INL a sus valores por defecto, es decir, a los valores que tienen inmediatamente después de la instalación. Estos valores son los siguientes: Número de teléfono del ISP: Cadena vacía Respuesta positiva del modem: CONNECT Nombre de usuario PPP: Cadena vacía Password PPP: Cadena vacía Negociar las direcciones IP de los servidores DNS: Sí Responder PINGs (peticiones de eco ICMP) entrantes: Sí Enviar periódicamente peticiones de eco PPP: Sí Negociar compresión de Van Jacobson: Sí TTL para los datagramas salientes: 64 TOS para los datagramas salientes: 0 Vector de cálculo de checksums: 31 Tamaño por defecto de los PINGs salientes (parte de datos): 64 No se debe llamar a esta función cuando hay una conexión abierta, ya que todas las direcciones IP también se restablecen a 0.0.0.0. * Establecimiento del vector de cálculo de checksums inl o Mediante la modificación de este valor es posible indicar a INL que dé por buenos los paquetes entrantes sin calcular uno o más de los checksums asociados. De esta forma se gana velocidad de ejecución a riesgo de dar por buenos paquetes defectuosos. Puede ser útil cuando se conectan dos ordenadores directamente, ya que en tal caso el riesgo de que lleguen paquetes erróneos es casi nulo. es un número entre 0 y 31 que debe interpretarse como un vector de seis bits. Cada bit gobierna el cálculo de un determinado tipo de checksum: si está a uno, ese checksum se calcula para todos los paquetes entrantes; si está a cero, ese checksum nunca se calcula, asumiendo que de calcularlo sería correcto. Los bits de están asignados como sigue: Bit 0 (valor 1): FCS de las tramas PPP Bit 1 (valor 2): Checksum de la cabecera de los datagramas IP Bit 2 (valor 4): Checksum de los segmentos TCP Bit 3 (valor 8): Checksum de la cabecera de los paquetes UDP Bit 4 (valor 16): Checksum de los mensajes ICMP (sólo eco) Por ejemplo, si sólo quieres que sean calculdos los checksums para PPP, TCP y UDP, usa el vector 1+4+8=13: INL O 13. El valor por defecto es 31, es decir que se calculan todos los checksums. En general debería dejarse así, a no ser que notes problemas de velocidad en el ordenador. * Lectura de un fichero de configuración inl f [] Esta opción permite ejecutar múltiples opciones de INL.COM con una sola ejecución del mismo; las opciones a ejecutar se almacenan en un fichero de texto, cuya ruta y nombre se pasan como argumento. Si no se pasa nombre de fichero, se usa el fichero INL.CFG situado en el mismo directorio que INL.COM. El fichero de texto debe contener las opciones a ejecutar, con la misma sintaxis que se usaría en caso de ejecutarlas directamente. Por ejemplo, si se quiere establecer el vector de cálculo de checksums en 31, desactivar el envío de ecos PPP y establecer la dirección IP local en 1.2.3.4, el contenido del fichero de configuración debería ser el siguiente: o 31 ppp e 0 ip l 1.2.3.4 Las líneas en blanco y las que comienzan con un carácter ";" se ignoran. No es posible anidar lecturas de ficheros de configuración (es decir, no se puede ejecutar la opción "f" desde un fichero de configuración). Tampoco es posible desinstalar INL (opción "u") desde un fichero de configuración. 2.4.2. OPCIONES DE MODEM Y CONEXION PPP * Envío de un comando al modem inl ppp m Esta opción envía la cadena al modem, espera una respuesta del mismo y la muestra. Puede ser útil, por ejemplo, para silenciar el altavoz del modem antes de realizar una conexión (lo cual se consigue con el comando ATM0). * Establecimiento de la velocidad de transmisión del interfaz RS232 inl ppp b Esta opción establece la velocidad de transmisión del interfaz RS232 según el especificado: 0 para 75 baudios 1 para 300 baudios 2 para 1200 baudios 3 para 1200 baudios 4 para 2400 baudios 5 para 4800 baudios 6 para 9600 baudios 7 para 19200 baudios 8 para 38400 baudios 9 para 57600 baudios 10 ó 11 para 115200 baudios Recuerda que INL sólo soporta hasta 19200 baudios en R800 y hasta 9600 baudios en Z80, a más velocidad hay pérdida de datos. Tras la instalación de INL la velocidad habrá sido establecida automáticamente al máximo valor admisible según el procesador detectado. * Establecimiento del número de teléfono del ISP inl ppp n [] Esta opción establece el número de teléfono que marcará el modem al abrir una conexión por medio de INL PPP O. Si es una cadena vacía, no se marcará ningún número y se intentará abrir la conexión PPP directamente (asumiendo que al otro lado hay otro equipo conectado directamente con cable null-modem). La longitud máxima para es de 15 carácteres. Si se especifica una cadena mayor, sólo se tienen en cuenta los 15 primeros. * Establecimiento de la respuesta positiva del modem inl ppp r Esta opción establece la parte inicial de la cadena que devolverá el modem cuando consiga realizar una marcación al ISP sin problemas durante el establecimento de la conexión mediante INL PPP O. INL.COM necesita conocer esta cadena para, una vez finalizada la marcación, decidir si debe mostrar un error o por el contrario puede continuar y abrir la conexión PPP, según la respuesta del modem. En la mayoría de los modems esta cadena es "CONNECT", y esa es por tanto la cadena que INL establece por defecto. No es necesario indicar la respuesta completa, basta con especificar los primeros carácteres. Por ejemplo, los modems suelen responder algo como "CONNECT 48000/V24BIS", pero basta con especificar "CONNECT". La longitud máxima para es de 15 carácteres. Si se especifica una cadena mayor, sólo se tienen en cuenta los 15 primeros. No es posible especificar una cadena vacía. * Establecimiento del nombre de usuario PPP inl ppp u [] Esta opción establece el nombre de usuario PPP que se usará durante el establecimiento de la conexión mediante INL PPP O; en otras palabras, el nombre de usuario de la cuenta de acceso a internet que proporciona el ISP. Si se especifica una cadena vacía, INL cancelará la conexión si se requiere autentificación. La longitud máxima para es 31 carácteres. Si se especifica una cadena mayor, sólo se tienen en cuenta los 31 primeros. * Establecimiento del password PPP inl ppp p [] Esta opción establece el password PPP que se usará durante el establecimiento de la conexión mediante INL PPP O; en otras palabras, el password de la cuenta de acceso a internet que proporciona el ISP. La longitud máxima para es 31 carácteres. Si se especifica una cadena mayor, sólo se tienen en cuenta los 31 primeros. * Apertura de una conexión PPP inl ppp o [/n:[]] [/u:[]] [/p:[]] [/r:] [/i] Esta opción inicia una conexión PPP, bien a internet por medio de un ISP, bien a otro equipo conectado directamente mediante cable null-modem. es el número de teléfono del ISP que será marcado por el modem. Si es una cadena vacía, no se marcará ningún número (es decir, INL asumirá que al otro lado hay otro equipo conectado directamente mediante cable null- modem). La longitud máxima es de 15 carácteres. y son los datos de autentificación PPP que se usarán (es decir, los datos de la cuenta que nos porporciona el ISP). Si no se especifica , INL.COM cancelará la conexión si se requiere autentificación. La longitud máxima para ambos es de 31 carácteres. es la parte inicial de la cadena que devuelve el modem tras realizar la marcación al ISP si consigue conectar sin problemas. En la mayoría de los modems esta cadena es "CONNECT", que es la cadena establecida por defecto por INL, y no es necesario modificarla. La longitud máxima es de 15 carácteres. /I causa que todas las direcciones IP (local, remota y los dos DNS) se pongan a 0.0.0.0, y activa la negociación de las direcciones IP de los servidores DNS, antes de iniciar la conexión. Esta es el comportamiento más normal, ya que habitualmente conectamos a internet por medio de un ISP que nos proporciona dinámicamente todas las direcciones. Sin embargo puede ser que estemos conectándonos con otro equipo mediante cable null-modem y ya conozcamos (o queramos proporcionar al otro equipo) una o más de las direcciones IP a usar. En ese caso estableceríamos previamente las direcciones de interés con alguna de las opciones INL IP x, y no especificaríamos /I al conectar. Si no se especifica alguno de los parámetros /N, /U, /P y/o /R, se usará el valor que haya sido especificado previamente mediante INL PPP N, INL PPP U, INL PPP P y/o INL PPP R, respectivamente. Es decir, las siguientes secuencias de ejecución son equivalentes: - Secuencia 1: INL PPP N 12345 INL PPP U kyoko INL PPP P jap0paya INL PPP O /R:OK - Secuencia 2: INL PPP O /N:12345 /U:kyoko /P:jap0paya /R:OK Ojo: no es lo mismo especificar un valor nulo para un parámetro que no especificar el parámetro (/N a secas significa que no se marcará ningún número de teléfono; no especificar /N significa que se marcará el número especificado previamente con INL PPP N). * Cierre de una conexión PPP inl ppp c Esta opción cierra la conexión a Internet (o al otro equipo en caso de conexión directa); normalmente el modem se desconectrá automáticamente del ISP al cabo de pocos segundos. No se modifican las direcciones IP ni ninguno de los parámetros de INL que hayan sido modificados antes de o durante la conexión. * Activación o desactivación del eco PPP automático inl ppp e 0|1 INL no es capaz de detectar la caída física de la conexión (es decir, la pérdida de portadora por parte del modem o el apagado o bloqueo del otro ordenador en caso de conexión directa). Por este motivo usa un mecanismo de comprobación soportado por el protocolo PPP, consistente en el envío periódico de paquetes de eco al ISP o al otro ordenador (ojo: son paquetes a nivel de protocolo PPP; no confundir con los PINGs, que son paquetes a nivel IP). INL tiene activada esta característica por defecto, pero puede desactivarse ejecutando INL PPP E 0, y volverse a activar con INL PPP E 1. Cuando esté activada, INL enviará paquetes de eco PPP al otro extremo cada cinco segundos; si envía cuatro paquetes consecutivos sin obtener respuesta, asumirá que la conexión ha caído y la cerrará automáticamente. * Activación o desactivación de la negociación de la compresión de Van Jacobson inl ppp v 0|1 Esta opción activa (1) o desactiva (0) la negociación de la compresión Van Jacobson de cabeceras TCP/IP durante el establecimiento de la conexión PPP; si se negoca y el otro extremo también soporta dicha compresión, se usará durante toda la vida de la conexión. La compresión de Van Jacobson es específica de las conexiones punto a punto y permite reducir el tamaño de las cabeceras TCP/IP, originalmente de 40 bytes, hasta un promedio de cinco bytes. A cambio, se requiere un procesado adicional para todos los segmentos TCP que se envían y reciben. Los cambios en esta opción tienen efecto la siguiente vez que se abre una conexión PPP; no se puede activar o desactivar la compresión Van Jacobson en una conexión ya abierta. 2.4.3. OPCIONES DE PROTOCOLO IP * Inicialización de las direcciones IP inl ip i Esta opción inicializa todas las direcciones IP (local, remota y de los dos servidores DNS) a 0.0.0.0. Por convenio, anunciar una dirección 0.0.0.0 durante la negociación PPP equivale a anunciar que no conocemos dicha dirección IP y que por tanto solicitamos que el otro extremo nos asigne una (todas las conexiones a ISPs funcionan de este modo). * Establecimiento de la dirección IP local inl ip l Esta opción establece la dirección IP local con el valor especificado. Nunca debe usarse cuando ya hay una conexión abierta. El uso de esta opción sólo tiene sentido en conexiones directas por cable, y sólo si sabemos que el otro extremo no nos puede proporcionar una dirección IP. Si el otro extremo nos proporciona una dirección IP durante el establecimiento de la conexión (lo cual siempre ocurrirá en las conexiones mediante ISP), dicha dirección prevalecerá sobre la que nosotros hayamos establecido previamente. * Establecimiento de la dirección IP remota inl ip r Esta opción establece la dirección IP remota (es decir, la del otro extremo de la conexión PPP) con el valor especificado. Nunca debe usarse cuando ya hay una conexión abierta. El uso de esta opción sólo tiene sentido en conexiones directas por cable, y sólo si sabemos que el otro extremo no conoce su propia dirección IP y debemos proporcionársela. Si el otro extremo nos comunica que ya conoce su dirección IP durante el establecimiento de la conexión (lo cual siempre ocurrirá en las conexiones mediante ISP), dicha dirección prevalecerá sobre la que nosotros hayamos establecido previamente. * Establecimiento de la dirección IP del servidor DNS primario inl ip p Esta opción establece la dirección IP del servidor DNS primario, es decir, el servidor de nombres al que el resolver integrado en INL consultará cuando se solicite la resolución de un nombre de host. La lógica de este parámetro es similar a la lógica de los parámetros IP L e IP R (direcciones IP local y remota): las direcciones propuestas por el otro extremo durante la conexión PPP prevalecerán sobre las direcciones que hayamos establecido manualmente antes de iniciar la conexión. Sin embargo existen dos diferencias importantes: 1) No hay ningún problema en modificar la dirección IP del servidor DNS una vez iniciada la conexión PPP. 2) Es posible desactivar la negociación de esta dirección ejecutando INL IP N 0 antes de abrir la conexión. En este caso, la dirección que hayamos especificado mediante INL IP P siempre seguirá siendo válida una vez iniciada la conexión (de hecho, ni siquiera le daremos oportunidad al otro extremo de proponer una dirección alternativa). Esto es cierto incluso en las conexiones vía ISP. El uso de INL IP N 0 seguido de INL IP P previo a la conexión es útil cuando queremos usar un servidor DNS diferente del proporcionado por el ISP. Nota: El parámetro /I de INL PPP O no sólo establece todas las direcciones IP a 0.0.0.0, sino que también activa la negociación de las direcciones de los DNS. Por tanto, si se desea conectar a un ISP sin negociar las direcciones de los DNS se deberá usar esta secuencia de ejecución: INL IP I INL IP N 0 INL PPP O /N: /U: /P: * Establecimiento de la dirección IP del servidor DNS secundario inl ip s Esta opción funciona como INL IP P, pero hace referencia a la dirección del sevidor DNS secundario. El resolver de INL siempre consulta al servidor DNS primario en primer lugar; sólo si éste no responde repetirá la consulta usando el servidor DNS secundario. * Activación o desactivación de la negociación de los servidores DNS inl ip n 0|1 Esta opción activa o desactiva la negociación de las direcciones IP de los servidores DNS para las conexiones PPP que se abran en el futuro. Por defecto esta negociación está activada; se puede desactivar ejecutando INL IP N 0 y volver a activarla con INL IP N 1. La opción /I de INL PPP O también activa esta negociación. Suprimir la negociación de las direcciones de los servidores DNS puede ser útil cuando queremos usar servidores diferentes a los proporcionados por el ISP. Ver la descripción de INL IP P más arriba para más detalles. * Activación o desactivación de la respuesta automática a PINGs inl ip e 0|1 Esta opción activa o desactiva el envío automático de respuesta para todas las peticiones de eco ICMP (PINGs) recibidas desde cualquier máquina de Internet. Por defecto esta característica está activada; se puede desactivar ejecutando INL IP E 0 y volver a activarla con INL IP E 1. * Establecimiento del TTL de los datagramas salientes inl ip t Esta opción establece el valor del campo TTL (Time To Live) para todos los datagramas salientes; debe ser un número entre 1 y 255. El valor por defecto es 64 y en condiciones normales no debería cambiarse. * Establecimiento del TOS de los datagramas salientes inl ip o Esta opción establece el valor del campo TOS (Type Of Service) para todos los datagramas salientes; debe ser un número entre 0 y 15. El valor por defecto es 0 y en condiciones normales no debería cambiarse. * Establecimiento del tamaño por defecto para los PINGs salientes inl ip z Esta opción modifica el tamaño por defecto de la parte de datos de las solicitudes de eco ICMP (PINGs) que se envíen usando la función que INL proporciona a los programadores para este propósito. Dicha función permite especificar el tamaño del paquete a enviar, pero también admite el valor especial -1, que significa que se usará el valor por defecto especificado con este comando. debe estar entre 0 y 548. El programa PING.COM suministrado con INL siempre envía solcitudes de eco con el tamaño por defecto. 2.4.4. OPCIONES DE RESOLUCION DE NOMBRES * Resolución de un nombre de host inl dns r Esta opción pedirá al resolver incoroporado en INL que resuelva el nombre de host pasado, y mostrará la dirección IP resultante en caso de que la consulta tenga éxito, o un mensaje de error en caso contrario. Mientras se realiza la consulta es posible cancelar el proceso pulsando cualquier tecla; esto causará que el programa finalice inmediatamente sin mostrar ningún resultado. 2.4.5. OPCIONES DE PROTOCOLO TCP * Obtención de información sobre el estado de las conexiones TCP inl tcp s [] Esta opción muestra información sobre la conexión especificada (un número entre 0 y 3), o sobre todas las conexiones si no se especifica ninguna conexión. Si la conexión no existe (el estado de la misma es CLOSED), se muestra la causa del cierre de la misma o se indica que esa conexión nunca ha sido usada. En caso contrario, se muestra el estado TCP de la conexión, el puerto local y la dirección IP y puerto remotos usados. * Cierre de una conexión TCP inl tcp c Esta opción cierra la conexión especificada (un número entre 0 y 3) mediante una llamada al método estandar CLOSE de TCP. El hecho de cerrar una conexión TCP no implica que la conexión deje de existir inmediatamente, ya que para ello es necesario que el otro extremo de la conexión también la cierre. Por otro lado, si aún quedan datos pendientes de envío la petición de cierre no se hará efectiva hasta que se hayan enviado todos. Por tanto, si lo que se desea es destruir la conexión y dejarla libre inmediatamente, en vez de cerrarla es necesario abortarla (ver opción TCP A a continuación). * Abortar una conexión TCP inl tcp a Esta opción aborta la conexión especificada (un número entre 0 y 3) mediante una llamada al método estandar ABORT de TCP. La conexión pasa inmediatamente al estado CLOSED y queda libre para ser abierta nuevamente. 2.5. APLICACIONES INCLUIDAS En esta sección se describe el uso de las aplicaciones de Internet suministradas con esta distribución de INL. 2.5.1 EL CLIENTE DE PING PING.COM es un cliente de petición de eco ICMP (PING) sencillo que permite averiguar si un determinado ordenador de Internet está disponible. Se usa de la siguiente forma: PING | El programa enviará un único paquete de petición de eco, pero es posible enviar más de forma manual pulsando Enter (pulsando cualquier otra tecla, el programa terminará). Cada vez que llegue la respuesta a una de las peticiones de eco enviadas, se mostrará en pantalla información sobre su número de secuencia y TTL. PING.COM siempre envía los paquetes de eco con TTL igual a 255 y el tamaño por defecto para la parte de datos (definible mediante INL IP Z). 2.5.2. EL CLIENTE/SERVIDOR DE TFTP TFTP.COM es una utilidad de transferencia de archivos entre dos máquinas mediante TFTP, un protocolo simple basado en UDP (descrito en RFC1350). Una transferencia TFTP requiere que una de las máquinas funcione en modo cliente y la otra en modo servidor; TFTP.COM soporta ambos modos. TFTP.COM sólo permite transferir un archivo a la vez, y no dispone de mecanismos de seguridad (cualquier archivo existente será transferido a o desde el servidor a petición del cliente). Al principio del listado de la aplicación, en el fichero TFTP-S.ASC, se mencionan otras limitaciones de la misma. Existen tres formas de ejecutar TFTP.COM según el modo de funcionamiento y la dirección de la transferencia: * Modo cliente, para recibir un fichero: TFTP R[CV]|G[ET] [] indica el nombre o la dirección IP de la máquina que nos enviará el fichero (que debe estar ejecutando un servidor de TFTP). es la ruta y el nombre del fichero que se nos enviará, en el formato adecuado al sistema de ficheros del host remoto (no puede contener espacios). Debe ser un fichero accesible según la directiva de seguridad del host remoto, si es que tiene alguna. es la ruta y el nombre del fichero que se creará en el ordenador local para alojar los datos recibidos. Debe ser una ruta y nombre válidos en DOS 1 o DOS 2, según la versión que usa el ordenador local. Si se omite la ruta, se usa la unidad y el directorio establecidos por defecto. Si se omite , se usará sin la información de ruta. Esto sólo es posible bajo DOS 2, y sólo si tiene una ruta válida para DOS 2. * Modo cliente, para enviar un fichero: TFTP S[END]|P[UT] [] indica el nombre o la dirección IP de la máquina a la que enviaremos el fichero (que debe estar ejecutando un servidor de TFTP). es la ruta y el nombre del fichero que enviaremos. Debe ser un fichero existente en la máquina local. es la ruta y el nombre del fichero tal como lo anunciaremos al ordenador remoto al iniciar el envío. Debe ser una ruta válida para su sistema de ficheros (y accesible según su directiva de seguridad, si la tiene). Si se omite , se usará sin la información de ruta. Esto sólo es posible si el nombre resultante es aceptable para el ordenador remoto según su sistema de ficheros y directiva de seguridad. * Modo servidor TFTP /S[ERVER] En este modo, la aplicación quedará a la espera de peticiones de envío y recepción de ficheros; serán servidas todas las peticiones de envío que hagan referencia a ficheros existentes, y todas las peticiones de recepción que indiquen un nombre de fichero válido para DOS 1 o DOS 2 (no hay ninguna directiva de seguridad). En este modo se puede salir del programa pulsando cualquier tecla. - Ejemplo 1 Para recibir el fichero PruebaDeTexto.Text del directorio /home/konamiman de una máquina con Linux, y almacenarlo localmente en c:\textos con el nombre prueba.txt TFTP mimaquinalinux.com G /home/konamiman/PruebaDeTexto.Text c:\textos\prueba.txt - Ejemplo 2 Para enviar el fichero programa.com a otro MSX que esté ejecutando TFTP.COM en modo servidor, y hacer que éste lo almacene con el mismo nombre en su directorio por defecto: TFTP 192.168.0.2 S c:\cosas\programa.com 2.5.3. LA CONSOLA TCP TCPCON.COM es una utilidad simple de cónsola TCP. Una vez establecida una conexión con el host deseado, todos los datos recibidos se imprimen directamente en pantalla, y todos los datos que se introducen mediante el teclado se envían directamente a la conexión; no se realiza ningún proceso adicional en los datos recibidos ni en los enviados. Se puede ver TCPCON como un cliente de Telnet simplificado (es un cliente de Telnet excepto por el hecho de que no procesa los comandos Telnet recibidos y nunca envía dichos comandos; además de por el hecho de no procesar los datos urgentes, ya que INL no los soporta). TCPCON se usa de la siguiente forma: TCPCON [] [P] indica el nombre o la dirección IP de la máquina a la que queremos conectar. indica el puerto TCP al que queremos conectar. Puertos comunmente usados son 23 para Telnet, 25 para SMTP y 110 para POP3. es un parámetro opcional que indica el puerto TCP que usaremos localmente. Si no se especifica, INL escogerá uno aleatoriamente en el rango 16384 a 32767. "P" es un parámetro opcional que indica que la conexión debe abrirse en modo pasivo, es decir, TCPCON esperará a que el host remoto intente conectar con nosotros en vez de iniciar activamente la conexión. Si se especifica conexión pasiva, puede usarse 0.0.0.0 como un valor especial de nombre del host. En este caso la conexión se abre en modo "socket remoto sin especificar", lo cual implica que se aceptará una petición de conexión procedente de cualquier máquina y de cualquier puerto remoto (en este caso aún es necesario especificar el número de puerto remoto, pero dicho parámetro se ignora; se puede especificar por ejemplo el valor 0). Una vez abierta la conexión, la aplicación entra en un bucle en el que imprime por pantalla todos los datos que llegan, y envía todo lo que se introduce mediante el teclado. Se aceptan además las siguientes teclas especiales: F1: Muestra una breve ayuda. F2: Cambia el modo de entrada de datos entre modo línea (se envían líneas de texto completas, finalizadas con CR-LF, cada vez que se pulsa Enter) y modo carácter (cada vez que se teclea un carácter, éste es enviado inmediatamente). F3: Activa o desactiva el eco local (sólo para modo carácter). ESC: Cierra la conexión y termina el programa. CTRL+ESC: Aborta la conexión y termina el programa. CTRL+C o CTRL+STOP: Termina el programa y vuelve al DOS. Bajo DOS 2, la conexión es abortada antes de terminar. Bajo DOS 1 será necesario cerrarla manualmente con INL TCP C o INL TCP A (es mejor usar ESC o CTRL+ESC para salir). Si el otro extremo cierra o aborta la conexión, la aplicación finaliza inmediatamente. Ejemplos: - Conexión activa a telnet.molamazo.com, puerto 23, usando un puerto local aleatorio: TCPCON www.molamazo.com 23 - Conexión pasiva a nuestro puerto 34, el host debe tener la dirección IP 1.2.3.4 y usar el puerto 100: TCPCON 1.2.3.4 100 34 P - Conexión pasiva a nuestro puerto 7, socket remoto sin especificar (aceptamos cualquier petición de conexión contra nuestro puerto 7): TCPCON 0.0.0.0 0 7 P 2.5.4. EL CLIENTE DE TELNET TELNET.COM es un programa similar a TCPCON, pero que cumple las especificaciones del protocol Telnet descritas en RFC854 y RFC855. TELNET se usa de la siguiente forma: TELNET [] [P] indica el nombre o la dirección IP de la máquina a la que queremos conectar. indica el puerto TCP al que queremos conectar, por defecto es el puerto 23. El puerto local usado siempre es el 23. "P" es un parámetro opcional que indica que la conexión debe abrirse en modo pasivo, es decir, TELNET esperará a que el host remoto intente conectar con nosotros en vez de iniciar activamente la conexión. Si se especifica conexión pasiva, puede usarse 0.0.0.0 como un valor especial de nombre del host, lo cual indica que se aceptará conexión desde cualquier máquina, igual que ocurría con TCPCON. Las teclas con función especial en TELNET son las mismas de TCPCON, excepto F1. En TELNET, al pulsar F1 aparecerá una pantalla de estado/opciones que permite enviar carácteres especiales al servidor y configurar algunos parámetros de la comunicación (por ejemplo, desde dicha pantalla se le puede pedir al servidor que envíe, o deje de enviar, eco de toda la información que recibe). TELNET reconoce algunas secuencias de escape ANSI, en concreto las relacionadas con el posicionamiento del cursor en la pantalla. De esta forma es posible la conexión a terminales VT100, aunque con funcionalidad limitada. TELNET reconoce y procesa las opciones SUPRESS-GA, BINARY-TRANSMISSION, EOR, ECHO y TERMINAL-TYPE. Respecto a esta última, se anuncian los siguientes tipos de terminal según las reglas definidas en RFC1091: ANSI, NETWORK- VIRTUAL-TERMINAL y UNKNOWN, en ese orden. 3. INTERNESTOR LITE - GUIA DEL PROGRAMADOR En esta parte del manual de INL se explica todo lo necesario para desarrollar programas que hagan uso de INL para acceder a Internet. 3.1. PARTES DE INTERNESTOR LITE Una vez instalado, INL modifica el gancho de la interrupción del reloj y ocupa tres zonas de memoria: - Una zona de salto en la página 3. - Un segmento del mapeador primario usado para almacenar código. Esta será la zona a la que normalmente accederá el programador para interactuar con INL. - Otro segmento del mapeador primario usado para almacenar variables y búferes de datos. A continuación se detalla el contenido de cada una de estas zonas, así como la manera de averiguar su localización. 3.1.1. DETECCION DE INTERNESTOR LITE Y LOCALIZACION DE LAS ZONAS Para averiguar si INL está instalado, y para obtener los números de segmento y la dirección de la zona de salto de la página 3, se usa el gancho de la BIOS extendida, #FFCA. El procedimiento es el siguiente: 1) Se realiza una llamada a #FFCA pasando A=0 y DE=#2203. 2) Si INL no está instalado, la llamada no hace nada y no modifica AF, BC, DE ni HL; por tanto devuelve A=0. 3) Si INL está instalado, la llamada devuelve A=1, y además la siguiente información: B = Número del segmento de código (se refiere siempre al mapeador primario). C = Número del segmento de variables y búferes (se refiere siempre al mapeador primario). HL = Dirección de la zona de salto en página 3. El siguiente es un ejemplo de código para la detección de INL: xor a ld de,#2203 call #FFCA or a jp z,ERROR ;INL no instalado ld a,b ld (INL_SEG1),a ld a,c ld (INL_SEG2),a ld (INL_P3DIR),hl ... INL_SEG1: ds 1 ;Segmento de código INL_SEG2: ds 1 ;Segmento de datos INL_P3DIR: ds 2 ;Dirección de la zona de salto NOTA: En las versiones de INL anteriores a la 1.0, la detección de INL y la localización de las zonas se relizaba consultando un área de seis bytes situada en una posición fija en memoria. Se incluye aquí una descripción de dicha área como ayuda para la modificación de los programas que funcionen con versiones antiguas de INL: * P3_ID (#F400): Cadena "IN" (bytes #49, #4E). La presencia de esta cadena indica que INL está instalado. * P3_SEG1 (#F402): Número del segmento de código (se refiere siempre al mapeador primario). * P3_SEG2 (#F403): Número del segmento de variables y búferes (se refiere siempre al mapeador primario). * P3_P3DIR (#F404): Dirección de la zona de salto en página 3. 3.1.2. LA ZONA DE SALTO La zona de salto se sitúa al final de la TPA, en la dirección indicada por P3_P3DIR en la zona de punteros. Es una zona de unos 256 bytes a cuyo interior apunta el gancho de la interrupción del reloj; el código así llamado se encarga de conectar el slot+segmento de INL en la página 1, pasar el control a la rutina de servicio a la interrupción del reloj de INL, y posteriormente restaurar el slot+segmento originales y pasar el control al gancho de interrupción original. Es decir, el comportamiento típico de cualquier programa residente que cargue código en un segmento de RAM. Sin embargo, la zona de salto no se limita a eso: a partir de la posición +12 de la misma hay una tabla de salto a rutinas de bajo nivel que pueden resultar de utilidad para el programador. Son las siguientes (el desplazamiento indicado, en decimal, es relativo a la dirección indicada por P3_P3DIR): +12: LDIRP3. Rutina auxiliar que resulta de utilidad a la hora de copiar bloques de datos de un segmento a otro. El proceso que realiza es el siguiente: 1) Conecta el slot pasado en L y el segmento pasado en H en la página 1. 2) Copia BC bytes desde la dirección pasada en IX a la dirección pasada en IY. 3) Restaura el segmento y el slot originales en la página 1. +15: PUT_P1. Se trata de una rutina para establecer el segmento de la página 1 de forma independiente de la versión del DOS usada: bajo DOS 1, se ejecutará una instrucción OUT (#FD),A; en cambio bajo DOS 2 se saltará a la rutina PUT_P1 proporcionada por el DOS. Usando este gancho y los tres siguientes es posible realizar cambios de segmento sin tener que preocuparse por la versión del DOS utilizada. +18: GET_P1. La rutina complementaria de la anterior: ejecutará una instrucción IN A,(#FD) o saltará a la rutina del DOS GET_P1, según la versión del DOS utilizada. +21: PUT_P2. Como PUT_P1, pero para la página 2. +24: GET_P2. Como GET_P1, pero para la página 2. +27: GETSLOT1. Devuelve en A el slot actualmente conectado en la página 1, en el formato estándar %E000SSPP (Extendido, Secundario, Primario). +30: PUTSLOT1. Establece en la página 1 el slot pasado en A, pero sólo si A<>B. Si se hace LD B,A antes de llamarla equivale a llamar directamente a ENASLT. 3.1.3. EL SEGMENTO DE CODIGO Esta es la parte más importante de INL de cara al programador. Se trata de un segmento de RAM del mapeador primario que contiene todo el código ejecutable de INL; al principio del mismo hay una tabla de salto a rutinas que el programador puede usar para interactuar con INL (abrir y cerrar la conexión PPP, enviar y recibir datos, obtener y establecer parámetros de configuración de INL, o realizar algunas operaciones auxiliares de manipulación de datos). El proceso de llamada a una rutina de INL es como sigue: 1) Asegúrate de que el mapeador primario está conectado en las páginas 1 y 2 (éste es el estado por defecto para los programas .COM). 2) Conecta el segmento de código de INL en la página 1 (puedes obtener el número de segmento desde la variable P3_SEG1 en la zona de punteros, y puedes uar la rutina PUT_P1 de la zona de salto para conectar el segmento). 3) Llama a la rutina deseada de INL con un simple CALL. 4) Opcionalmente, restaura el segmento/slot anteriores en página 1 y/o 2 (a este respecto, ver la sección 3.2, "Consideraciones de programación", más adelante). Observa que en el caso de la página 2 sólo hay que asegurar que está conectado el slot del mapeador primario, no es necesario hacer ningún cambio de segmento. A continuación se listan las rutinas disponibles en la tabla de salto, y más adelante se detalla el funcionamiento de cada una de ellas. * Interrupción del reloj - TIME_INT (#4000): Punto de entrada para la rutina de servicio a la interrupción del reloj. No debe ser llamado directamente desde un programa. * Manejo de slots y segmentos (las rutinas LDIRP3 a PUTSLOT1 son saltos directos a las rutinas homónimas en la zona de salto en página 3, descrita anteriormente) - LDIRP3 (#4003) - PUT_P1 (#4006) - GET_P1 (#4009) - PUT_P2 (#400C) - GET_P2 (#400F) - GETSLOT1 (#4012) - PUTSLOT1 (#4015) - GETSLOT2 (#4018): Obtiene el slot conectado en la página 2. - PUTSLOT2 (#401B): Conecta un determinado slot en la página 2. * Acceso al puerto RS232 (se trata de saltos directos a las rutinas homónimas proporcionadas por el driver Fossil) - RS_IN (#401E): Obtiene un byte entrante. - RS_OUT (#4021): Envía un byte. - RS_IN_STAT (#4024): Comprueba si hay bytes entrantes disponibles. - RS_OUT_STAT (#4027): Comprueba si se pueden enviar bytes (no usado por el código de INL en esta versión, los datos se envían directamente). * Manipulación de datos y configuración - VERS_PAUSE (#402A): Obtiene el número de versión de INL, y consulta o modifica el estado de pausa/activación de INL. - GET_VAR (#402D): Lee una variable del segmento de datos de INL. - SET_VAR (#4030): Modifica una variable del segmento de datos de INL. - COPY_DATA (#4033): Copia un bloque de datos del segmento de datos de INL a TPA o viceversa. - IP_STRING (#4036): Genera una cadena que representa una dirección IP dada. - CALC_MD5 (#4039): Calcula el hash MD5 de un cierto bloque de datos de hasta 1024 bytes. - CALC_CHKSUM (#403C): Calcula el checksum estandar de un cierto bloque de datos de hasta 1024 bytes. - B64_INIT (#403F): Inicia una operación de codificación o decodificación Base64. - B64_ENCODE (#4042): Codifica un bloque de datos en Base64. - B64_DECODE (#4045): Decodifica un bloque de datos codificado en Base64. * Acceso al modem y conexión PPP - SEND_MODEM (#4048): Envía un comando al modem y devuelve su respuesta. - PPP_OPEN (#404B): Abre una conexión PPP. - PPP_CLOSE (#404E): Cierra la conexión PPP. * Envío y recepción de PINGs - SEND_ECHO (#4051): Envío de un paquete de petición de eco ICMP (PING). - RCV_ECHO (#4054): Obtención del paquete de respuesta de eco ICMP (PING) más antiguo recibido. * Envío y recepción de paquetes UDP - UDP_SEND (#4057): Envío de un paquete UDP. - UDP_RCV (#405A): Obtención del paquete UDP más antiguo recibido. * Resolución de nombres - DNS_Q (#405D): Efectúa una petición de resolución de nombre. - DNS_S (#4060): Obtención del estado de la petición de resolución de nombre. * Conexiones TCP - TCP_OPEN (#4063): Abre una conexión TCP. - TCP_CLOSE (#4066): Cierra una conexión TCP. - TCP_ABORT (#4069): Aborta una conexión TCP. - TCP_SEND (#406C): Envía datos a una conexión TCP. - TCP_RCV (#406F): Obtiene datos entrantes de una conexión TCP. - TCP_STATUS (#4072): Obtiene el estado de una conexión TCP. - TCP_FLUSH (#4075): Limpia el búfer de salida de una conexión TCP. * Envío y captura de datagramas en bruto - RAW_SEND (#4078): Envía un datagrama en bruto. - RAW_CONTROL (#407B): Controla la captura de un datagrama en bruto. - RAW_RCV (#407E): Recupera el datagrama en bruto capturado. * Varias - WAIT_INT (#4081): Espera a que se produzca una interrupción del reloj. - NETWORK_STATE (#4084): Obtiene el estado de la red. Todas las rutinas excepto las de los grupos "Acceso al puerto RS232" y "Acceso al modem y conexión PPP" son compatibles con todas las implementaciones de INL. A continuación se detalla el funcionamiento de todas estas rutinas. * LDIRP3 a PUTSLOT1: Ver la descripción de las rutinas homónimas en la sección dedicada a la zona de salto en página 3 * GETSLOT2 (#4018): Obtiene el slot conectado en la página 2 Entrada: - Salida: A = Slot conectado actualmente en la página 2, en el formato estandar %E000SSPP (Extendido, Secundario, Primario) * PUTSLOT2 (#401B): Conecta un determinado slot en la página 2 Entrada: A = Slot a conectar B = Slot para comparar Salida: - El slot especificado en A será conectado en la página 2 sólo si es distinto del valor especificado en B. * RS_IN (#401E): Obtención de un byte entrante desde el RS232 Entrada: - Salida: A = Byte más antiguo recibido por el puerto RS232 Antes de ejecutar esta rutina es necesario ejecutar RS_IN_STAT para comprobar si realmente hay datos entrantes disponibles. * RS_OUT (#4021): Envío de un byte al puerto RS232 Entrada: A = Byte para enviar Salida: - * RS_IN_STAT (#4024): Comprueba si hay bytes entrantes disponibles desde el puerto RS232 Entrada: - Salida: A = #FF si hay bytes disponibles, 0 en caso contrario Es necesario usar esta rutina antes de llamar a RS_IN. * RS_OUT_STAT (#4027): Comprueba si se pueden enviar bytes al puerto RS232 Entrada: - Salida: A = #FF si se pueden enviar bytes, 0 en caso contrario No es necesario usar esta rutina antes de llamar a RS_OUT. De hecho, el código de esta versión de INL no usa esta rutina. * VERS_PAUSE (#402A): Obtención el número de versión de INL, y consulta o modificación del estado de pausa/activación de INL Entrada: A = 0 para no modificar el estado de pausa/activación de INL 1 para pausar INL 2 para reactivar INL Salida: A = Estado de INL tras la ejecución de la rutina: 1 si INL está pausado 2 si INL está activo C = Número de versión principal de INL D = Número de versión secundario de INL E = Número de revisión de INL B = Implementación de INL 0: INL para driver Fossil y PPP Si simplemente se desea consultar la versión de INL, basta ejecutar esta rutina pasándole A=0 y consultar B, C, D y E a la salida. El valor devuelto en B indica la implementación de INL que está instalada; actualmente sólo existe una implementación, que usa el driver Fossil para acceder al medio físico y el protocolo PPP para el nivel de enlace. En el futuro podrían desarrollarse otras implementaciones, por ejemplo para el uso de tarjetas Ethernet, que devolverán un valor distinto en B. Es probable que estas otras implementaciones no tengan las rutinas y las variables relacionadas con el acceso al puerto RS232, la interacción con el modem y la gestión de la conexión PPP; por tanto, las aplicaciones que hagan uso de dichas rutinas y variables deberían ejecutar VERS_PAUSE y consultar el valor devuelto en B. Cuando INL está pausado, no se ejecuta su rutina de servicio a la interrupción del reloj, por lo que no recoge datos entrantes, actualiza temporizadores ni envía datos. El estado de pausa puede ser útil cuando se necesita realizar una operación muy larga (por ejemplo copiar muchos ficheros), ya que cuando INL está instalado y activo el funcionamiento global del ordenador se ralentiza. * GET_VAR (#402D): Lectura una variable del segmento de datos de INL Entrada: HL = Dirección de la variable a leer Salida: A = Contenido de la variable, como un dato de un byte DE = Contenido de la variable, como un dato de dos bytes (E=A) Las variables del segmento de datos de INL gobiernan ciertos parámetros del funcionamiento global de INL y ofrecen información sobre la conexión actual (como el estado de la conexión PPP y las direcciones IP en uso). Las variables útiles para el programador se listan en una sección posterior. Aunque INL siempre accede al segmento de datos a través de la página 2, no es necesario que la dirección pasada en HL esté en el rango #8000-#BFFF. Por ejemplo, da igual especificar #8034 que #0034, #4034 o #C034. * SET_VAR (#4030): Modificación una variable del segmento de datos de INL Entrada: HL = Dirección de la variable a modificar Cy = 0 y A = Byte a escribir; o bien, Cy = 1 y DE = Dato de dos bytes a escribir Salida: - Las variables del segmento de datos de INL gobiernan ciertos parámetros del funcionamiento global de INL y ofrecen información sobre la conexión actual (como el estado de la conexión PPP y las direcciones IP en uso). Las variables útiles para el programador se listan en una sección posterior; observa que algunas variables están pensadas para ser sólo leidas, y nunca deben modificarse mediante programa. Al igual que ocurre con GET_VAR, no es obligatorio que la dirección pasada en HL esté en el rango #8000-#BFFF. * COPY_DATA (#4033): Copia un bloque de datos del segmento de datos de INL a TPA o viceversa Entrada: HL = Dirección de origen DE = Dirección de destino BC = Longitud Cy = Dirección de la copia: 0: Del segmento de datos de INL a TPA 1: De TPA al segmento de datos de INL Salida: - Esta rutina es usada principalmente por el código de INL cuando ejecuta rutinas que implican traspaso de datos entre el usuario e INL, pero también puede ser útil para el programador, por ejemplo para obtener/establecer direcciones IP o para leer con una sola llamada todas las variables de configuración de INL. Al igual que ocurre con SET_VAR y GET_VAR, la dirección referente al segmento de datos de INL (HL si Cy=0, DE si Cy=1) no tiene por qué estar en el rango #8000-#BFFF. * IP_STRING (#4036): Generación una cadena que representa una dirección IP dada Entrada: HL, DE = Dirección IP, con el formato L.H.E.D IX = Dirección en TPA donde dejar la cadena generada (máximo 16 bytes) A = Carácter de terminación a usar en la cadena generada Salida: - Esta función resultará de utilidad para aquellas aplicaciones que muestren por pantalla información sobre direcciones IP. Por ejemplo, si se le pasa HL=#0304, DE=#0A0B y A="$", generará la cadena "4.3.11.10$". El búfer en TPA para la cadena generada debería tener una longitud de 16 bytes, para permitir alojar la cadena generada más larga posible (del tipo "xxx.xxx.xxx.xxx$"). Para realizar la operación inversa (obtener una dirección IP a partir de la cadena que la representa) se pueden usar las funciones DNS_Q y DNS_S. * CALC_MD5 (#4039): Cálculo del hash MD5 de un bloque de datos Entrada: HL = Dirección del bloque en TPA DE = Dirección para dejar el hash resultante en TPA (16 bytes) BC = Longitud del bloque (0 a 1024 bytes) Salida: Cy = 1 en caso de error (se pasó BC>512) Esta rutina calcula el código hash del bloque de datos pasado usando el algoritmo MD5 especificado en RFC1321. El hash resultante es una "firma" de 16 bytes generada a partir de los datos del bloque; teóricamente es única para cada bloque e irreversible (es decir, no es posible recuperar el bloque de datos original a partir del hash). Esta rutina es usada por INL para la autentificación por medio del protocolo CHAP durante la apertura de una conexión PPP. Puede ser de utilidad para el programador a la hora de implementar protocolos de alto nivel sobre TCP que hagan uso de mecanismos de autentificación basados en MD5 (por ejemplo, un cliente de SMTP). * CALC_CHKSUM (#403C): Cálculo del checksum estandar de un cierto bloque de datos Entrada: HL = Dirección del bloque en TPA BC = Longitud del bloque (1 a 1024 bytes) Salida: Cy = 1 en caso de error (se pasó BC>1024 o BC=0) DE = Checksum resultante Esta rutina calcula el checksum estándar (es decir, el basado en complementos a uno que se usa en las cabeceras IP, TCP y UDP) del bloque pasado. El checksum es una suma de comprobación generada a partir de los datos del bloque, y se usa normalmente para comprobar la integridad de los paquetes recibidos desde Internet. El siguiente es un procedimiento sencillo para comprobar el funcionamiento de esta rutina: 1) Generar un bloque de datos aleatorio. 2) Poner sus dos primeros bytes a 0. 3) Pasar el bloque a CALC_CHKSUM. 4) Escribir el checksum devuelto en los dos bytes que habíamos puesto a 0: LD (BLOQUE),DE 5) Volver a pasar el bloque a CALC_CHKSUM. El resultado devuelto por la última operación debe ser DE=0 (checksum correcto, indica que el bloque no ha sufrido modificaciones entre las dos llamadas a CALC_CHKSUM aparte del propio establecimiento del checksum). En un caso real, el bloque de datos sería un datagrama, y entre los pasos 4 y 5 habría una transferencia del mismo a través de Internet, con una posible corrupción de los datos; de ahí la utilidad del checksum. * B64_INIT (#403F): Inicia una operación de codificación o decodificación Base64 Entrada: A = Longitud de una línea codificada, o 0 para Infinito (usado sólo al codificar) Salida: Todos los registros preservados Esta rutina inicializa las variables internas de INL usadas para la codificación y decodificación en Base64, de forma que tras ejecutarla es posible comenzar la decodificación o decodificación de un bloque a/desde Base64 usando las rutinas B64_ENCODE y B64_DECODE. INL permite codificar y decodificar a y desde Base64 un bloque de datos de tamaño arbitrariamente grande, para lo cual se debe dividir el bloque en subbloques pequeños (de hasta 512 bytes) y codificar o decodificar cada uno de ellos mediante llamadas sucesivas a B64_ENCODE o B64_DECODE (que devolverán, a su vez, subbloques de la secuencia de salida). Para que esto sea posible, INL necesita almacenar internamente cierta información sobre el estado del proceso de codificación o decodificación; es decir, en cada llamada a B64_ENCODE o B64_DECODE se utiliza la información de estado guardada en la llamada anterior. Por este motivo, antes de iniciar la codificación o decodificación de un bloque nuevo es necesario llamar a B64_INIT una vez, para establecer las variables de estado en sus valores iniciales. Dicho de otro modo, el proceso para codificar o decodificar un bloque de datos de cualquier tamaño es el siguiente: 1) Dividir lógicamente el bloque a (de)codificar en subbloques de hasta 512 bytes. 2) Ejecutar B64_INIT una vez. 3) Ejecutar B64_ENCODE o B64_DECODE con Cy=0 para todos los subbloques excepto para el último (bloques intermedios). 4) Ejecutar B64_ENCODE o B64_DECODE con Cy=1 para el último subbloque (bloque final). Nótese que para bloques de tamaño total igual o inferior a 512 bytes se puede obviar el paso 3 (aunque también se podría, por ejemplo, dividir un bloque de 512 bytes en cuatro subbloques de 128 bytes; todo depende del tamaño de los búferes usados por cada aplicación en concreto). Además, no es necesario que todos los subbloques intermedios tengan el mismo tamaño. B64_ENCODE y B64_DECODE devuelven los punteros de entrada y salida de datos convenientemente actualizados según los tamaños de los bloques pasado y devuelto, respectivamente; por tanto, si los subbloques están almacenados consecutivamente en memoria resulta sencillo componer un bucle que los (de)codifique secuencialmente (en la descripción de la rutina B64_ENCODE hay ejemplos de código). La longitud de una línea codificada pasada en A indica cuántos carácteres Base64 generará la rutina B64_ENCODE antes de insertar un fin de línea (secuencia CR-LF) en la secuencia de salida. Esto resulta útil, por ejemplo, si se deben enviar datos mediante el protocolo SMTP, que impone un límite en la longitud de las líneas enviadas; en ese caso se suele usar el valor 76. Este parámetro no es usado por B64_DECODE. El valor pasado como longitud de una línea codificada debe ser un múltiplo de cuatro, de lo contrario se redondeará al múltiplo de cuatro más cercano por defecto (excepto los valores uno a tres, que se redondearán a cuatro). Un valor cero significa "Infinito", es decir, no se insertarán secuencias de fin de línea en la secuencia codificada de salida. Nota: no se deben mezclar llamadas a B64_ENCODE y llamadas a B64_DECODE sin antes ejecutar B64_INIT de nuevo, ya que ambas rutinas utilizan la misma zona de memoria para almacenar el estado del proceso de codificación o decodificación. Nota: si se va a codificar o decodificar un bloque muy grande, y se están enviando o recibiendo datos UDP o TCP, puede ser conveniente insertar llamadas a WAIT_INT entre las llamadas a B64_ENCODE o B64_DECODE (ver sección 3.2, "Consideraciones de programación"). La ejecución del código principal de INL durante una interrupción del reloj no afecta a las variables usadas para guardar el estado del proceso de codificación o decodificación. * B64_ENCODE (#4042): Codifica un subbloque de datos en Base64 Entrada: IX = Dirección de origen del subbloque a codificar en TPA IY = Dirección de destino para la secuencia codificada en TPA BC = Tamaño del subbloque a codificar (0 a 512) Cy = 0 si es un bloque intermedio 1 si es un bloque final Salida: En caso de error, Cy = 1 y A = Código de error: 1: Tamaño del bloque de entrada incorrecto (mayor que 512) Si no hay error, Cy = 0, A = 0 y el resto de registros como sigue BC = Tamaño de la secuencia Base64 generada IX = IX + BC a la entrada IY = IY + BC a la salida HL:DE = Tamaño acumulado de las secuencias Base64 generadas Esta rutina codifica un bloque de datos de hasta 512 bytes mediante la codificación Base64. Como se ha explicado en la descripción de la rutina B64_INIT, es posible codificar bloques de datos de cualquier tamaño dividiéndolos en subbloques de hasta 512 bytes y ejecutando sucesivamente esta rutina para cada uno de los subbloques, pasando Cy=0 para todos ellos excepto para el último. Antes de iniciar el proceso de codificación es necesario ejecutar B64_INIT una vez. Si se pasó un valor distinto de cero como longitud de línea codificada al ejecutar B64_INIT, la rutina insertará un fin de línea (CR, ASCII 13 seguido de LF, ASCII 10) en la secuencia de salida cada vez que haya sido generado el número de carácteres codificados especificado. No se inserta salto de línea al codificar el bloque final, a no ser que el número total de carácteres codificados sea un múltiplo exacto de la longitud de línea codificada. Si termina sin errores, esta rutina devuelve los punteros pasados en IX e IY incrementados según el tamaño del bloque de entrada y del bloque de salida, respectivamente. Por tanto, el proceso de codificación resulta sencillo si los subbloques se encuentran almacenados consecutivamente en memoria. Por ejemplo, para codificar un bloque de 1280 bytes (512+512+256) que se encuentra en la posición de memoria #4000 y depositar el resultado en #8000 (ambas direcciones referentes a los segmentos de TPA) se puede hacer lo siguiente (suponiendo que el segmento de código de INL se encuentra conectado en la página 1): xor a call B64_INIT ;Necesario siempre antes de empezar ld ix,#4000 ;Direcciones iniciales ld iy,#8000 ;de entrada y salida ld bc,512 ;Primer bloque intermedio or a call B64_ENCODE ld bc,512 ;Segundo bloque intermedio or a call B64_ENCODE ld bc,256 ;Bloque final scf call B64_ENCODE La codificación Base64 genera cuatro carácteres codificados por cada tres carácteres a codificar, por tanto la secuencia generada siempre será más larga que el bloque de datos pasado. Concretamente, el tamaño de la secuencia generada variará, según la longitud de línea codificada pasado a B64_INIT, entre 4/3 del bloque pasado (para una longitud de línea infinita) y el doble del bloque pasado (para una longitud de línea igual a cuatro). Por tanto, el tamaño máximo absoluto de la secuencia generada por esta rutina es de 1024 bytes. El valor devuelto en HL:DE representa el tamaño de secuencia codificada acumulado, es decir, la suma de los tamaños de todas las secuencias Base64 generadas mediante ejecuciones de B64_ENCODE desde que se ejecutó B64_INIT. O en otras palabras, tras codificar el bloque final es el tamaño de la codificación completa Base64 del bloque completo codificado. A continuación se muestra una rutina genérica capaz de codificar un bloque de datos de "cualquier" tamaño almacenado en la dirección #4000 de TPA; la secuencia codificada se devuelve en #8000. El tamaño máximo admisible para la secuencia codificada por esta rutina es de 16K (toda la página 2 de TPA), por tanto, estrictamente hablando el tamaño máximo del bloque a codificar es de 8K. Límites más realistas para este tamaño máximo son 12288 bytes (si se usa una longitud de línea codificada igual a infinito) o 11972 bytes (si se usa una longitud de línea codificada igual a 76). Se supone que el segmento de código de INL está conectado en la página 1. ;Rutina para codificar un bloque de datos en Base64 ;Entrada: Bloque en #4000 ; A=Longitud de una línea codificada ; BC=Tamaño del bloque ; (hasta 8192 o 12288 bytes según valor pasado en A) ;Salida: Secuencia Base64 en #8000 ; DE=Longitud secuencia Base64 B64_ENC: call B64_INIT ;No modifica BC ld ix,#4000 ld iy,#8000 B64_ENC_LOOP: ld a,b ;Quedan menos de 512 bytes? cp 2 jr c,B64_ENC_FIN push bc ;Codificacion de un bloque intermedio ld bc,512 or a call B64_ENCODE pop bc ;Resta 512 a la longitud pendiente dec b ;y repite el bucle dec b jr B64_ENC_LOOP B64_ENC_FIN: scf ;Codificacion del ultimo bloque call B64_ENCODE ;Devuelve long. total codificada en DE ret Esta rutina de ejemplo funcionará incluso aunque el tamaño pasado en BC sea un múltiplo exacto de 512, ya que B64_ENCODE funciona correctamente incluso aunque se le pase un tamaño de bloque a codificar igual a cero (en ese caso, si se trata de un subbloque final es posible que aún se devuelvan datos codificados, dependiendo del estado del proceso almacenado). IMPORTANTE: Téngase en cuenta las restricciones de uso de TPA con las rutinas de INL que se detallan en la sección 3.2, "Consideraciones de programación". En concreto, es importante recordar que un bloque de datos pasado a INL o devuelto por INL no debe cruzar los límites entre dos páginas. * B64_DECODE (#4045): Decodifica una subsecuencia Base64 Entrada: IX = Dirección de origen de la susecuencia a decodificar en TPA IY = Dirección de destino para el bloque decodificado en TPA BC = Tamaño de la subsecuencia a decodificar (0 a 512) Cy = 0 si es un bloque intermedio 1 si es un bloque final Salida: En caso de error, Cy = 1 y A = Código de error: 1: Tamaño del bloque de entrada incorrecto (mayor que 512) 2: Tamaño total de la secuencia incorrecta (sólo puede ocurrir al decodificar bloques finales) 3: Carácter inválido encontrado en la secuencia Si no hay error, Cy = 0, A = 0 y el resto de registros como sigue BC = Tamaño del bloque de datos generado IX = IX + BC a la entrada IY = IY + BC a la salida HL:DE = Tamaño acumulado de los bloques de datos generadas Esta rutina es la complementaria de B64_ENCODE: Admite como entrada una secuencia Base64 y devuelve el bloque de datos resultante de su decodificación. Como en el caso de B64_DECODE, es posible decodificar secuencias Base64 de cualquier tamaño dividiéndolas en subsecuencias de hasta 512 bytes y ejecutando sucesivamente esta rutina para cada una de las subsecuencias, pasando Cy=0 para todas ellas excepto para la última. También, antes de iniciar el proceso de decodificación es necesario ejecutar B64_IN IT una vez. Las rutinas B64_ENCODE y B64_DECODE son esencialmente simétricas: ambas tienen los mismos parámetros de entrada y salida, y el código de ejemplo mostrado para la rutina B64_ENCODE se puede usar con B64_DECODE sin apenas modificaciones. Sólo hay que tener en cuenta que B64_DECODE puede devolver dos códigos de error adicionales, y que la relación entre el tamaño del bloque pasado y el tamaño del bloque devuelto cambia. Dado que el tamaño de una secuencia Base64 es siempre mayor que el correspondiente bloque codificado, por simetría el bloque de datos devuelto por esta rutina siempre será menor que la secuencia Base64 pasada. Concretamente, si la secuencia no tiene finales de línea el tamaño del bloque generado será 3/4 del tamaño de la secuencia; en caso contrario será aún menor. Por tanto, el tamaño máximo absoluto del bloque devuelto por esta rutina es de 384 bytes. Los carácteres de la secuencia pasada son tratados por esta rutina como sigue: 1) Los carácteres A-Z, a-z, 0-9, "/" y "+" son carácteres Base64 válidos y por tanto son procesados adecuadamente. 2) Los carácteres tabulador (ASCII 9), espacio (ASCII 32), retorno de carro (ASCII 13) y salto de línea (ASCII 10) son ignorados: no se procesan pero no provocan un error. 3) El carácter "=" (o la secuencia "==") es especial: si se encuentra, se considera que marca el final de la secuencia codificada. Si tras llegar al final de la secuencia de este modo aún quedan por procesar carácteres que no pertenecen al grupo mencionado en 2), se consideran carácteres erróneos y se devuelve el error 3. 4) Cualquier otro carácter encontrado es erróneo y provoca siempre el error 3. El error 2 se produce si el número total de carácteres codificados en la secuencia (es decir, el tamaño total de la secuencia descontanto los carácteres ignorables) no es un múltiplo exacto de cuatro. Este error sólo puede ser devuelto cuando se decodifica un bloque final. Si esta rutina devuelve el error 2 o el error 3, la secuencia es errónea y no es posible continuar con el proceso de decodificación. En cualquier caso, antes de procesar otra secuencia siempre es necesario ejecutar B64_INIT. * SEND_MODEM (#4048): Envío de un comando al modem y obtención de su respuesta Entrada: Cy = 0 para enviar el comando apuntado por HL Cy = 1 para enviar ATDT+el número almacenado en BUF_DIAL HL = Dirección del comando en TPA, acabado en 0 (si Cy=0) DE = Dirección para almacenar la respuesta en TPA (0 si no estamos interesados en la respuesta) A = Carácter de terminación para usar en la cadena de respuesta (si DE<>0) Salida: A = Código de terminación: 0 si no se cumple ninguna de las condiciones siguientes 1 si la respuesta comienza con OK 2 si la respuesta comienza con ERROR 4 si la respuesta comienza con la cadena almacenada en BUF_MODREP 8 si se pasó Cy=1 pero BUF_DIAL contiene una cadena vacía Esta rutina es la única disponible en INL para interactuar con el modem. Se usa típicamente en dos escenarios: 1) Conexión con el ISP previamente a la apertura de una conexión PPP. En este caso se debe establecer el número del ISP en el búfer BUF_DIAL del segmento de datos de INL (y si es necesario, establecer la respuesta positiva del modem en BUF_MODREP), y ejecutar esta rutina pasándole Cy=1. La rutina no vuelve hasta que recibe una respuesta por parte del modem. Si la rutina devuelve A=1 o A=4, la conexión ha tenido éxito y se puede proceder a abrir la conexión PPP; en caso contrario no ha podido establecerse la conexión. En concreto, si A=8 no se habrá enviado ningún comando al modem. 2) Envío de un comando de inicialización al modem, con anterioridad al establecimiento de la conexión. En este caso se debe ejecutar la rutina pasándole Cy=0 y HL apuntando al comando que queremos enviar. De nuevo, la rutina no volverá hasta que reciba una respuesta por parte del modem. Si la rutina devuelve A=1, se debe interpretar que el comando se ha ejecutado con éxito. En ambos casos, opcionalmente podemos pasar en DE la dirección de un búfer en TPA si queremos examinar la respuesta devuelta por el modem (si no nos basta saber que era "OK", "ERROR" o "CONNECT"). IMPORTANTE: Si no hay ningún modem conectado al puerto RS232, esta rutina colgará el ordenador. Nota: Esta rutina es capaz de detectar si el modem tiene activada la devolución de eco local. En ese caso, el propio comando será filtrado de la respuesta. * PPP_OPEN (#404B): Apertura de una conexión PPP Entrada: - Salida: - Esta rutina inicia la apertura de una conexión PPP, suponiendo que previamente se han establecido adecuadamente los parámetros necesarios en el segmento de datos de INL (PPP_USER, PPP_PASSWORD y si es necesario las direcciones IP). En el caso de conexiones vía ISP, antes de ejecutar esta rutina es necesario que el modem haya establecido una conexión física, para lo cual se usa la rutina SEND_MODEM. La rutina vuelve inmediatamente, y la conexión se establece como tarea de fondo, gobernada por la rutina de servicio a la interrupción del reloj. Se puede consultar el estado de la conexión en cualquier momento sin más que leer la variable PPP_STATE del segmento de datos de INL (la conexión quedará completamente establecida cuando esta variable alcance el valor 4). Si PPP_STATE vuelve a 0 (conexión cerrada), significa que ha habido un error que ha impedido el establecimiento de la conexión. El código de error se podrá consultar entonces en la variable PPP_CLCODE. IMPORTANTE: Nunca se debe llamar a esta rutina cuando la conexión ya está abierta o abriéndose (es decir, si PPP_STATE tiene cualquier valor distinto de cero). Cuando se usa INL.COM para abrir una conexión PPP, se muestra un mensaje de error si la conexión ya está abierta o abriéndose; pero esta comprobación la hace el propio INL.COM, no la rutina PPP_OPEN. * PPP_CLOSE (#404E): Cierre de una conexión PPP Entrada: - Salida: - Esta rutina cierra la conexión PPP, para lo cual simplemente envía al otro extremo un paquete LCP de terminación y pone PPP_STATE a cero (se comete aquí, en aras de la simplicidad, una violación deliberada de las especificaciones del protocolo PPP: en teoría se debería esperar a que el otro extemo enviara un paquete de confirmación del cierre). En conexiones vía ISP, no se envía ningún comando al modem pero éste se desconectará al cabo de pocos segundos, debido a que el ISP cortará el enlace físico al recibir el paquete de terminación. Su puede ejecutar esta rutina sin problemas sea cual sea el estado de la conexión PPP, incluso si ya está cerrada. * SEND_ECHO (#4051): Envío de un paquete de petición de eco ICMP (PING) Entrada: HL, DE = Dirección IP de la máquina de destino, con el formato L.H.E.D. A = TTL del datagrama IX = Identificador ICMP IY = Número de secuencia ICMP BC = Longitud de los datos, 0 a 548 (-1 para usar el tamaño por defecto) Salida: Cy = 1 en caso de error (no hay una conexión PPP abierta) Esta rutina envía un paquete de petición de eco ICMP (un PING) a la máquina especificada. Es posible elegir el tamaño de la zona de datos del mensaje ICMP, pero no su contenido; éste siempre consistirá en la secuencia de bytes 0 1 2 ... 253 254 255 0 1 2... convenientemente truncada para que se ajuste al tamaño especificado. Si se indica un tamaño de -1, se usará el valor almacenado en la variable PING_SIZE del segmento de datos de INL (tamaño por defecto de los PINGs salientes). El identificador y el número de secuencia pueden ayudar a emparejar respuestas con peticiones (una respuesta de eco siempre tendrá estos valores idénticos a su petición de eco asociada), pero pueden ignorarse si es necesario. El valor del campo TTL debe especificarse siempre; en general es recomendable usar el valor 255 para maximizar la probabilidad de que el paquete llegue a su destino. * RCV_ECHO (#4054): Obtención del paquete de respuesta de eco ICMP (PING) más antiguo recibido Entrada: - Salida: HL, DE = IP del originador del paquete, con el formato L.H.E.D. A = TTL del paquete IX = Identificador ICMP del paquete IY = Número de secuencia ICMP del paquete BC = Longitud de los datos del paquete Cy = 1 en caso de error (no hay paquetes disponibles) Esta rutina es la complementaria de SEND_ECHO: obtiene la información asociada a la cabecera del paquete de eco más antiguo recibido. No es posible recuperar la parte de datos de los paquetes de respuesta de eco recibidos; únicamente se recuperará la información de TTL, identificador ICMP, número de secuencia ICMP y longitud de la parte de datos. INL puede almacenar información de hasta ocho paquetes de eco recibidos; si llegan nuevos paquetes cuando ya hay ocho en cola, estos paquetes nuevos se perderán. Por tanto, un programa que envíe peticiones de eco debería recoger las respuestas en un tiempo razonable. Esta rutina siempre devuelve la información sobre el paquete más antiguo recibido (y aún no consumido) sin comprobar dicha información, por lo que es responsabilidad del programador comprobar que la dirección IP del originador es realmente la esperada. El siguiente código muestra una manera sencilla de hacer que INL limpie el búfer de paquetes de respuesta de eco recibidos; lo cual deberían hacer todas las aplicaciones que vayan a enviar y recibir PINGs, para asegurarse de que no obtienen paquetes antiguos olvidados por alguna otra aplicación ejecutada anteriormente: CLEAR_ECHO: call RCV_ECHO jr nc,CLEAR_ECHO * UDP_SEND (#4057): Envío de un paquete UDP Entrada: HL, DE = Dirección IP de la máquina de destino, con el formato L.H.E.D. IX = Puerto local (origen) IY = Puerto remoto (destino) BC = Dirección en TPA de los datos a enviar AF = Longitud de los datos (0 a 548 bytes) Salida: Cy = 1 en caso de error (no hay conexion PPP o se ha especificado BC>548) Esta rutina envía el bloque de datos especificado en BC y AF como un paquete UDP a la máquina con la IP especificada en HL y DE, usando los puertos especificados en IX e IY. El programador es libre de usar cualquier puerto local excepto el #FFFE, ya que este puerto está reservado para el resolver. La forma más sencilla de establecer el par AF con la longitud de los datos es usar la pila: LD BC,longitud PUSH BC POP AF LD BC,direccion ... * UDP_RCV (#405A): Obtención del paquete UDP más antiguo recibido Entrada: HL = Dirección TPA donde dejar la parte de datos del paquete (0 si no estamos interesados en la parte de datos) Salida: HL, DE = IP del originador del paquete, con el formato L.H.E.D. IX = Puerto remoto (origen) IY = Puerto local (destino) BC = Longitud de los datos Cy = 1 en caso de error (no hay paquetes disponibles) Esta rutina es la complementaria de SEND_UDP: obtiene la información de cabecera y la parte de datos del paquete de UDP más antiguo recibido. Si se le pasa HL=0, sólo se recuperará la información de cabecera. El búfer para los datos en TPA debería tener una longitud de al menos 548 bytes; éste es el tamaño máximo que tendrá la parte de datos de un paquete UDP recibido. INL puede almacenar información de hasta ocho paquetes UDP recibidos; si llegan nuevos paquetes cuando ya hay ocho en cola, estos paquetes nuevos se perderán. Por tanto, un programa que envíe paquetes UDP y espere respuestas debería recoger éstas en un tiempo razonable. Esta rutina siempre devuelve el paquete más antiguo recibido (y aún no consumido) sin comprobar sus datos de cabecera, por lo que es responsabilidad del programador comprobar que la dirección IP del originador y los puertos son realmente los esperados. El siguiente código muestra una manera sencilla hacer que INL limpie el búfer de paquetes UDP recibidos; lo cual deberían hacer todas las aplicaciones que vayan a enviar y recibir paquetes UDP, para asegurarse de que no obtienen paquetes antiguos olvidados por alguna otra aplicación ejecutada anteriormente: CLEAR_UDP: ld hl,0 call UDP_RCV jr nc,CLEAR_UDP * DNS_Q (#405D): Petición de resolución de nombre de host Entrada: HL = Puntero en TPA al nombre a resolver, acabado en 0 (longitud máxima 255 carácteres) A = Banderas, cuando están establecidas indican: bit 0: Sólo interrumpir la consulta en curso si la hay (ignora el resto de banderas y registros) bit 1: NO consultar los servidores DNS (intentar interpretar el nombre como una dirección IP y devolver un error si no lo es) bit 2: Si ya hay una consulta en curso, NO interrumpirla y devolver un error Salida: En caso de error, Cy = 1 y A = código de error: 1: No hay una conexión PPP establecida 2: Ya hay una consulta en curso (sólo si se ha pasado A:1 establecido) 3: El nombre pasado no es una dirección IP válida (sólo si se ha pasado A:2 establecido) 4: No hay servidores DNS disponibles Si no hay error, Cy = 0 y A = código de resultado: 0: La consulta está en curso (o ha sido cancelada, si se ha pasado A:0 establecido) 1: El nombre era una representación de una dirección IP HL, DE: Direccion IP resultante con el formato L.H.E.D (sólo si se devuelve Cy=0 y A=1) Esta rutina junto con su complementaria DNS_S, explicada más adelante, permite obtener la dirección IP asociada a un determinado nombre de host (por ejemplo "smtp.mailserver.com"), para lo cual se consulta a los servidores DNS cuyas direcciones IP han sido previamente configuradas (o bien obtenidas durante el establecimiento de la conexión PPP). Como caso particular de nombre de host también se aceptan cadenas que representen directamente direcciones IP (por ejemplo "120.200.0.34"), lo cual facilita el desarrollo de programas que admitan tanto nombres de host como direcciones IP como parámetro de conexión (por ejemplo el cliente de PING suministrado, cuya sintaxis de ejecución es: PING |). Tras ser llamada, la rutina termina su ejecución inmediatamente, y el proceso de resolución del nombre continúa como tarea de fondo gobernada por la rutina de servicio de interrupción al reloj (a no ser que se haya pasado A:0 establecido; en ese caso, lo único que se hace es cancelar la consulta actualmente en curso si es que la hay). Es necesario entonces ejecutar la rutina DNS_S, explicada más adelante, para saber si el proceso ha finalizado (en cuyo caso se obtendrá la dirección IP resultante o un código de error si la consulta ha fallado) o por el contrario aún continúa. Si ya hay una consulta en curso cuando DNS_Q es ejecutada, normalmente dicha consulta será cancelada para poder iniciar la consulta nueva (el resolver de INL sólo puede ejecutar una consulta simultáneamente). Sin embargo, si se pasa A:2 establecido al ejecutar DNS_Q y ya hay una consulta en marcha, ésta no será cancelada y DNS_Q devolverá un error. DNS_Q intentará en primer lugar interpretar la cadena pasada como si fuera la representación directa de una dirección IP. Si tiene éxito, devolverá el resultado inmediatamente sin que sea necesario realizar ninguna consulta a los servidores DNS (aunque el resultado será almacenado de forma que posteriores ejecuciones a DNS_S también lo devolverán); en caso contrario, se procederá a iniciar el proceso de consulta a los servidores DNS. Sin embargo, si se pasa A:1 establecido y la cadena pasada no representa una dirección IP, no se iniciará la consulta y DNS_Q devolverá un error. A la hora de consultar a los servidores DNS, se consultará primero al servidor primario; si éste no está disponible o no responde, se intentará con el secundario (DNS_Q devolverá un error si ninguno de los dos está disponible, es decir, si la dirección IP asociada es 0.0.0.0 para ambos). El resolver de INL es recursivo, es decir, si en vez de una respuesta a la consulta se obtiene la dirección IP de otro servidor DNS, el proceso de consulta volverá a comenzar con el nuevo servidor, y así sucesivamente hasta obtener una respuesta o hasta que uno de los servidores de la cadena no responda (Nota: INL decide que un servidor no responde después de enviarle cinco veces el paquete de petición de resolución del nombre; los envíos están espaciados tres segundos). INL cancelará el proceso si éste dura más de un minuto en total. A continuación se muestra un ejemplo de código para la resolución de un nombre de host. Se supone que el segmento de código de INL ya está conectado en la página 1. ;* Realiza la consulta ld hl,HOST_NAME xor a call DNS_Q jp c,ERROR_Q ;* Espera a que se devuelva el resultado o un error WAIT: call WAIT_INT ;Ver la sección 3.2 "Consideraciones de programación" xor a call DNS_S cp 1 ;Proceso aún en curso jr z,WAIT cp 3 ;Error jp z,ERROR_S ;Llegados aquí, A sólo puede ser igual a 2 ld (IP_ADD),hl ;Almacena el resultado ld (IP_ADD+2),de ... HOST_NAME: db "nombre.host.com",0 IP_ADD: ds 4 Observa que en ningún momento comprobamos si HOST_NAME contiene un nombre o la representación de una dirección IP; podemos obtener esta información a partir del resultado devuelto por DNS_Q en A o el resultado devuelto por DNS_S en B, pero normalmente no estaremos interesados en ella. * DNS_S (#4060): Obtención del estado de la petición de resolución de nombre de host Entrada: A = Banderas, cuando están establecidas indican: bit 0: Limpiar cualquier resultado o error existente después de la ejecución (excepto si hay una consulta en curso) Salida: A = Código de estado primario y B = Código de estado secundario: A = 0: No hay ninguna consulta en curso, ni ningún resultado ni error disponible A = 1: Hay una consulta en curso B=1: Consultando al servidor DNS primario B=2: Consultando al servidor DNS secundario B=3: Consultando a otro servidor DNS al que se nos ha redirigido A = 2: Consulta completada, se devuelve el resultado en HL, DE en formato L.H.E.D B=0: El nombre no era una dirección IP B=1: El nombre era una dirección IP A = 3: Error: B=1-15: Error devuelto por el servidor DNS: 1: Formato del paquete de petición inválido 2: Fallo del servidor DNS 3: El nombre de host especificado no existe 4: El servidor DNS no soporta este tipo de consulta 5: Petición rechazada 6-15: Códigos no definidos actualmente B=16: Alguno de los servidores DNS no ha respondido B=17: Temporizador total de un minuto expirado B=18: Consulta cancelada por el usuario (se ejecutó DNS_Q con A:0 establecido) B=19: Se ha perdido la conexión PPP durante el proceso B=20: La respuesta no contenía REPLY ni AUTHORITATIVE B=21: La respuesta está truncada HL, DE: Direccion IP resultante con el formato L.H.E.D (sólo si se devuelve A=2) Esta es la rutina complementaria de DNS_Q: permite obtener el estado actual del proceso de resolución de nombre y, en caso de que éste haya terminado, devuelve el resultado del mismo (la dirección IP buscada o bien un código de error). La rutina devuelve un código de estado primario en A, y un código secundario en B con información más detallada; normalmente sólo estaremos interesados en el código primario (y en la dirección IP resultante cuando el proceso haya terminado con éxito), salvo en caso de error. Si se pasa A:0 establecido, DNS_S devolverá igualmente el estado actual y el resultado de la consulta si lo hay, pero limpiará dicho resultado de forma que posteriores llamadas a DNS_S devolverán A=0 (excepto si hay una consulta en curso; en ese caso se ignora A:0). Los errores en el rango 1 a 15 son devueltos por el servidor DNS y están definidos en la especificación del protocolo usado (RFC1035), mientras que los errores a partir del 16 son generados por INL. La recepción de un error con código 1 o 4 implica que el paquete de petición era inválido, por tanto nunca deberían recibirse estos errores a no ser que haya fallos en el código de INL. Los errores 20 y 21 implican la imposibilidad de resolver el nombre, pero estos errores son muy raros y no deberían recibirse nunca. En la explicación del uso de DNS_Q se muestra un ejemplo de código que hace uso de DNS_Q y DNS_S para resolver un nombre de host. * TCP_OPEN (#4063): Apertura de una conexión TCP Entrada: A = 0 para conexión activa, #FF para conexión pasiva HL, DE = Dirección IP de la máquina de destino, con el formato L.H.E.D. (0.0.0.0 para socket remoto sin especificar) IX = Puerto remoto (ignorado si la dirección IP es 0.0.0.0) IY = Puerto local (#FFFF para usar un número aleatorio entre 16384 y 32767) BC = Valor del temporizador de usuario 1 a 1080: Valor en segundos 0: Valor por defecto, 3 minutos #FFFF: Infinito Salida: En caso de error, Cy = 1 y A = código de error: 1: Hay demasiadas conexiones abiertas 2: No hay una conexión PPP establecida 3: Ya existe esa conexion 4: Se ha pedido conexión activa pero se ha especificado 0.0.0.0 como dirección IP 5: Valor del temporizador de usuario invalido Si no hay error, Cy = 0 y A = Identificador de la conexión Esta rutina abre una conexión TCP al socket remoto especificado en HL y DE (dirección IP) e IX (puerto), usando el puerto local especificado en IY. Se puede especificar #FFFF como puerto local, en cuyo caso INL escogerá un puerto aleatoriamente en el rango 16384 a 32767 usando el temporizador del sistema. Si no hay errores, la rutina devuelve un identificador de conexión (un número entre 0 y 3) que es necesario almacenar para poder interactuar con la conexión mediante las demás rutinas TCP_xxx. Si A=#FF, la conexión se abrirá en modo pasivo (entrará en el estado LISTEN), a la espera de que llegue una petición de conexión desde el host y puertos especificados en HL, DE e IX, contra el puerto local especificado en IY. En caso de conexión pasiva, se puede abrir la conexión en modo "socket remoto sin especificar", pasando 0.0.0.0 (HL=0, DE=0) como la dirección IP remota. En este modo, la conexión se establecerá cuando llegue una petición de conexión desde cualquier host y puerto remoto (se ignora el número de puerto pasado en IX); la única condición para aceptar la conexión es que el puerto requerido coincida con el puerto local que hemos especificado en IY. INL puede mantener abiertas un máximo de cuatro conexiones TCP simultáneamente. Si ya hay cuatro conexiones abiertas, esta rutina devolverá el error 1. Una conexión TCP se identifica unívocamente por el socket local (dirección IP y puerto locales) y el socket remoto (dirección IP y puerto remotos). Por tanto, no es posible abrir una conexión especificando la dirección IP remota y los dos puertos iguales a los de una conexión ya existente; si se intenta, la rutina devolverá un error 3. Si se permite a INL escoger un puerto local aleatoriamente (pasando IY=#FFFF), dicho error nunca se producirá. Las conexiones pasivas con socket remoto sin especificar son una excepción a lo anterior. Se puede abrir más de una conexión de dicho tipo aún usando el mismo puerto local. Aunque el estándar TCP permite transformar una conexión pasiva en activa volviéndola a abrir, INL no lo permite; si se intenta, se obtendrá el error 3. Para transformar una conexión pasiva en activa es necesario cerrarla y volver a abrirla. El "temporizador de usuario" es un temporizador que se activa cada vez que se envían datos nuevos a la conexión y se detiene cuando llega el reconocimiento (ACK) para dichos datos. Si dicho temporizador llega a cero, se considera que los datos no pueden llegar a su destino y se aborta la conexión. Este temporizador también se aplica al establecimiento de la conexión (es decir, es también el tiempo máximo que puede tardar en llegar el reconocimiento de un segmento SYN enviado). El valor pasado se interpreta como sigue: - 1 a 1080: Valor del temporizador en segundos (1 segundo a 18 minutos). - 0: Valor por defecto para el temporizador. En esta versión de INL el valor por defecto es de 3 minutos, pero podría cambiar en futuras versiones. - #FFFF: Infinito, los datos enviados se retransmiten indefinidamente hasta que llega el reconocimiento o hasta que se aborta la conexión o llega un segmento RST. Otros valores causan que la rutina devuelva el error 5. Nótese que para el valor "infinito" del temporizador de usuario, una llamada a TCP_CLOSE puede tardar un tiempo arbitrario en hacerse efectiva, ya que no se envía el segmento FIN mientras quedan datos por retransmitir. Sin embargo las llamadas a TCP_ABORT siempre se hacen efectivas inmediatamente. Nótese que no es posible enviar o recibir datos de la conexión inmediatamente después de abrirla con TCP_OPEN. Es necesario esperar a que la conexión progrese hasta el estado ESTABLISHED antes de poder iniciar el intercambio de datos (el estado de la conexión se puede obtener mediante la rutina TCP_STATUS). * TCP_CLOSE (#4066): Cierre de una conexión TCP Entrada: A = Identificador de la conexión Salida: En caso de error, Cy = 1 y A = código de error: 1: Identificador de conexión inválido 2: La conexión ya está cerrada Si no hay error, Cy = 0 y A = 0 Esta rutina cierra la conexión especificada. El error 2 es devuelto cuando la conexión está cerrada (estado CLOSED); si la conexión se está cerrando, la rutina no hace nada pero no devuelve error. Nótese que, en concordacia con la especificación del protocolo TCP, cerrar una conexión implica que no es posible enviar nuevos datos a la misma, pero el otro extremo puede seguir enviando datos (y nosotros podemos seguir recibiéndolos). En realidad esta rutina no cierra inmediatamente la conexión, sino que establece una bandera que indica que hay que cerrarla. Cuando no quedan datos pendientes de envío (ni nuevos ni a retransmitir), el cierre se hace efectivo y se envía el segmento FIN correspondiente. Se puede usar la rutina TCP_FLUSH para eliminar los datos pendientes de envío que aún no han sido enviados antes o después de cerrar la conexión (aunque dicha rutina no elimina los datos de la cola de retransmisión). La conexión queda definitivamente cerrada (estado CLOSED) cuando ambas partes han intercambiado un segmento FIN, tal como especifica el protocolo TCP. Si lo que se quiere es destruir inmediatamente la conexión y dejarla disponible para volverla a abrir, se debe usar TCP_ABORT en vez de TCP_CLOSE. * TCP_ABORT (#4069): Abortar una conexión TCP Entrada: A = Identificador de la conexión Salida: En caso de error, Cy = 1 y A = código de error: 1: Identificador de conexión inválido 2: La conexión ya está cerrada Si no hay error, Cy = 0 y A = 0 Esta rutina aborta la conexión especificada: envía un segmento RST si procede, y a continuación pone la conexión en estado CLOSED, lista para ser abierta de nuevo. Se devuelve un error 2 si la conexión ya está en estado CLOSED. * TCP_SEND (#406C): Envío de datos a una conexión TCP Entrada: A = Identificador de la conexión HL = Dirección en TPA de los datos a enviar BC = Longitud de los datos a enviar Cy = 1 para enviar datos como PUSH Salida: En caso de error, Cy = 1 y A = código de error: 1: Identificador de conexión inválido 2: La conexión está cerrada 3: La conexión está en un estado que no permite enviar datos (no es ESTABLISHED ni CLOSE-WAIT) 4: Espacio libre en el búfer de salida insuficiente para almacenar los datos Si no hay error, Cy = 0 y A = 0 Esta rutina encola los datos especificados en HL y BC para ser enviados por la conexión especificada en A. La conexión debe estar en estado ESTABLISHED o CLOSE-WAIT, de lo contrario se devolverá el error 3 (aunque el protocolo TCP permite que se encolen datos para envío mientras la conexión se está abriendo, INL no lo permite). En otras palabras: no es posible enviar datos mientras la conexión se está iniciando; y no es posible enviar datos cuando ya hemos cerrado la conexión (con TCP_CLOSE), pero sí cuando sólo el host remoto la ha cerrado. Cada conexión TCP tiene asignado un búfer para datos salientes en el segmento de datos de INL; esta rutina encola los datos especificados en dicho búfer, donde permanecen hasta que son enviados y se recibe un reconocimiento (ACK) para los mismos. El tamaño de este búfer está fijado en 1024 bytes, por tanto no se puede encolar un número arbitrario de datos, ya sea en una única llamada a TCP_SEND o en llamadas sucesivas. En concreto, esta rutina deolverá el error 4 si BC es mayor que el espacio libre en el búfer de salida. Para saber por adelantado cuánto espacio libre hay, se puede usar la rutina TCP_STATUS; dicho valor nunca será superior a 1024 bytes. Si se especifica Cy=1 (datos PUSH), los datos se envían en la siguiente interrupción del reloj (se envían los datos más antiguos encolados, no necesariamente los afectados por esta ejecución concreta de TCP_SEND). Encolar datos PUSH implica que todos los datos presentes en la cola de envío se marcan como PUSH, no sólo los afectados por esta llamada a TCP_SEND. En caso contrario (no hay datos PUSH), los datos se envían cuando ocurre uno de estos dos sucesos: - Se acumulan en la cola de datos a enviar suficientes bytes como para poder enviar un segmento de tamaño máximo (según lo especificado por el host en la opción MSS al iniciar la conexión, 512 si no ha recibido dicha opción). - Pasan 0.5 segundos desde que los datos más antiguos fueron encolados. Nótese que no obstante lo anterior, no se enviarán datos si el host remoto no está listo para recibirlos (si tiene su ventana de recepción a cero), tal como especifica el protocolo TCP. Por razones de simplicidad de código y aprovechamiento de memoria, INL sólo envía un segmento de datos a la vez. Es decir, una vez que se ha enviado un segmento de datos, no se envían más (excepto retransmisiones) hasta que llega un reconocimiento (ACK) para los datos enviados. Se puede ejecutar esta rutina con BC=0 y Cy=1. Esto causará que no se encolen datos nuevos pero se marquen todos los datos ya encolados como PUSH, por lo que serán enviados inmediatamente. Ejecutar la rutina con BC=0 y Cy=0 no tiene ningún efecto y no se devuelve ningún error (a no ser que se den las condiciones para los errores 1 a 3). INL no soporta datos urgentes. No es posible indicar que los datos deben ser enviados como urgentes. Los datos encolados pero no enviados aún se pueden desechar mediante una llamada a TCP_FLUSH. * TCP_RCV (#406F): Obtención de datos entrantes de una conexión TCP Entrada: A = Identificador de la conexión DE = Dirección en TPA para depositar los datos BC = Longitud del bloque de datos a obtener Salida: BC = Número de bytes que han podido ser obtenidos Z = 1 si BC = 0 En caso de error, Cy = 1 y A = código de error: 1: Identificador de conexión inválido 2: La conexión está cerrada Si no hay error, Cy = 0 y A = 0 Esta rutina extrae y copia en la dirección de TPA especificada por DE los datos recibidos por la conexión TCP especificada en A, hasta un máximo de BC bytes. Si no hay BC bytes disponibles, se extraen tantos bytes como sea posible (todos los disponibles); en cualquier caso se devuelve en BC el número de bytes realmente extraídos. Si no hay datos entrantes disponibles, se devuelve BC=0 y Z=1, pero esto no se considera un error. Tampoco se devuelve error si la conexión está en un estado en el que no puede haber datos entrantes (por ejemplo LISTEN); simplemente no se devuelven datos. Sólo se devuelve error si la conexión está en estado CLOSED. Cada conexión TCP dispone de un búfer fijo de 1024 bytes en el segmento de datos de INL para almacenar datos entrantes. Si llegan datos sin que la aplicación los recoja con TCP_RCV, dicho búfer acabará por llenarse y no se podrán recibir datos nuevos. No obstante esto no se considera un error, ya que esta situación está prevista y controlada por los mecanismos de control de flujo del protocolo TCP (INL anunciará una ventana de recepción cero en ese caso). Se puede saber por anticipado cuántos bytes se pueden obtener mediante una llamada a TCP_STATUS, aunque esto no es imprescindible como en el caso de TCP_SEND (como se ha visto, no es un error intentar extraer más datos de los disponibles). Por otro lado, se puede especificar un valor para BC mayor de 1024, pero eso no tiene mucho sentido porque nunca habrá más de 1024 bytes disponibles. INL no soporta datos urgentes, por tanto no hay forma de saber si los datos recibidos son normales o urgentes. Tampoco se puede saber si la bandera PUSH estaba establecida para los datos recibidos. Los valores de BC y Z a la salida siempre son válidos, aún en caso de error. * TCP_STATUS (#4072): Obtención del estado de una conexión TCP Entrada: A = Identificador de la conexión Salida: En caso de error, Cy = 1 y A = código de error: 1: Identificador de conexión inválido Si no hay error, Cy = 0 y el resto de parámetros como sigue Si la conexión está cerrada: A = 0 B = Causa del cierre de la conexión Si la conexión no está cerrada: A = Estado de la conexión HL = Número de bytes entrantes listos para ser recogidos DE = Espacio disponible en la cola de envío BC = Número de bytes en la cola de retransmisión IX = Dirección del TCB de la conexión Esta rutina devuelve el estado y diversa información sobre la conexión TCP especificada en A. Al contrario que las otras rutinas TCP_xxx, esta no devuelve error si la conexión está cerrada. El estado de la conexión se devuelve en A codificado de la siguiente manera: 0: CLOSED 1: LISTEN 2: SYN-SENT 3: SYN-RECEIVED 4: ESTABLISHED 5: FIN-WAIT-1 6: FIN-WAIT-2 7: CLOSE-WAIT 8: CLOSING 9: LAST-ACK 10: TIME-WAIT (no implementado) Nota: El estado TIME-WAIT no está implementado en esta versión de INL; las conexiones TCP pasan directamente a CLOSED cuando deberían pasar a TIME-WAIT. No obstante puede que dicho estado sea implementado en futuras versiones de INL. Si la conexión está cerrada (CLOSED), se devuelve la causa del cierre de la conexión en el registro B, codificada como sigue: 0: Esta conexión nunca ha sido usada desde que se instaló INL 1: Llamada al método TCP_CLOSE 2: Llamada al método TCP_ABORT 3: Segmento RST recibido (conexión rechazada o abortada por el host remoto) 4: Temporizador de usuario expirado 5: Temporizador de conexión inicial expirado 6: Conexión PPP perdida mientras la conexión TCP estaba abierta Si la conexión no está cerrada, otros registros devuelven información como sigue. HL devuelve el número de bytes entrantes que pueden ser recogidos mediante una llamada a TCP_RCV. Es un valor entre 0 y 1024. DE devuelve el número de bytes libres en la cola para datos a enviar; es decir, el máximo número de bytes que pueden encolarse mediante una llamada a TCP_SEND. Es un valor entre 0 y 1024. BC devuelve el número de bytes en la cola de retransmisión; es decir, el número de bytes que han sido enviados pero para los cuales aún no se ha recibido reconocimiento (ACK). Es un valor entre 0 y 536 (ya que INL sólo envía un segmento cada vez) y no incluye las banderas SYN y FIN. IX devuelve la dirección del TCB (Transmission Control Block) asociado a esta conexión. Se trata de un bloque de variables usado por INL para controlar el estado de la conexión y el envío y la recepción de datos; el TCB se describe en la sección 4. La dirección devuelta en IX es una dirección de página 2 y se refiere al segmento de datos de INL. * TCP_FLUSH (#4075): Limpieza del búfer de salida de una conexión TCP Entrada: A = Identificador de la conexión Salida: En caso de error, Cy = 1 y A = código de error: 1: Identificador de conexión inválido 2: La conexión está cerrada Si no hay error, Cy = 0 y A = 0 Esta rutina limpia el búfer de salida de una conexión TCP, es decir, elimina los datos encolados mediante TCP_SEND que aún no han sido enviados. Esta rutina no afecta a los datos de la cola de retransmisión, que seguirán siendo retransmitidos con normalidad hasta que se reciba un reconocimiento (ACK) para los mismos. * RAW_SEND (#4078): Envío de un datagrama en bruto Entrada: Cy=0 si hay que añadir una cabecera IP al bloque de datos a enviar Cy=1 si el bloque de datos a enviar es un datagrama completo (es decir, ya incluye la cabecera IP) IX = Dirección en TPA del bloque de datos a enviar BC = Longitud del bloque de datos a enviar (hasta 556 bytes si Cy=0, hasta 576 bytes si Cy=1) L.H.E.D = Dirección IP de destino (ignorado si si Cy=1) A = Protocolo de transporte (ignorado si Cy=1) IYh = ToS para el datagrama (ignorado si Cy=1) IYl = TTL para el datagrama (ignorado si Cy=1) Salida: En caso de error, Cy = 1 y A = código de error: 1: No hay conexión a Internet 2: Tamaño incorrecto (mayor de 556 ó 576) Si no hay error, Cy = 0 y A = 0 Esta rutina permite el envío de un datagrama en bruto, es decir, el envío de datos IP directos sin pasar por las capas TCP y UDP proporcionadas por INL. Se puede pasar un datagrama completo para su envío o bien dejar que INL componga la cabecera IP. Esta rutina está pensada principalmente para el envío de mensajes ICMP, pero puede usarse para enviar datagramas con cualquier otro protocolo de transporte. Si se especifica Cy=0, se añadirá al bloque de datos pasado una cabecera IP en base a los parámetros pasados en HL, DE, A e IY; el datagrama resultante será entonces enviado a la red. No es posible incluir opciones IP ni especificar el campo de identificador del datagrama si se opta por este método. Si se especifica Cy=1, en cambio, INL asumirá que el bloque de datos pasado es un datagrama IP completo (es decir, ya incluye una cabecera IP) y lo enviará a la red sin realizar ningún proceso adicional sobre el mismo. Este método permite que el datagrama contenga opciones IP, así como establecer el campo de identificador IP en cualquier valor deseado. Si se pasa el datagrama completo, es responsabilidad de la aplicación componer la cabecera IP correcta (según se especifica en RFC791), incluyendo el campo de checksum (se puede usar la función CALC_CHKSUM proporcionada por INL para calcularlo). Si la cabecera IP del datagrama pasado es incorrecta, el datagrama será enviado igualmente, pero es probable que no sea recibido o que sea descartado por el destinatario. Al pasar un datagrama completo, si el valor del campo que indica el tamaño del datagrama en la cabecera IP no coincide con el tamaño real del bloque de datos pasado, se usará el primero para decidir la cantidad de datos a enviar. Es decir, si BC es el parámetro de tamaño pasado a la rutina y L es el tamaño del datagrama según se especifica en la cabecera del propio datagrama, se cumple lo siguiente: - Si L=0, no se envía nada. - Si LBC, se envía la totalidad del bloque pasado, y a continuación se envían datos aleatorios (el contenido de la memoria en ese momento) hasta completar L bytes enviados. - Si L>576, se envían exactamente 576 bytes (parte de los cuales pueden ser aleatorios, por el punto anterior). Nota: Esta rutina es completamente independiente de RAW_CONTROL y RAW_RCV; se pueden enviar datagramas en bruto en cualquier momento, con independencia del estado de la captura de un datagrama en bruto. * RAW_CONTROL (#407B): Control de la captura de un datagrama en bruto Entrada: A = Acción requerida 0: Sólo obtener el estado de la captura 1: Requerir la captura de un datagrama 2: Cancelar la petición de captura de un datagrama B = Protocolo de transporte del datagrama a capturar (usado sólo si A=1) 0: Capturar el primer datagrama recibido #FF: Capturar el primer datagrama recibido que no sea TCP, UDP o petición/respuesta de eco ICMP Otro: Capturar el primer datagrama recibido que transporte el protocolo especificado Salida: En caso de error, Cy = 1 y A = código de error: 1: Se ha requerido una captura pero no hay conexión a Internet 2: Acción requerida inválida Si no hay error, Cy = 0 y otros registros como sigue A = Estado de la captura: 0: No se ha requerido la captura de un datagrama, o ya se ha recuperado el datagrama capturado 1: Se ha pedido la captura de un datagrama pero aún no ha llegado ninguno del protocolo adecuado 2: Hay un datagrama capturado, que se puede recuperar llamando a RAW_RCV BC = Tamaño del datagrama capturado (sólo si se devuelve A=2), 20 a 576 bytes D = Protocolo de transporte requerido (sólo si se devuelve A=1 ó 2) Esta rutina controla el proceso de captura de un datagrama en bruto. El mecanismo de captura de datagramas está pensado principalmente para la captura de mensajes ICMP, pero puede usarse para capturar datagramas de cualquier otro protocolo de transporte. El procedimiento completo de captura es como sigue: 1) Se llama a RAW_CONTROL pasando A=1 y el código del protocolo de transporte deseado en B. 2) Se llama repetidamente a RAW_CONTROL pasando A=0 hasta que devuelva A=2 (o A=0, lo cual indicaría que se ha perdido la conexión a Internet). 3) Al obtener A=2, se recupera el datagrama capturado llamando a RAW_RCV. Si se ejecuta esta rutina con A=2, la petición de captura se cancela, y el datagrama capturado (si lo hay) es descartado. Cuando se pide la captura de un datagrama en bruto, INL comprueba el código de protocolo de transporte de todos los datagramas recibidos. El primer datagrama recibido cuyo protocolo de transporte coincida con el que se especificó al pedir la captura (y cuyo checksum IP sea correcto) es almacenado, y puede recuperarse mediante una llamada a RAW_RCV. Sólo se puede capturar un datagrama a la vez. El datagrama capturado no sufre ningún otro proceso por parte de INL (por ejemplo, si es un paquete UDP no se encola en la cola de paquetes UDP recibidos). Hay dos valores especiales que se pueden especificar como código de protocolo de transporte deseado cuando se realiza la petición de captura. El valor 0 indica que se debe capturar el primer datagrama que llegue, sea cual sea el protocolo que transporta. El valor #FF indica que se debe capturar el primer datagrama que llegue y que no sea TCP, UDP o una petición o respuesta de eco ICMP (es decir, se pasan por alto todos los datagramas que normalmente son procesados por INL). Nótese que el valor 1 causará que se capture el primer mensaje ICMP recibido, incluso si es una petición o respuesta de eco. La captura de datagramas no es persistente: una vez se ha capturado un datagrama, no se capturan más a no ser que vuelva a ejecutarse RAW_CONTROL con A=1 (se recupere o no el datagrama con RAW_RCV). Si se pierde la conexión a internet, el proceso de captura pasará a estado 0 automáticamente; el datagrama capturado y no recogido aún, si lo hay, se perderá. Realizar una petición de captura implica la cancelación automática de la petición anterior, si la hay; si hay un datagrama capturado, se perderá. Nota: Para almacenar un datagrama se usa el búfer correspondiente al octavo paquete UDP recibido; por tanto, cuando se pide la captura de un datagram sólo se pueden recibir siete paquetes UDP. Si hay ocho paquetes UDP almacenados cuando se pide la captura, el octavo se pierde. * RAW_RCV (#407E): Recuperación del datagrama en bruto capturado Entrada: HL = Dirección de destino en TPA para copiar el datagrama capturado Salida: En caso de error, Cy = 1 y A = código de error: 1: No se ha capturado ningún datagrama Si no hay error, Cy = 0 y otros registros como sigue A = Longitud de la cabecera IP del datagrama BC = Longitud del datagrama DE = Longitud de la parte de datos del datagrama (es decir, BC-A) HL = Puntero al principio de la parte de datos del datagrama (es decir, HL a la entrada - A) Esta rutina copia en TPA el datagrama capturado tras una petición de captura efectuada con RAW_CONTROL. Para saber por adelantado si hay un datagrama capturado, así como su tamaño, se puede llamar a RAW_CONTROL con A=0 (ver la descripción de la rutina RAW_CONTROL). El datagrama completo capturado es copiado en la dirección especificada en HL, y los registros A, BC, DE y HL devuelven diversa información sobre el mismo. Para un datagrama que no contiene opciones IP, se devuelve A=20 y DE=BC-20. En cualquier caso, HL apuntará al principio de la zona de datos del datagrama (es decir, apuntará al primer byte posterior a la cabecera IP). Tras una ejecución de RAW_RCV que no devuelve error, el estado de la captura de datagramas pasará a ser 0 de nuevo (no hay captura pendiente). Para capturar otro datagrama es necesario efectuar una nueva petición con RAW_CONTROL. * WAIT_INT (#4081): Espera a que se produzca una interrupción del reloj Entrada: - Salida: Cy = 1 si ha sido necesario esperar Cy = 0 si no ha sido necesario esperar Esta rutina compara el valor del temporizador del sistema (variable situada en #FC9E) con el valor que tenía dicha variable en la anterior ejecución de la misma rutina. Si dichos valores difieren, la rutina termina inmediatamente con Cy=0. Si son iguales, la rutina espera a que el valor del temporizador cambie, y entonces termina con Cy=1. En otras palabras: entre dos llamadas de WAIT_INT siempre se produce al menos una interrupción del reloj, y por tanto el código principal de INL se ejecuta al menos una vez. El uso de la rutina WAIT_INT o de alguna otra rutina similar se hace necesario cuando la aplicación entra en un bucle en el que se ejecutan continuamente otras rutinas de INL en rápida sucesión. Para más detalles, ver la sección 3.2.3, "Interrupciones y tiempos de espera". * NETWORK_STATE (#4084): Obtención el estado de la red Entrada: - Salida: A = Estado de la red: 0: No disponible 1: Inicializándose 2: Disponible (se pueden transportar datagramas IP) 3: Cerrándose Esta rutina devuelve el estado actual de la red en base a una abstracción a cuatro estados que es siempre la misma independientemente de la implementación de INL que esté instalada. Las aplicaciones deberían usar esta rutina en vez de leer directamente la variable PPP_STATE (que podría no estar disponible en otras implementaciones de INL que se desarrollen en el futuro) para saber si es posible enviar y recibir datagramas IP (y por tanto, paquetes UDP y TCP). La red sólo puede transportar datagramas cuando esta rutina devuelve A=2 (red disponible). En el caso concreto de INL para driver Fossil y PPP, esta rutina devuelve 0 cuando PPP_STATE tiene un valor 0, devuelve 2 cuando PPP_STATE tiene un valor 4, y devuelve 1 en los demás casos; nunca devuelve 3. 3.1.4. EL SEGMENTO DE DATOS Otra parte importante de INL es el segmento de datos. Se trata de otro segmento de RAM del mapeador primario, que esta vez contiene diversas variables y búferes de datos usados por el código de INL durante su funcionamiento. Concretamente, este segmento consta de las siguientes partes: - Variables de configuración y estado (unos 128 bytes). - Variables y temporizadores de uso interno (unos 64 bytes). - Un búfer de 256 bytes para almacenar el nombre de host que procesa el resolver. - Ocho búferes de 11 bytes para almacenar información sobre las respuestas de eco ICMP recibidas. - Ocho búferes de 10 bytes para almacenar información sobre los paquetes UDP recibidos. - Ocho búferes de 556 bytes para alojar los paquetes UDP recibidos. - Un búfer de 582 bytes para alojar temporalmente el último paquete recibido (576 bytes para un datagrama de tamaño máximo, más cuatro bytes para la cabecera PPP, más dos bytes para el FCS final). - Un búfer de 582 bytes para alojar temporalmente un paquete saliente. Este búfer y el anterior también son usados por las rutinas de INL que realizan algún proceso sobre datos del usuario alojados en TPA (por ejemplo el calculador de hash MD5, o la rutina de envío de datos TCP). - Cuatro búferes de 256 bytes para almacenar el TCB de las conexiones TCP, más otro búfer temporal del mismo tamaño para almacenar el TCB que está siendo usado (aunque el tamaño real de un TCB es inferior a 256 bytes). - Ocho búferes circulares de 1024 bytes, dos para cada conexión TCP; uno es para datos entrantes y otro para datos salientes y retransmisiones. El resto del espacio del segmento (unos 600 bytes) está reservado para uso futuro. En esta sección se describirá en detalle la parte de variables de configuración y estado, que es la que puede resultar de interés para el programador. La mayoría de las variables de esta zona tienen asociada una opción en INL.COM para permitir su modificación de forma sencilla. Las variables que forman un TCB se describen en la sección 4, "Formato del TCB". Para acceder a las variables de configuración y estado se pueden usar dos métodos distintos, el indirecto y el directo. El método indirecto consiste en el uso de las rutinas GET_VAR, SET_VAR y COPY_DATA del segmento de código, explic