25 mayo 2007

Mi orgullo friki (extracto)

;================================================
;=== Proceso del paquete recibido: ===
;=== Comprobacion del protocolo del paquete ===
;================================================

ld hl,(IN_BUFFER+2) ;Ether-Type

;--- Comprueba si el protocolo es conocido,
; si lo es salta a la parte adecuada del codigo

ld de,#0008
call COMP
jp z,IS_IP

ld de,#0608
call COMP
jp z,IS_ARP

;--- Protocolo desconocido: lo ignoramos

jp END_GET_PACK


;==============================
;=== Paquete ARP recibido ===
;==============================

;Algoritmo segun RFC826, "Packet Reception"

IS_ARP: ;--- Comprueba el hardware y el protocolo

ld hl,(IN_BUFFER+4)
ld de,#0100
call COMP
jp nz,END_GET_PACK

ld hl,(IN_BUFFER+6)
ld de,#0008
call COMP
jp nz,END_GET_PACK

;--- Comprueba si el tamanyo de la dir HW es 0: paquete UNARP.
; En ese caso busca la entrada y la borra si no es estatica.

ld a,(IN_BUFFER+8)
cp 6
jr z,NO_UNARP
or a
jp nz,END_GET_PACK ;Si no es 0 ni 6, es incorrecto

ld de,IN_BUFFER+18 ;Busca entrada ARP...
call SRCH_ARP
jp c,END_GET_PACK

ld a,(ix)
cp 1
jp z,END_GET_PACK

ld (ix),0 ;...y si la encuentra, la borra
call END_GET_PACK ;si no es estatica.
NO_UNARP:

;--- Si la IP de origen es 0, no cachea la direccion
; (clientes DHCP comprobando la direccion que se
; les acaba de asignar pueden enviar un ARP request
; con la IP del enviador a cero)

ld a,#FF
ld (MERGE_FLAG),a

ld hl,IN_BUFFER+18
ld de,ZERO32
call COMP32
jr z,IS_ARP_2

;--- Comprueba si hay una entrada ARP para esa IP

xor a
ld (MERGE_FLAG),a

ld de,IN_BUFFER+18
call SRCH_ARP
jr c,NOTINCACHE

;* Encontrada: actualiza dir MAC

ld a,#FF
ld (MERGE_FLAG),a

ld a,(ix) ;Si es estatica, no hace nada
cp 1
jp z,NOUPTIMER

push ix
pop de
inc de
ld hl,IN_BUFFER+12
ld bc,6

ldir
NOUPTIMER:

;* Actualiza temporizador de expiracion

ld (ix),2 ;Por si estaba en resolucion
push ix
pop hl
ld bc,11
add hl,bc
ex de,hl
ld hl,ARP_TOUT
ld bc,4
ldir
NOTARPSTAT:
NOTINCACHE:

;--- Si la IP requerida no es la nuestra, terminar ya

IS_ARP_2: ld hl,IN_BUFFER+28
ld de,BUF_IPLOCAL
call COMP32
jp nz,END_GET_PACK

;--- Si la entrada no estaba en la tabla, anyadirla

ld a,(MERGE_FLAG)
or a
jr nz,OKMERGEARP

call GET_FREE_ARP

ld (ix),2 ;Entrada dinamica

push ix ;Copia la MAC y la IP de una sentada
pop de
inc de
ld hl,IN_BUFFER+12
ld bc,10
ldir

ld hl,ARP_TOUT ;Establece temp. de expiracion
ld bc,4
ldir
OKMERGEARP:

;--- Si el paquete era Request, enviar Reply

ld a,(IN_BUFFER+11)
cp 1
call z,SEND_ARP_RP

jp END_GET_PACK

MERGE_FLAG: db 0


;===============================
;=== Datagrama IP recibido ===
;===============================

IS_IP: ;

;--- Primero comprobamos que llegue al tamanyo minimo:
; Eth1 Eth2 Type Cabecera(20)

ld hl,(INBUF_SIZE)
ld de,34
call COMP
jp nc,END_GET_PACK

;--- Guardamos el tamanyo de la cabecera IP en OUT_BUFFER.
; Si es <20 bytes, ignoramos el paquete.

ld a,(IN_BUFFER+4)
and #0F
cp 5
jp c,END_GET_PACK

sla a
sla a
ld l,a
ld h,0
ld (IP_HEADER_LEN),hl

;--- Guardamos el tamanyo total del paquete en OUT_BUFFER+2.
; Si es >576 bytes, ignoramos el paquete.

ld a,(IN_BUFFER+6)
ld h,a
ld a,(IN_BUFFER+7)
ld l,a
ld de,577
call COMP
jp c,END_GET_PACK

ld (IP_TOTAL_LEN),hl

;--- Calcula el checksum de la cabecera

ld a,(CHKVECT)
and %10
jr z,IPCHKSOK

ld ix,IN_BUFFER+4
ld bc,(IP_HEADER_LEN)
call CALC_CHKSUM
ld a,d
or e
jp nz,END_GET_PACK
IPCHKSOK: ;

;--- Comprueba la direccion IP de destino,
; debe ser la nuestra o una de broadcast

;* Es nuestra IP?

ld hl,IN_BUFFER+20
ld de,BUF_IPLOCAL
call COMP32
jr z,OK_IPDEST

;* Es de broadcast? (Mascara OR dir = todo 1s?)

ld hl,SUBNET_MASK
ld de,IN_BUFFER+20
ld b,4
CHKIPBROAD2: ld a,(de)
or (hl)
inc a
jp nz,END_GET_PACK
inc hl
inc de
djnz CHKIPBROAD2
OK_IPDEST:

;--- Si el campo de version no tiene 4, ignoramos el datagrama

ld a,(IN_BUFFER+4)
and #F0
cp #40
jp nz,END_GET_PACK

;--- Si es un fragmento de datagrama, lo ignoramos

ld hl,(IN_BUFFER+10)
res 7,l ;Pone a 0 el bit no usado (previene futuras extensiones de IP)
res 6,l ;Pone a 0 el bit DF. Desde ahora HL solo tiene DF y Frag. Off.
ld a,h ;Si MF o Fragment Offset son <>0,
or l ;es un fragmento: ignorarlo
jp nz,END_GET_PACK

;--- Si el paquete contiene opciones, las descartamos
; (movemos los datos a la posicion IN_BUFFER+24)

ld hl,(IP_HEADER_LEN) ;HL = Long cabecera
ld de,20
call COMP
jr z,IPOPTOK

ld bc,IN_BUFFER+4
add hl,bc ;HL = Inicio de los datos
ld de,IN_BUFFER+24
ld bc,(IP_TOTAL_LEN)
ldir

ld hl,(IP_TOTAL_LEN) ;Cambia el tamanyo
ld de,(IP_HEADER_LEN) ;del paquete para no incluir
or a ;las opciones IP
sbc hl,de ;HL = Long. sin cabeceras
ld de,20
add hl,de ;HL = Long. con cabecera sin opciones
ld a,h
ld (IN_BUFFER+6),a
ld a,l
ld (IN_BUFFER+7),a
ld (IP_TOTAL_LEN),hl
ld (IP_HEADER_LEN),de
IPOPTOK:

;--- Sustituye el campo de tamanyo total del paquete
; por el tamanyo de los datos, y en little-endian

ld hl,(IP_TOTAL_LEN)
ld de,(IP_HEADER_LEN)
or a
sbc hl,de
ld (IN_BUFFER+6),hl

;--- Decide accion segun protocolo transportado

ld a,(IN_BUFFER+13)
cp 6
jp z,IS_TCP
cp 17
jp z,IS_UDP
cp 1
jp z,IS_ICMP

jp END_GET_PACK ;Desconocido: ignoramos


;===============================
;=== Mensaje ICMP recibido ===
;===============================

IS_ICMP: ;--- Comprueba el checksum

ld a,(CHKVECT)
and %10000
jr z,ICMPCHK_OK

ld ix,IN_BUFFER+24
ld bc,(IN_BUFFER+6)
call CALC_CHKSUM
ld a,d
or e
jp nz,END_GET_PACK
ICMPCHK_OK: ;

;--- Comprobamos el tipo

ld a,(IN_BUFFER+24)
or a ;ECHO Reply?
jr z,IS_ICMP_EREP
cp 3
jp z,IS_ICMP_HOSTUN
cp 8 ;ECHO request?
jp nz,END_GET_PACK ;Otro: lo ignoramos

;--- ICMP Echo Request: Lo respondemos si REPLYECHO<>0

IS_ICMP_EREQ: ld a,(REPLYECHO)
or a
jp z,END_GET_PACK

ld hl,IN_BUFFER+24 ;Copiamos el mensaje tal cual en la respuesta
ld de,OUT_BUFFER+24
ld bc,(IN_BUFFER+6)
push bc
inc bc ;Para que incluya el 0 de padding
ldir

xor a
ld (OUT_BUFFER+24),a ;Cambia el tipo a "Echo reply"
pop bc
ld hl,0
ld (OUT_BUFFER+26),hl ;Borra checksum antiguo
ld ix,OUT_BUFFER+24
call CALC_CHKSUM
ld (OUT_BUFFER+26),de ;Establece nuevo checksum

ld hl,(IN_BUFFER+16) ;IP destino = el originador del mensaje
ld de,(IN_BUFFER+18)
ld bc,(IN_BUFFER+6) ;Longitud
ld a,1 ;Protocolo = ICMP
jp SEND_IP

;--- ICMP Echo Reply: lo encolamos si no hay ya 4 encolados

IS_ICMP_EREP: ld a,(ICMPI_PINDEX)
ld b,a

inc b
ld ix,ICMPI_IP0-11
ld de,11
BUC_EREP1: add ix,de
djnz BUC_EREP1 ;Ahora IX apunta a la zona de datos adecuada

ld a,(ICMPI_PINDEX)
ld b,a
ld a,(ICMPI_GINDEX)
cp b
jr nz,EREP_HAYSITIO

ld a,(ix) ;Indice GET = Indice PUT:
or (ix+1) ;Hay sitio si entrada vacia (IP=0),
or (ix+2) ;si no, terminamos.
or (ix+3)
jp nz,END_GET_PACK
EREP_HAYSITIO: ;

ld hl,(IN_BUFFER+16)
ld (ix),l
ld (ix+1),h
ld hl,(IN_BUFFER+18)
ld (ix+2),l
ld (ix+3),h
ld a,(IN_BUFFER+12) ;TTL
ld (ix+4),a
ld hl,(IN_BUFFER+28) ;Identificador (guardamos en L.E.)
ld (ix+5),h
ld (ix+6),l
ld hl,(IN_BUFFER+30) ;Numero de secuencia (guardamos en L.E.)
ld (ix+7),h
ld (ix+8),l
ld hl,(IN_BUFFER+6) ;Longitud de los datos
ld bc,8 ;Resta long. cabecera ICMP
or a
sbc hl,bc
ld (ix+9),l
ld (ix+10),h

ld a,(ICMPI_PINDEX) ;Incrementa indice PUT
inc a ;saltando de 3 a 0
and %111
ld (ICMPI_PINDEX),a

jp END_GET_PACK

;--- ICMP Destination Unreachable: si esta asociado a una
; conexion TCP, la abortamos con codigo 7

;Nota: la cabecera del paquete causante de esta respuesta
;ICMP esta en IN_BUFFER+32.

IS_ICMP_HOSTUN: ld a,(IN_BUFFER+9+32)
cp 6 ;Si el paquete causante no es TCP, ignorarlo
jp nz,END_GET_PACK

ld hl,(IN_BUFFER+20+28)
ld de,(IN_BUFFER+22+28)
ld a,(IN_BUFFER+24+28)
ld iyh,a
ld a,(IN_BUFFER+25+28)
ld iyl,a
ld a,(IN_BUFFER+26+28)
ld ixh,a
ld a,(IN_BUFFER+27+28)
ld ixl,a
call SEARCH_TCP
cp #FF ;Conexion asociada inexistente: terminar
jp z,END_GET_PACK

call LOAD_TCB
xor a ;Cierra la conexion con codigo 7
ld (TCP_STATE),a
ld a,7
ld (LAST_CLOSE),a
call SAVE_TCB

jp END_GET_PACK


;==============================
;=== Paquete UDP recibido ===
;==============================

IS_UDP: ;--- Comprueba checksum, excepto si CHKVECT dice lo contrario

ld a,(CHKVECT)
and %1000
jp z,OK_UDP_CHKSUM

;* Para calcular el checksum, componemos la pseudo-cabecera
; justo antes de los datos, pisando la cabecera IP.
; Pero como el orden de los datos (palabras de 16 bits)
; no es importante para el calculo,
; aprovechamos que las direcciones IP y el campo de protocolo
; ya estan establecidos en la cabecera IP, y asi
; solo necesitamos establecer 0 donde estaba el TTL y la longitud UDP
; donde estaba el checksum IP.

xor a
ld (IN_BUFFER+12),a ;Campo TTL
ld hl,(IN_BUFFER+28) ;Long. UDP en el campo checksum IP
ld (IN_BUFFER+14),hl

ld hl,(IN_BUFFER+6)
ld bc,12 ;Para que incluya pseudo-cabecera
add hl,bc
push hl
pop bc
ld ix,IN_BUFFER+12
call CALC_CHKSUM
ld a,d
or e
jp nz,END_GET_PACK
OK_UDP_CHKSUM: ;

;--- Checksum correcto: comprobamos si es una respuesta DNS
; o un paquete DHCP, en ese caso se salta al codigo
; de procesamiento adecuado

ld a,(IN_BUFFER+26)
ld h,a
ld a,(IN_BUFFER+27)
ld l,a

ld de,DNS_PORT
call COMP
jp z,IS_DNS

ld de,68
call COMP
jp z,IS_DHCP

;--- Comprobamos que se puede almacenar el paquete

ld hl,(IN_BUFFER+6) ;Paquete demasiado grande?
ld de,548+8+1
call COMP
jp c,END_GET_PACK

ld a,(UDPI_PINDEX)
ld b,a

inc b
ld ix,UDPI_IP0-10
ld de,10
BUC_UDP1: add ix,de
djnz BUC_UDP1 ;Ahora IX apunta a la zona de datos adecuada

ld a,(UDPI_PINDEX)
ld b,a
ld a,(UDPI_GINDEX)
cp b
jr nz,UDPI_HAYSITIO

ld a,(ix) ;Indice GET = Indice PUT:
or (ix+1) ;Hay sitio si entrada vacia (IP=0),
or (ix+2) ;si no, terminamos.
or (ix+3)
jp nz,END_GET_PACK
UDPI_HAYSITIO: ;

;--- Almacenamos paquete

ld hl,(IN_BUFFER+16) ;IP origen
ld (ix),l
ld (ix+1),h
ld hl,(IN_BUFFER+18)
ld (ix+2),l
ld (ix+3),h
ld hl,(IN_BUFFER+24) ;Puerto origen (little endian)
ld (ix+4),h
ld (ix+5),l
ld hl,(IN_BUFFER+26) ;Puerto destino (little endian)
ld (ix+6),h
ld (ix+7),l
ld hl,(IN_BUFFER+6) ;Longitud datos UDP
ld bc,8
or a
sbc hl,bc
ld (ix+8),l
ld (ix+9),h

ld a,h ;Copia la parte de datos
or l ;si no tiene long 0
jr z,OK_UDPIDATA

push hl
ld hl,UDP_BUFFERS-512
ld de,512
ld a,(UDPI_PINDEX)
inc a
ld b,a
UDPLEN_LOP1: add hl,de
djnz UDPLEN_LOP1
ex de,hl
ld hl,IN_BUFFER+32
pop bc
ldir
OK_UDPIDATA: ;

ld hl,UDPI_PINDEX
call INC_UDPINDEX ;Actualiza indice para el proximo paquete
jp END_GET_PACK


;==============================
;=== Paquete DNS recibido ===
;==============================

IS_DNS: ;--- Comprueba si hay una peticion en curso
; y si coincide el ID con el ultimo enviado,
; si no, descarta el paquete

ld a,(DNS_STAT_P)
cp 1
jp nz,END_GET_PACK

ld hl,(IN_BUFFER+32)
ld de,(ID_DNS)
call COMP
jp nz,END_GET_PACK

;--- Si la respuesta esta truncada, ponemos error 21
; a no ser que podamos reintentar con el DNS secundario.

ld a,(IN_BUFFER+34)
and %00000010
jr z,DNS_NOTRUNC

call DNS_USE_SEC
jp nc,END_GET_PACK

ld a,3
ld (DNS_STAT_P),a
ld a,21
ld (DNS_STAT_S),a
jp END_GET_PACK
DNS_NOTRUNC: ;

;--- Si la respuesta contenia algun error,
; lo establecemos, a no ser que podamos reintentar
; con el DNS secundario.

ld a,(IN_BUFFER+35)
and %00001111
jr z,DNS_NOERR

call DNS_USE_SEC
jp nc,END_GET_PACK

ld a,3
ld (DNS_STAT_P),a
ld a,(IN_BUFFER+35)
and %00001111
ld (DNS_STAT_S),a
jp END_GET_PACK
DNS_NOERR: ;

;--- La respuesta no contiene error.
; Comprobamos si contiene alguna respuesta valida.

ld ix,IN_BUFFER+32 ;Apunta al ppio del paquete DNS

ld h,(ix+6)
ld l,(ix+7)
ld (ANCOUNT),hl ;Numero de respuestas
ld h,(ix+8)
ld l,(ix+9)
ld (NSCOUNT),hl ;Numero de servidores autoritativos
ld h,(ix+10)
ld l,(ix+11)
ld (ARCOUNT),hl ;Numero de secciones adicionales

ld ix,IN_BUFFER+44 ;IX=Inicio de la pregunta

SKIPQ_LOOP: ld a,(ix) ;Se salta la pregunta
inc ix ;(campo QNAME)
or a ;comprobando si hay compresion
jr z,SKIPQ_LOOP3
bit 7,a
jr z,SKIPQ_LOOP
SKIPQ_LOOP2: inc ix ;Se salta QTYPE y QCLASS
SKIPQ_LOOP3: inc ix ;(mas el segundo byte del puntero
inc ix ;si es necesario)
inc ix
inc ix ;Ahora IX apunta a las respuestas

;* Comprobamos si se nos da directamente la respuesta

SCAN_FOR_AN: ld bc,(ANCOUNT)
ld a,b
or c
jr z,SCAN_FOR_NS
call SCAN_DNS_RR
or a
jr z,SCAN_FOR_NS

ld a,2 ;Si habia una respuesta valida,
ld (DNS_STAT_P),a ;ponemos status=2 y terminamos
xor a
ld (DNS_STAT_S),a
jp END_GET_PACK

;* Si no, comprobamos si hay IPs de otros servidores
; en "Authoritative" o en "Aditional"

SCAN_FOR_NS: ld bc,(NSCOUNT)
ld a,b
or c
jr z,SCANNS_FAILED ;Seccion NS vacia?

call SCAN_DNS_RR ;Busca IPs de servidores
or a ;en "authoritative"
jp nz,CHANGE_DNS_IP

ld bc,(ARCOUNT)
ld a,b
or c
jr z,SCANNS_FAILED ;Seccion AR vacia?

call SCAN_DNS_RR ;Busca IPs de servidores
or a ;en "additional"
jp nz,CHANGE_DNS_IP

SCANNS_FAILED: ld a,3 ;Si no hay nada en NS ni AR, error 20
ld (DNS_STAT_P),a
ld a,20
ld (DNS_STAT_S),a
jp END_GET_PACK

;* Se ha encontrado la IP de otro DNS:
; Repetimos la consulta usandola

CHANGE_DNS_IP: ld hl,DNS_RESULT
ld de,DNS_IP ;Establecemos nueva direccion
ld bc,4 ;del DNS a consultar
ldir

ld hl,(ID_DNS) ;Incrementamos identificador
inc hl
ld (ID_DNS),hl

ld a,3 ;Ponemos estado secundario a 3
ld (DNS_STAT_S),a
xor a ;Inicializamos reintentos
ld (DNS_RETRY),a
inc a
ld (DNS_TOUT),a ;Para que se reenvie inmediatamente

jp END_GET_PACK


;--- Esta subrutina examina la zona a la que apunta IX
; y busca un RR del tipo "Direccion IP".
; Si lo encuentra, pone la IP en DNS_REPLY
; y DNS_RESP_FLAG a #FF (que devuelve en A).
; Al final, IX apunta a la siguiente zona.
;
; Entrada: BC = Numero de RRs en la zona.

SCAN_DNS_RR: xor a
ld (DNS_RESP_FLAG),a

DNS_AN_LOOP: push bc
SKIPQ_LOOP4: ld a,(ix) ;Se salta el nombre
inc ix ;comprobando si esta comprimido
or a
jr z,SKIPQ_LOOP6
bit 7,a
jr z,SKIPQ_LOOP4
SKIPQ_LOOP5: inc ix
SKIPQ_LOOP6: ;

ld a,(DNS_RESP_FLAG) ;Si ya hay una respuesta
or a ;valida, simplemente se salta
jr nz,DNS_AN_LOOP2 ;el RR

;* Comprueba que el tipo sea "Direccion IP"

ld h,(ix) ;IX apunta a TYPE
ld l,(ix+1)
ld de,1
call COMP
jr nz,DNS_AN_LOOP2

;* Se ha encontrado respuesta:
; se copia a DNS_RESULT

ld l,(ix+10)
ld h,(ix+11)
ld e,(ix+12)
ld d,(ix+13)
ld (DNS_RESULT),hl
ld (DNS_RESULT+2),de

ld a,#FF
ld (DNS_RESP_FLAG),a

;* Pasa al siguiente RR

DNS_AN_LOOP2: ld bc,10
add ix,bc ;Para que apunte a RDATA
ld b,(ix-2)
ld c,(ix-1) ;BC = RDLENGTH
add ix,bc

;* Si quedan RRs, vuelve a empezar

pop bc
dec bc
ld a,b
or c
jr nz,DNS_AN_LOOP

ld a,(DNS_RESP_FLAG)
ret

DNS_RESP_FLAG: db 0 ;#FF cuando se encuentra respuesta


;--- Esta subrutina es llamada cuando se recibe un paquete
; DNS erroneo o se consumen todos los reenvios.
; Lo que hace es comprobar si el DNS
; usado era el primario y hay un secundario disponible.
; Si es asi, establece DNS_IP con la direccion
; del DNS secundario, pone DNS_STAT_S a 2
; y DNS_RETRY a 0 (es decir, lo prepara todo
; para repetir la consulta usando el DNS secundario),
; y devuelve Cy=0.
; En caso contrario, devuelve Cy=1 (error).

DNS_USE_SEC: ld a,(DNS_STAT_S)
cp 1
scf
ret nz ;Si no era el primario

ld ix,BUF_IPDNS1
ld a,(ix+4)
or (ix+5)
or (ix+6)
or (ix+7)
scf
ret z ;Si era el primario pero no hay sec.

ld hl,BUF_IPDNS2 ;Establece secundario
ld de,DNS_IP ;y reinicia contador de
ld bc,4 ;reenvios
ldir
ld a,2
ld (DNS_STAT_S),a
xor a ;De paso pone Cy=0
ld (DNS_RETRY),a
inc a
ld (DNS_TOUT),a ;Para que se repita el envio inmediatamente
ret


;===============================
;=== Paquete DHCP recibido ===
;===============================

IS_DHCP: ld a,(DHCP_VECT) ;Ignorar si no usamos DHCP
or a
jp z,END_GET_PACK

ld a,(DHCP_STATE)
cp CONFIGURED
jp z,END_GET_PACK

;--- Obtiene el tipo del paquete

ld a,(IN_BUFFER+32) ;Es BOOTREPLY?
cp 2
jp nz,END_GET_PACK

ld hl,IN_BUFFER+36 ;El 'xid' coincide?
ld de,DHCP_XID
call COMP32
jp nz,END_GET_PACK

call DHCP_GET_TYPE
cp DHCPOFFER
jr z,IS_DHCP_OFFER
cp DHCPACK
jr z,IS_DHCP_ACK
cp DHCPNAK
jp z,IS_DHCP_NAK
jp END_GET_PACK ;Otros tipos son ignorados


;--- Paquete DHCPOFFER

IS_DHCP_OFFER:

;* Si el estado no es SELECTING, ignorarlo

ld a,(DHCP_STATE)
cp SELECTING
jp nz,END_GET_PACK

;* Guarda el identificador del servidor

call DHCP_GET_SERVER
jp c,END_GET_PACK
ld de,DHCP_SERVER
ld bc,4
ldir

;* Guarda el campo 'yiaddr' como la IP ofrecida

ld hl,IN_BUFFER+48
ld de,DHCP_YIADDR
ld bc,4
ldir

;* Guarda el 'xid' recibido

ld hl,IN_BUFFER+36
ld de,DHCP_RCVXID
ld bc,4
ldir

;* Pasa a estado REQUESTING y envia DHCPREQUEST

ld a,REQUESTING
ld (DHCP_STATE),a
ld a,DHCPREQUEST
call SEND_DHCP
call DHCP_FIRST
jp END_GET_PACK


;--- Paquete ACK

IS_DHCP_ACK:

;* Si el estado no es REBINDING, REQUESTING, RENEWING
; o INFORMING, ignorarlo

ISDHCPACK2: cp REQUESTING
jr z,ISDHCPACK3
cp RENEWING
jr z,ISDHCPACK3
cp REBINDING
jr z,ISDHCPACK3
cp INFORMING
jp nz,END_GET_PACK
ISDHCPACK3:

;* Si el identificador del servidor no coincide con el anterior,
; ignorar el mensaje
; (Problema: Que pasa si el ACK ha sido recibido en estado REBINDING,
; por un servidor distinto al usado anteriormente?
; Por eso, nos fiaremos unicamente del XID)

;call DHCP_GET_SERVER
;jp c,END_GET_PACK
;ld de,DHCP_SERVER
;call COMP32
;jp nz,END_GET_PACK

;* Inicializa los campos Lease, T1 y T2

ld hl,DHCP_T1
ld de,DHCP_T1+1
ld bc,12-1
ld (hl),0
ldir

;* Guarda el campo 'yiaddr' como la IP asignada,
; a no ser que hubieramos enviado DHCPINFORM

ld a,(DHCP_VECT)
and 1
jr z,ISDHCPACK4

ld hl,IN_BUFFER+48
ld de,BUF_IPLOCAL
ld bc,4
ldir
ld a,1
ld (DHCP_VECT_O),a
ISDHCPACK4:

;* Recorre todas las opciones y las procesa

call DHCP_INIT_OP

ISDHCPACKL: call DHCP_NEXT_OP
or a
jp z,ISDHCPACKEND ;No quedan opciones?

;* T1: Lo almacena

cp 58
jr nz,ISDHCPACK_NOT1

push ix
pop hl
ld de,DHCP_T1
ld bc,4
ldir

ld ix,DHCP_T1
call POR60_32
jr ISDHCPACKL
ISDHCPACK_NOT1:

;* T2: Lo almacena

cp 59
jr nz,ISDHCPACK_NOT2

push ix
pop hl
ld de,DHCP_T2
ld bc,4
ldir

ld ix,DHCP_T2
call POR60_32
jr ISDHCPACKL
ISDHCPACK_NOT2:

;* Lease: Lo almacena

cp 51
jr nz,ISDHCPACK_NOLS

push ix
pop hl
ld de,DHCP_LEASE
ld bc,4
ldir

ld ix,DHCP_LEASE
call POR60_32
jr ISDHCPACKL
ISDHCPACK_NOLS:

;* Mascara de subred: la almacena si la habiamos pedido

cp 1
jr nz,ISDHCPACK_NOSB

ld a,(DHCP_VECT)
and %10 ;La habiamos pedido?
jr z,ISDHCPACKL

push ix
pop hl
ld de,SUBNET_MASK
ld bc,4
ldir

ld a,(DHCP_VECT_O)
or %10
ld (DHCP_VECT_O),a
jr ISDHCPACKL
ISDHCPACK_NOSB:

;* Gateway por defecto: lo almacena si lo habiamos pedido

cp 3
jr nz,ISDHCPACK_NOGW

ld a,(DHCP_VECT)
and %100 ;La habiamos pedido?
jr z,ISDHCPACKL

push ix
pop hl
ld de,DEFGW
ld bc,4
ldir

ld a,(DHCP_VECT_O)
or %100
ld (DHCP_VECT_O),a
jp ISDHCPACKL
ISDHCPACK_NOGW:

;* Servidores DNS: los almacena si los habiamos pedido

cp 6
jr nz,ISDHCPACK_NODN

ld a,(DHCP_VECT)
and %1000 ;Los habiamos pedido?
jp z,ISDHCPACKL

ld a,(DHCP_VECT_O)
or %1000
ld (DHCP_VECT_O),a

push bc
push ix
pop hl
ld de,BUF_IPDNS1
ld bc,4
ldir

pop bc ;Hay mas de un DNS en la opcion?
ld a,b
cp 8
jp c,ISDHCPACKL
ld de,BUF_IPDNS2
ld bc,4
ldir
jp ISDHCPACKL
ISDHCPACK_NODN:

;* Timeout ARP: lo almacena si lo habiamos pedido

cp 35
jr nz,ISDHCPACK_NOAT

ld a,(DHCP_VECT)
and %10000 ;Lo habiamos pedido?
jp z,ISDHCPACKL

push ix,ix
pop hl
ld de,ARP_TOUT
ld bc,4
ldir
pop hl
ld de,ARP_TOUT_SECS
ld bc,4
ldir
ld ix,ARP_TOUT
call POR60_32

ld a,(DHCP_VECT_O)
or %10000
ld (DHCP_VECT_O),a
jp ISDHCPACKL
ISDHCPACK_NOAT:

;* Tipo de trama ethernet: lo almacena si lo habiamos pedido

cp 36
jr nz,ISDHCPACK_NOFT

ld a,(DHCP_VECT)
and %100000 ;Lo habiamos pedido?
jp z,ISDHCPACKL

ld a,(ix)
or a
jr z,ISDHCPACK_FT
ld a,#FF
ISDHCPACK_FT: ld (FRAME_TYPE),a

ld a,(DHCP_VECT_O)
or %100000
ld (DHCP_VECT_O),a
;jp ISDHCPACKL
ISDHCPACK_NOFT:

;* Opcion no reconocida: ignorarla

jp ISDHCPACKL
ISDHCPACKEND:

;* Fin de las opciones.
; Si estamos en INFORMING, pasar a CONFIGURED y terminar.

ld a,(DHCP_STATE)
cp INFORMING
jr nz,ISDHCPACKEND0

ld a,CONFIGURED
ld (DHCP_STATE),a
jp END_GET_PACK
ISDHCPACKEND0:

;* Si T1 es 0, establecerlo a lease/2

ld hl,DHCP_T1
ld de,ZERO32
call COMP32
jr nz,OKT1NZ

ld hl,#FFFF ;Si lease infinito, T1 infinito
ld (DHCP_T1),hl
ld (DHCP_T1+2),hl
ld a,(DHCP_LEASE)
cp h
jr z,OKT1NZ

ld hl,DHCP_LEASE
ld de,DHCP_T1
ld bc,4
ldir
ld ix,DHCP_T1
ld b,1
call ENTRE2_32
OKT1NZ:

;* Si T2 es 0, establecerlo a 0.875*lease ((7/8)*lease)

ld hl,DHCP_T2
ld de,ZERO32
call COMP32
jr nz,OKT2NZ

ld hl,#FFFF ;Si lease infinito, T2 infinito
ld (DHCP_T2),hl
ld (DHCP_T2+2),hl
ld a,(DHCP_LEASE)
cp h
jr z,OKT2NZ

ld hl,DHCP_LEASE
ld de,DHCP_T2
ld bc,4
ldir

ld ix,DHCP_T2 ;Divide T2 entre 8
ld b,3
call ENTRE2_32

ld hl,DHCP_T2
ld de,IN_BUFFER
ld bc,4
ldir

ld b,6
T2ISZL: push bc ;Multiplica (T2/8) por 7
ld hl,IN_BUFFER
ld de,DHCP_T2
ld bc,DHCP_T2
call ADD32
pop bc
djnz T2ISZL
OKT2NZ:

;* Fin: pasa a BOUND y termina

ld a,BOUND
ld (DHCP_STATE),a
jp END_GET_PACK


;--- Paquete NAK

IS_DHCP_NAK:

;* En estado BOUND, SELECTING, INFORMING o CONFIGURED, ignorarlo

ld a,(DHCP_STATE)
cp BOUND
jp z,END_GET_PACK
cp SELECTING
jp z,END_GET_PACK
cp INFORMING
jp z,END_GET_PACK
cp CONFIGURED
jp z,END_GET_PACK

;* En otros estados, volver a INIT

xor a ;ld a,INIT
ld (DHCP_STATE),a
jp END_GET_PACK


;===============================
;=== Segmento TCP recibido ===
;===============================

;Despues de la publicidad.

NOTA: Si no sabes qué es esto, no eres un friki auténtico. Háztelo mirar.

4 comentarios:

Anónimo dijo...

Pues en contestacion:

if stat[conexact]=4 then
begin {*** D ***}
writeln('*** conexion abierta');

repeat {*** 2 ***}

{ letra:=inkey;}

letrain:=inltcp_rcvstr(conex[conexact]);

write('linea: ');
write(linea);
writeln(' ');

for code:=1 to 10 {Length(letrain)} do
write(letrain[code]);

writeln;

if Length(letrain)>0 then
begin {*** C ***}
if linea=1 then
begin {*** B ***}
Comm:=copy(letrain,1,pos(' ',letrain)-1);

if Comm='GET'then
begin
writeln('*** Recibido GET');
letraprime:=letrain;
linea:=linea+1
end;


if Comm='HEAD' then
begin
writeln('*** Recibido HEAD');
inltcp_close(conex[conexact]);

end;
if Comm='POST' then
begin
writeln('*** Recibido POST');
inltcp_close(conex[conexact]);
end;
end; {*** B ***}

if letrain[length(letrain)-2]=chr(10) then
begin {*** B ***}
writeln('*** Recibida Cabecera');

if Comm='GET'then
begin {*** A ***}

filename:=copy(letraprime , 5 , pos(' ',copy(letraprime,5,(length(letraprime)-1) ))-1);

if pos('?',filename)> 0 then
begin
capture;
filename:=copy(filename,1,pos('?',filename)-1);
end;



writeln('*** Pedido: '+Filename+'***');

IF FILENAME='/' THEN FILENAME:='index.htm'
else
Filename:=copy(filename,2,length(filename));

writeln('*** Abriendo: '+Filename);

{filesz:=trunc(GetFileSize(filename));}
sal:=real2intstr(GetFileSize(filename));

WRITELN('*** LONGITUD:',SAL);
WRITELN('*** IORESULT:',MsxIoResult);

if MsxIoResult = $D7 then
begin
writeln('*** ERROR 404');
inltcp_SENDstr(conex[conexact],'HTTP/1.0 Document follows'+chr(13)+chr(10));
inltcp_SENDstr(conex[conexact],'Content-type: text/html'+chr(13)+chr(10));
inltcp_SENDstr(conex[conexact],'Server MSX Server/0.1'+chr(13)+chr(10));

sal:=real2intstr(GetFileSize(filename));


inltcp_SENDstr(conex[conexact],'Content-length: '+sal+chr(13)+chr(10));
inltcp_SENDstr(conex[conexact],chr(13)+chr(10));

file_http:=msxfileopen('ERROR404.HTM') ; { Assign name to handle }

repeat {*** 3 ***}
lecturas:=448;
MSXfileRead(file_http,addr(bufftcpout),lecturas);

INLTCP_FLUSH(conex[conexact]);
repeat
freebuff:=INLTCP_FREEBUFF(conex[conexact]);
writeln('*** WAITING FOR BUFFER',freebuff);
until (freebuff>=lecturas) or (freebuff<0);

INLTCP_SENDblck (conex[conexact],addr(bufftcpout),lecturas);

until (MSXIoResult=$C7) or (freebuff<0); {*** 3 ***}

msxfileclose(file_http);

end
else
begin

inltcp_SENDstr(conex[conexact],'HTTP/1.0 Document follows'+chr(13)+chr(10));

fileext:=copy(filename , length(filename)-2,3);


writeln('*** '+fileext);

if ((fileext='gif') or (fileext='jpg') or (fileext='bmp') or (fileext='png')) then
begin
inltcp_SENDstr(conex[conexact],'Content-type: image/'+fileext+chr(13)+chr(10));
end
else
begin
inltcp_SENDstr(conex[conexact],'Content-type: text/html'+chr(13)+chr(10));
end;

inltcp_SENDstr(conex[conexact],'Server MSX Server/0.1'+chr(13)+chr(10));

inltcp_SENDstr(conex[conexact],'Content-length: '+sal+chr(13)+chr(10));
inltcp_SENDstr(conex[conexact],chr(13)+chr(10));

file_http:=msxfileopen(filename); { Assign name to handle }

repeat {*** 3 ***}
lecturas:=448;
MSXfileRead(file_http,addr(bufftcpout),lecturas);


WRITELN('*** IORESULT:',MsxIoResult);
WRITELN('*** LECTURAS:',LECTURAS);

INLTCP_FLUSH(conex[conexact]);
repeat
freebuff:=INLTCP_FREEBUFF(conex[conexact]);
writeln('*** WAITING FOR BUFFER',freebuff);
until (freebuff>=lecturas) or (freebuff<0);

INLTCP_SENDblck (conex[conexact],addr(bufftcpout),lecturas);

until (MSXIoResult=$C7) or (freebuff<0); {*** 3 ***}

msxfileclose(file_http);


end;
inltcp_close(conex[conexact]);

linea:=1;

letrain:='';
letraprime:='';

end; {*** A ***}

end; {*** B ***}

end; {*** C ***}


until (letra=chr(27)) or (INLTCP_STATUS (conex[conexact])<>4); {*** 2 ***}

inltcp_close(conex[conexact]);

repeat {*** 2b ***}
statin:=stat[conexact];
stat[conexact]:=INLTCP_STATUS (conex[conexact]);

if not(statin=stat[conexact]) then
begin
write('* ');
write(stat[conexact]);
write(' ');
end;
until (stat[conexact]=0); {*** 2b ***}

{-------}
end; {*** D ***}

Anónimo dijo...

protocolos tcp,udp ,suma de comprobacion todo eso me suena pero aun no soy lo suficiente friki eso si para el año me apunto a un curso, quiero llegar a ser tan friki como konamiman!!

Anónimo dijo...

Konamiman, acabo de verte en Kirai donde eramos los únicos a los que nos importaba mínimamente el MSX (y la pregunta estaba mal formulada :( :P )

Te agrego al RSS, y aprovecho para saludar... Próximamente me leeré todo lo que tengas que decir :P

Por cierto, el código que pones parece una pila de estas TCP/IP escrita ahí a lo bruto... Ya me gustaría a mi tener una ObsoNET para probarlo (*GUIÑO GUIÑO*) xD

Saludos!

Konamiman dijo...

Bienvenido y lee poco a poco, no te indigestes. Respecto a la Obsonet, paciencia, todo llegará.