;=====================================================
;=== Obtain an incoming packet, Ethernet version ===
;=====================================================
;The packet start address in memory is decided dynamically
;according to the packet type (Ethernet II or IEEE802.3),
;so that the packet data (after the Ethernet header)
;always starts at IN_BUFFER+4.
;This allows to use the same IP/UDP/TCP processing code
;as the PPP version.
GET_PACKET:
ethnet ETH_IN_STATUS ;BC=length, HL=Eth-Type or length
or a
jp z,END_GET_PACK
;--- If there is a frame available, network is active
ld a,#FF
ld (NET_STATE),a
ld de,5*60
ld (NETSTAT_TIMER),de
;--- If frame payload is >576 bytes, discard it
ld a,b
and %11111100
jp nz,END_GET_PACK
ld a,b
or c
jp z,END_GET_PACK
push hl,bc
pop hl
ld de,576+22+1 ;Prevents IEEE802.3 header
call COMP ;NC if HL<DE
pop hl
jr nc,INSIZE_OK
ld hl,0 ;Too big: discard it
ethnet ETH_GET_FRAME
jp END_GET_PACK
INSIZE_OK:
;--- Retrieve frame at the appropriate address
; according to its type
ld de,1501
call COMP
jr nc,IN_IS_IEEE
;* Ethernet 2 type
ld hl,IN_BUFFER-10
ethnet ETH_GET_FRAME
ld (INBUF_SIZE),bc
jr IN_GET_OK
;* IEEE802.3 type
IN_IS_IEEE:
ld hl,IN_BUFFER-18
ethnet ETH_GET_FRAME
;Modify header so that MAC addresses and ether-type
;are in the same place as in Ethernet 2 frames
push bc
pop hl
ld bc,8
or a
sbc hl,bc
ld (INBUF_SIZE),hl
ld hl,IN_BUFFER-18+6
ld de,IN_BUFFER-10+6
ld bc,6
ldir
ld hl,IN_BUFFER-18
ld de,IN_BUFFER-10
ld bc,6
ldir
IN_GET_OK:
;--- Check that destination MAC address matches our address
; or is the broadcast address (prevents promiscuous mode
; being set by another program)
ld hl,HWAD
ld de,IN_BUFFER-10
call COMP32
jp nz,GETP_CHKBRO
ld hl,(HWAD+4)
ld de,(IN_BUFFER-10+4)
call COMP32
jp z,OK_GETP_HW
GETP_CHKBRO: ld hl,ETH_BROAD
ld de,IN_BUFFER-10
call COMP32
jp nz,END_GET_PACK
ld hl,(ETH_BROAD+4)
ld de,(IN_BUFFER-10+4)
call COMP32
jp nz,END_GET_PACK
OK_GETP_HW:
;====================================
;=== Process received frame: ===
;=== Check transported protocol ===
;====================================
ld hl,(IN_BUFFER+2) ;Ether-Type
;--- If protocol is known, jump to the
; appropriate handling code
ld de,#0008
call COMP
jp z,IS_IP
ld de,#0608
call COMP
jp z,IS_ARP
;--- Unknown protocol: ignore frame
jp END_GET_PACK
;============================
;=== ARP frame received ===
;============================
;Algorithm according to RFC826, "Packet Reception"
IS_ARP:
;--- Check hardware and protocol
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
;--- Check if hardware address size is 0: UNARP packet.
; In this case, search the entry and delete it
; unless it is a static entry.
ld a,(IN_BUFFER+8)
cp 6
jr z,NO_UNARP
or a
jp nz,END_GET_PACK ;If not 0 nor 6, invalid value
ld de,IN_BUFFER+18 ;Search ARP entry...
call SRCH_ARP
jp c,END_GET_PACK
ld a,(ix)
cp 1
jp z,END_GET_PACK
ld (ix),0 ;...and if found and not static,
call END_GET_PACK ;delete it.
NO_UNARP:
;--- If source IP address is 0, do not cache the address
; (DHCP clients checking its newly assigned address
; may send ARP request with the IP of sender set to 0)
ld a,#FF
ld (MERGE_FLAG),a
ld hl,IN_BUFFER+18
ld de,ZERO32
call COMP32
jr z,IS_ARP_2
;--- Check if there is an ARP entry for this IP
xor a
ld (MERGE_FLAG),a
ld de,IN_BUFFER+18
call SRCH_ARP
jr c,NOTINCACHE
;* Found: update MAC address
ld a,#FF
ld (MERGE_FLAG),a
ld a,(ix) ;Static entry: do nothing
cp 1
jp z,NOUPTIMER
push ix
pop de
inc de
ld hl,IN_BUFFER+12
ld bc,6
ldir
NOUPTIMER:
;* Update entry expiration timer
ld (ix),2 ;Prevents entry being in resolution
push ix
pop hl
ld bc,11
add hl,bc
ex de,hl
ld hl,ARP_TOUT
ld bc,4
ldir
NOTARPSTAT:
NOTINCACHE:
;--- If required IP is not our address, terminate
IS_ARP_2:
ld hl,IN_BUFFER+28
ld de,BUF_IPLOCAL
call COMP32
jp nz,END_GET_PACK
;--- If the entry was not in the table, create it
ld a,(MERGE_FLAG)
or a
jr nz,OKMERGEARP
call GET_FREE_ARP
ld (ix),2 ;Dynamic entry
push ix ;Copy MAC and IP with one single LDIR
pop de
inc de
ld hl,IN_BUFFER+12
ld bc,10
ldir
ld hl,ARP_TOUT ;Set expiration timer
ld bc,4
ldir
OKMERGEARP:
;--- If it was a Request packet, send Reply
ld a,(IN_BUFFER+11)
cp 1
call z,SEND_ARP_RP
jp END_GET_PACK
MERGE_FLAG:
db 0
endif
;==============================
;=== IP datagram received ===
;==============================
IS_IP: ;
;--- First check that it reaches the minimum size:
; #FF #03 #00 #21 Header(20) FCS1 FCS2
ld hl,(INBUF_SIZE)
ld de,26
call COMP
jp nc,END_GET_PACK
;--- Save header size.
; If <20 bytes, ignore datagram.
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
;--- Save total datagram length.
; If >576 bytes, ignore datagram.
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
;--- Calculate header checksum
if LINK=0
ld a,(VJ_RCVED) ;If it was VJ Compressed_TCP, header was
cp 2 ;generated by us: don't calculate checksum
jr z,IPCHKSOK
endif
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: ;
if LINK=1
;--- Check destination IP address,
; must be our address or a broadcast address
;* Is it our address?
ld hl,IN_BUFFER+20
ld de,BUF_IPLOCAL
call COMP32
jr z,OK_IPDEST
;* Is it a broadcast address?
; (Network mask OR address = all ones)
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:
endif
;--- Check if datagram must be captured
ld a,(PCAP_STATE) ;Capture requested?
cp 1
jr nz,NO_PCAP
ld a,(PCAP_PROTO) ;Capture all?
or a
jr z,DO_PCAP
ld b,a ;Protocol matches?
ld a,(IN_BUFFER+13)
ld d,a
cp b
jr z,DO_PCAP
ld a,b
cp #FF ;All except UDP, TCP and ICMP echo?
jr nz,NO_PCAP
;Check if it is UDP, TCP or ICMP echo
ld a,d
cp 6 ;TCP
jr z,NO_PCAP
cp 17 ;UDP
jr z,NO_PCAP
cp 1 ;ICMP
jr nz,DO_PCAP
ld a,(IN_BUFFER+24)
or a ;ECHO Reply?
jr z,NO_PCAP
cp 8 ;ECHO request?
jp z,NO_PCAP
;Capture datagram
DO_PCAP:
ld a,(IN_BUFFER+6)
ld b,a
ld a,(IN_BUFFER+7)
ld c,a
ld (PCAP_SIZE),bc
ld hl,IN_BUFFER+4
ld de,PCAP_BUFFER
ldir
ld a,2
ld (PCAP_STATE),a
jp END_GET_PACK
NO_PCAP:
;--- If version field is not 4, ignore datagram
ld a,(IN_BUFFER+4)
and #F0
cp #40
jp nz,END_GET_PACK
;--- If it is a datagram fragment, ignore it
ld hl,(IN_BUFFER+10)
res 7,l ;Reset unused bit (prevents future extensions of IP)
res 6,l ;Reset DF bit. Now HL has only DF & Fragment offset bits.
ld a,h ;If MF or Fragment Offset are set,
or l ;it is a fragment: ignore it
jp nz,END_GET_PACK
;--- If the datagram contains options, discard them
; (move data to IN_BUFFER+24)
ld hl,(IP_HEADER_LEN) ;HL = Header length
ld de,20
call COMP
jr z,IPOPTOK
ld bc,IN_BUFFER+4
add hl,bc ;HL = Start of data
ld de,IN_BUFFER+24
ld bc,(IP_TOTAL_LEN)
ldir
ld hl,(IP_TOTAL_LEN) ;Change datagram size
ld de,(IP_HEADER_LEN) ;to not include IP options
or a
sbc hl,de ;HL = Size without headers
ld de,20
add hl,de ;HL = Size with header and without options
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:
if LINK=0
;--- If it was VJ Uncompressed_TCP,
; copy headers to the appropriate slot.
ld a,(VJ_RCVED)
cp 1
jr nz,NO_VJU_RCV
ld a,(IN_BUFFER+36)
push af
and #F0
or #50 ;Save with TCP header size=20
ld (IN_BUFFER+36),a
ld a,(VJ_CUR_ISLOT)
call GET_VJ_INSLOT
ld (VJ_ISLOT_DIR),hl
inc hl
inc hl
inc hl
inc hl
ex de,hl
ld hl,IN_BUFFER+4
ld bc,40
ldir
pop af
ld (IN_BUFFER+36),a
NO_VJU_RCV:
endif
;--- Substitute total datagram size field for
; data size, in little-endian
ld hl,(IP_TOTAL_LEN)
ld de,(IP_HEADER_LEN)
or a
sbc hl,de
ld (IN_BUFFER+6),hl
;--- Decide action according to transported protocol
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 ;Unknown: discard datagram
;Note: IS_xxx routines always terminate with JP END_GET_PACK
;--- Process the next TCP connection
END_GET_PACK:
call IS_CONNECTED
ret nz
jp DO_TCP_PROCESS
;===============================
;=== ICMP message received ===
;===============================
IS_ICMP:
;--- Check 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:
;--- Check type
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 ;Other: ignore it
;--- ICMP Echo Request: Reply iy if REPLYECHO<>0
IS_ICMP_EREQ:
ld a,(REPLYECHO)
or a
jp z,END_GET_PACK
ld hl,IN_BUFFER+24 ;Copy message verbosely
ld de,OUT_BUFFER+24
ld bc,(IN_BUFFER+6)
push bc
inc bc ;To include padding zero
ldir
xor a
ld (OUT_BUFFER+24),a ;Change type to "Echo reply"
pop bc
ld hl,0
ld (OUT_BUFFER+26),hl ;Delete old checksum
ld ix,OUT_BUFFER+24
call CALC_CHKSUM
ld (OUT_BUFFER+26),de ;Calculate new checksum
ld hl,(IN_BUFFER+16) ;Destination IP = message sender
ld de,(IN_BUFFER+18)
ld bc,(IN_BUFFER+6) ;Length
ld a,1 ;Protocol = ICMP
jp SEND_IP
;--- ICM Echo Reply: queue it if there is enough room
; (there is space for 4 messages to be stored)
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 ;Now IX points to the appropriate data area
ld a,(ICMPI_PINDEX)
ld b,a
ld a,(ICMPI_GINDEX)
cp b
jr nz,EREP_OKROOM
ld a,(ix) ;GET index = PUT index:
or (ix+1) ;There is room if the entry is empty (IP=0),
or (ix+2) ;otherwise, terminate.
or (ix+3)
jp nz,END_GET_PACK
EREP_OKROOM: ;
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) ;Identifier (we store in little-endian)
ld (ix+5),h
ld (ix+6),l
ld hl,(IN_BUFFER+30) ;Sequence number (we store in little-endian)
ld (ix+7),h
ld (ix+8),l
ld hl,(IN_BUFFER+6) ;Data length
ld bc,8 ;Substracts ICMP header length
or a
sbc hl,bc
ld (ix+9),l
ld (ix+10),h
ld a,(ICMPI_PINDEX) ;Increase PUT index,
inc a ;jumping from 3 to 0 if necessary
and %111
ld (ICMPI_PINDEX),a
jp END_GET_PACK
;--- ICMP Destination Unreachable: if it refers to an
; existing TCP connection, abort connection with error code 7.
;Nota: the header of the packet that caused this message
;ICMP to be returned is at IN_BUFFER+32.
IS_ICMP_HOSTUN:
ld a,(IN_BUFFER+9+32)
cp 6 ;If the offending packet it not TCP, ignore it
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 ;Not referring to an existing TCP connection: terminate
jp z,END_GET_PACK
call LOAD_TCB
xor a ;Close connection with error code 7
ld (TCP_STATE),a
ld a,7
ld (LAST_CLOSE),a
call SAVE_TCB
jp END_GET_PACK
;=============================
;=== UDP packet received ===
;=============================
IS_UDP:
;--- Check checksum, unless CHKVECT says no
ld a,(CHKVECT)
and %1000
jp z,OK_UDP_CHKSUM
;* To calculate checksum, compose pseudo-header
; immediately before data, overwriting the IP header.
; But since the order of the 16-bit words is not meaningful
; for the checksum calculation,
; we recycle the IP addresses and the protocol field already set
; on the IP header; therefore we only need to set 0 on the TTL field
; and the UDP length on the IP checksum field.
xor a
ld (IN_BUFFER+12),a ;TTL field
ld hl,(IN_BUFFER+28) ;UDP length on the IP checksum field
ld (IN_BUFFER+14),hl
ld hl,(IN_BUFFER+6)
ld bc,12 ;To include pseudo-header
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 OK: check if it is a DNS reply or a DHCP packet,
; in this case jump to the appropriate processing code
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
if LINK=1
ld de,68
call COMP
jp z,IS_DHCP
endif
;--- Check if the packet can be stored
; (there is space for 8 packets to be stored)
ld hl,(IN_BUFFER+6) ;Too big?
ld de,556+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 ;Now IX points to the appropriate buffer
ld a,(UDPI_PINDEX)
ld b,a
ld a,(UDPI_GINDEX)
cp b
jr nz,UDPI_HAYSITIO
ld a,(ix) ;GET index = PUT index:
or (ix+1) ;There is room if the entry is empty (IP=0),
or (ix+2) ;otherwise, terminate.
or (ix+3)
jp nz,END_GET_PACK
UDPI_HAYSITIO:
;--- Store packet
ld hl,(IN_BUFFER+16) ;Source IP
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) ;Source port (little endian)
ld (ix+4),h
ld (ix+5),l
ld hl,(IN_BUFFER+26) ;Destination port (little endian)
ld (ix+6),h
ld (ix+7),l
ld hl,(IN_BUFFER+6) ;UDP data length
ld bc,8
or a
sbc hl,bc
ld (ix+8),l
ld (ix+9),h
ld a,h ;Copy data part unless its length is 0
or l
jr z,OK_UDPIDATA
push hl
ld hl,UDP_BUFFERS-556
ld de,556
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 ;Update index for the next packet
jp END_GET_PACK
;--- This subroutine increases UDPI_PINDEX or UDPI_GINDEX (address passed in HL).
; Wraps from 7 to 0, or from 6 to 0 if there is a captured packet
; or a packet capture is pending.
; Modifies: AF, BC, HL
INC_UDPINDEX: ld a,(hl)
inc a
and %111
ld (hl),a
ld b,a
ld a,(PCAP_STATE)
or a
ret z
ld a,b
cp 7
ret nz
ld a,6
ld (hl),a
ret
;=============================
;=== DNS packet received ===
;=============================
IS_DNS:
;--- Check if there is a query in progress
; and the response ID matches the last sent query ID;
; if not, discard packet.
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
;--- If the response is truncated, terminate with error 21
; unless we can try with secondary server.
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:
;--- If the response contains any error, set it and terminate,
; unless we can try with secondary server.
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:
;--- The response does not contain any error.
; Check if it contains any valid resource information.
ld ix,IN_BUFFER+32 ;Point to packet start DNS
ld h,(ix+6)
ld l,(ix+7)
ld (ANCOUNT),hl ;Resource count
ld h,(ix+8)
ld l,(ix+9)
ld (NSCOUNT),hl ;Authoritative servers count
ld h,(ix+10)
ld l,(ix+11)
ld (ARCOUNT),hl ;Additional sections count
ld ix,IN_BUFFER+44 ;IX=Start of query
SKIPQ_LOOP:
ld a,(ix) ;Skip query
inc ix ;(QNAME field)
or a ;checking for compression
jr z,SKIPQ_LOOP3
bit 7,a
jr z,SKIPQ_LOOP
SKIPQ_LOOP2:
inc ix ;Skip QTYPE and QCLASS
SKIPQ_LOOP3:
inc ix ;(plus the second byte of pointer if necessary)
inc ix
inc ix
inc ix ;Now IX points to the resources
;* Check if the requested address was already provided
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 ;If valid resource present,
ld (DNS_STAT_P),a ;set status=2 and terminate
xor a
ld (DNS_STAT_S),a
jp END_GET_PACK
;* Otherwise, chek for other DNS servers IPs
; in "Authoritative" or in "Aditional"
SCAN_FOR_NS:
ld bc,(NSCOUNT)
ld a,b
or c
jr z,SCANNS_FAILED ;Empty NS section?
call SCAN_DNS_RR ;Search server IPs
or a ;in "authoritative"
jp nz,CHANGE_DNS_IP
ld bc,(ARCOUNT)
ld a,b
or c
jr z,SCANNS_FAILED ;Empty AR section?
call SCAN_DNS_RR ;Search server IPs
or a ;in "additional"
jp nz,CHANGE_DNS_IP
SCANNS_FAILED:
ld a,3 ;If both NS and AR are empty, error 20
ld (DNS_STAT_P),a
ld a,20
ld (DNS_STAT_S),a
jp END_GET_PACK
;* The IP of other DNS server has been found:
; Repeat query using this address.
CHANGE_DNS_IP:
ld hl,DNS_RESULT
ld de,DNS_IP ;Set new DNS server address
ld bc,4
ldir
ld hl,(ID_DNS) ;Increase identifier
inc hl
ld (ID_DNS),hl
ld a,3 ;Set secondary state to 3
ld (DNS_STAT_S),a
xor a ;Initialize retry count
ld (DNS_RETRY),a
inc a
ld (DNS_TOUT),a ;This causes the query to be sent immediately
jp END_GET_PACK
;--- This subroutine examinates the zone pointed by IX
; and searches a RR of type "Address IP".
; If found, copies the IP address to DNS_REPLY
; and sets DNS_RESP_FLAG to #FF (returned also in A).
; At the end, IX poins to the next zone.
;
; Input: BC = Count of RRs in the zone.
SCAN_DNS_RR:
xor a
ld (DNS_RESP_FLAG),a
DNS_AN_LOOP:
push bc
SKIPQ_LOOP4:
ld a,(ix) ;Skip name, checking for compression
inc ix ;comprobando if 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) ;If there is a valid reply,
or a ;simply skip RR
jr nz,DNS_AN_LOOP2
;* Check that type is "IP address"
ld h,(ix) ;IX points to TYPE
ld l,(ix+1)
ld de,1
call COMP
jr nz,DNS_AN_LOOP2
;* Answer found: copy it to 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
;* Go to next RR
DNS_AN_LOOP2:
ld bc,10
add ix,bc ;So that it points to RDATA
ld b,(ix-2)
ld c,(ix-1) ;BC = RDLENGTH
add ix,bc
;* If there are RRs left, start again
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 when a reply is found
;--- This subroutine is invoked when an invalid DNS packet
; is received or all retransmissions are exhausted.
; It checks if the DNS server used was the primary one
; and there is a secondary server available.
; If that is the case, set the secondary server address to DNS_IP,
; set DNS_STAT_S to 2 DNS_RETRY to 0 (that is, prepare all to
; repeat the query using the secondary server), and return Cy=0.
; Otherwise, return Cy=1 (error).
DNS_USE_SEC:
ld a,(DNS_STAT_S)
cp 1
scf
ret nz ;Not the primary server
ld ix,BUF_IPDNS1
ld a,(ix+4)
or (ix+5)
or (ix+6)
or (ix+7)
scf
ret z ;It was primary but no secondary available.
ld hl,BUF_IPDNS2 ;Set secondary server
ld de,DNS_IP ;and reset retransmission counter
ld bc,4
ldir
ld a,2
ld (DNS_STAT_S),a
xor a ;This causes Cy=0
ld (DNS_RETRY),a
inc a
ld (DNS_TOUT),a ;This causes the query to be sent immediately
ret
if LINK=1
;==============================
;=== DHCP packet received ===
;==============================
IS_DHCP:
ld a,(DHCP_VECT) ;Ignore it if not using DHCP
or a
jp z,END_GET_PACK
ld a,(DHCP_STATE)
cp CONFIGURED
jp z,END_GET_PACK
;--- Obtain packet type
ld a,(IN_BUFFER+32) ;BOOTREPLY?
cp 2
jp nz,END_GET_PACK
ld hl,IN_BUFFER+36 ;'xid' matches?
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 ;Other types are ignored
;--- DHCPOFFER packet
IS_DHCP_OFFER:
;* If not in SELECTING state, ignore it
ld a,(DHCP_STATE)
cp SELECTING
jp nz,END_GET_PACK
;* Save server identifier
call DHCP_GET_SERVER
jp c,END_GET_PACK
ld de,DHCP_SERVER
ld bc,4
ldir
;* Save 'yiaddr' field as the offered IP address
ld hl,IN_BUFFER+48
ld de,DHCP_YIADDR
ld bc,4
ldir
;* Save received 'xid'
ld hl,IN_BUFFER+36
ld de,DHCP_RCVXID
ld bc,4
ldir
;* Enter REQUESTING state and send DHCPREQUEST
ld a,REQUESTING
ld (DHCP_STATE),a
ld a,DHCPREQUEST
call SEND_DHCP
call DHCP_FIRST
jp END_GET_PACK
;--- ACK packet
IS_DHCP_ACK:
;* If not in REBINDING, REQUESTING, RENEWING
; or INFORMING state, ignore it
ISDHCPACK2:
cp REQUESTING
jr z,ISDHCPACK3
cp RENEWING
jr z,ISDHCPACK3
cp REBINDING
jr z,ISDHCPACK3
cp INFORMING
jp nz,END_GET_PACK
ISDHCPACK3:
;* If server identifier does not match the previous one, ignore message
; (Problem: What if ACK has been received in REBINDING state,
; by a server different from the one used previously?
; For this reason, we'll rely on XID only)
;call DHCP_GET_SERVER
;jp c,END_GET_PACK
;ld de,DHCP_SERVER
;call COMP32
;jp nz,END_GET_PACK
;* Initialize fields Lease, T1 and T2
ld hl,DHCP_T1
ld de,DHCP_T1+1
ld bc,12-1
ld (hl),0
ldir
;* Save 'yiaddr' field as the assigned IP,
; unless we had sent 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:
;* Traverse all options and precess them
call DHCP_INIT_OP
ISDHCPACKL:
call DHCP_NEXT_OP
or a
jp z,ISDHCPACKEND ;No more options?
;* T1: Store it
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: Store it
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: Store it
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:
;* Subnet mask: store it if we had requested it
cp 1
jr nz,ISDHCPACK_NOSB
ld a,(DHCP_VECT)
and %10 ;Requested?
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:
;* Default gateway: store it if we had requested it
cp 3
jr nz,ISDHCPACK_NOGW
ld a,(DHCP_VECT)
and %100 ;Requested?
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:
;* DNS servers: store them if we had requested them
cp 6
jr nz,ISDHCPACK_NODN
ld a,(DHCP_VECT)
and %1000 ;Requested?
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 ;More than one DNS supplied?
ld a,b
cp 8
jp c,ISDHCPACKL
ld de,BUF_IPDNS2
ld bc,4
ldir
jp ISDHCPACKL
ISDHCPACK_NODN:
;* ARP timeout: store it if we had requested it
cp 35
jr nz,ISDHCPACK_NOAT
ld a,(DHCP_VECT)
and %10000 ;Requested?
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:
;* Ethernet frame type: store it if we had requested it
cp 36
jr nz,ISDHCPACK_NOFT
ld a,(DHCP_VECT)
and %100000 ;Requested?
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:
;* Unknown option: ignore it
jp ISDHCPACKL
ISDHCPACKEND:
;* No more options.
; In INFORMING state, enter CONFIGURED state and terminate.
ld a,(DHCP_STATE)
cp INFORMING
jr nz,ISDHCPACKEND0
ld a,CONFIGURED
ld (DHCP_STATE),a
jp END_GET_PACK
ISDHCPACKEND0:
;* If T1 is 0, set it to lease/2
ld hl,DHCP_T1
ld de,ZERO32
call COMP32
jr nz,OKT1NZ
ld hl,#FFFF ;If lease is infinite, T1 infine
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:
;* If T2 is 0, set it to 0.875*lease ((7/8)*lease)
ld hl,DHCP_T2
ld de,ZERO32
call COMP32
jr nz,OKT2NZ
ld hl,#FFFF ;If lease is infinite, T2 infine
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 by 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 ;Multiply (T2/8) by 7
ld hl,IN_BUFFER
ld de,DHCP_T2
ld bc,DHCP_T2
call ADD32
pop bc
djnz T2ISZL
OKT2NZ:
;* End: enter BOUND and terminate
ld a,BOUND
ld (DHCP_STATE),a
jp END_GET_PACK
;--- NAK packet
IS_DHCP_NAK:
;* In BOUND, SELECTING, INFORMING or CONFIGURED states, ignore it
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
;* In other states, return to INIT
xor a ;ld a,INIT
ld (DHCP_STATE),a
jp END_GET_PACK
endif
;==============================
;=== TCP segment received ===
;==============================
IS_TCP:
if LINK=0
ld a,#FF
ld (GOT_TCP),a
endif
;--- Check checksum, unless CHKVECT says no
ld a,(CHKVECT)
and %100
jp z,OK_TCP_CHKSUM
;For ckecksum calculation we use the same trick as for UDP
;(see IS_UDP)
xor a
ld (IN_BUFFER+12),a ;TTL field
ld ix,IN_BUFFER+14
ld hl,(IN_BUFFER+6) ;TCP length in the IP checksum field
ld (ix),h
ld (ix+1),l
;ld hl,(IN_BUFFER+6)
ld bc,12 ;To include pseudo-header
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_TCP_CHKSUM:
;--- If the segment contains TCP options, search MSS;
; then discard all others and move TCP data to IN_BUFFER+44.
; If no MSS option is present, assume 512.
ld de,536
ld (RECEIVED_MSS),de
ld a,(IN_BUFFER+36)
and #F0
srl a
srl a
ld l,a
ld h,0 ;HL = TCP header size in bytes
cp 20
jr z,TCPOPTOK ;No options present
push hl
ld a,(IN_BUFFER+37) ;Do not bother searching MSS option
and %10 ;if the segment has no SYN
jr z,TCP_OP_END2
;* Search MSS option
ld a,l
sub 20
ld b,a ;B = Options size
ld ix,IN_BUFFER+44 ;Options start
TCP_OP_LOOP:
ld a,(ix)
or a
jr z,TCP_OP_END ;"End of options" option
inc ix
dec b
jr z,TCP_OP_END ;No more options
dec a
jr z,TCP_OP_LOOP ;Option 1 ("Padding")
dec a
jr nz,TCP_OP_NEXT ;Option 2 (MSS)
;MSS option found
ld h,(ix+1)
ld l,(ix+2)
ld a,h ;If MSS=0 is announced, assume 1
or l
jr nz,NO_MSS_0
ld hl,1
NO_MSS_0:
ld (RECEIVED_MSS),hl
jr TCP_OP_END ;Ignore all other options
;Other option: ignore it
TCP_OP_NEXT:
ld a,(ix) ;Option length
dec a ;Do not count option code (already skipped)
TCP_NXT_LOOP:
inc ix
dec b
dec a
jr nz,TCP_NXT_LOOP
;Next option, if any
TCP_OP_NEXT2:
ld a,b ;If less than 4 bytes remaining,
cp 4 ;there can't be MSS option
jr nc,TCP_OP_LOOP
TCP_OP_END:
;* Move data to segment start
pop hl
push hl
TCP_OP_END2:
ld bc,IN_BUFFER+44
add hl,bc ;HL = Data area start
ld de,IN_BUFFER+44
ld bc,(IP_TOTAL_LEN)
ldir ;Move data immediately after IP header
pop hl
TCPOPTOK:
push hl
pop bc ;Update data size (subtract TCP header)
ld hl,(IN_BUFFER+6)
or a
sbc hl,bc
ld (IN_BUFFER+6),hl
if LINK=0
ld a,(VJ_RCVED) ;If it was a VJ compressed packet,
or a ;save data size in its slot
jr z,TCPOPTOK2
ld ix,(VJ_ISLOT_DIR)
ld (ix),l
ld (ix+1),h
TCPOPTOK2:
endif
;--- Change window information to be little-endian
ld a,(IN_BUFFER+38)
ld h,a
ld a,(IN_BUFFER+39)
ld l,a
ld (IN_BUFFER+38),hl
;--- Search associated connection
call SRCH_TCP_INBUF
cp #FF
jr nz,TCP_OKEX
;If associated connection is not found, and the segment has SYN,
;search a connection in LISTEN state with remote socket unespecified
ld a,(IN_BUFFER+37) ;Has SYN?
and %10
jr z,TCP_NOEX
call SEARCH_LISTEN ;Appropriate connection exists?
cp #FF
jr z,TCP_NOEX
call LOAD_TCB
ld hl,IN_BUFFER+16 ;Set remote IP as the datagram source IP
ld de,REMOTE_IP
ld bc,4
ldir
ld hl,(IN_BUFFER+24) ;Set ports as the packet ports
ld de,(IN_BUFFER+26)
ld ix,TCB_TEMP
ld (ix+5),h
ld (ix+6),l
ld (ix+7),d
ld (ix+8),e
jr TCP_OKEX2
;Associated connection not found and segment has no SYN:
;sent RST and terminate
TCP_NOEX:
ld a,(IN_BUFFER+37)
bit 2,a
jp nz,END_GET_PACK ;If it has RST, terminate
and %10000 ;RST type to be sent depends on whether
ld a,1 ;the segment has ACK or not
jp z,TCP_SNDRST
inc a
TCP_SNDRST:
call SEND_RST
jp END_GET_PACK
TCP_OKEX:
call LOAD_TCB
TCP_OKEX2: ;
;>>> Processing in LISTEN state <<<
ld a,(TCP_STATE)
cp LISTEN
jp nz,TCP_NO_LISTEN
;--- If it has RST, ignore it
ld a,(IN_BUFFER+37)
bit 2,a
jp nz,END_GET_PACK
;--- If it has ACK, send RST
bit 4,a
jr z,TCP_LST_NOACK
ld a,2
call SEND_RST
jp END_GET_PACK
TCP_LST_NOACK:
;--- If it has NO SYN, ignore it;
; otherwise process it
bit 1,a
jp z,END_GET_PACK
ld hl,(RECEIVED_MSS)
ld (MSS),hl
ld hl,IN_BUFFER+28 ;Do RCV_NXT = SEG_SEQ+1
ld bc,RCV_NXT
call INC32
ld hl,IN_BUFFER+28 ;Do IRS = SEG_SEQ
ld de,IRS
ld bc,4
ldir
;> Here we should set ISS, but it is 0 already
;ld hl,ZERO32
;ld de,ISS
;ld bc,4
;ldir
ld hl,ISS ;Do SND_NXT = ISS+1
ld bc,SND_NXT
call INC32
ld hl,ISS ;Do SND_UNA = ISS
ld de,SND_UNA
ld bc,4
ldir
ld hl,ISS ;Prepare segment to be sent: sequence number
ld de,OUT_BUFFER+28
ld bc,4
ldir
ld hl,RCV_NXT ;Prepare ACK
ld de,OUT_BUFFER+32
ld bc,4
ldir
ld a,%10010
ld (OUT_BUFFER+37),a ;ACK + SYN
call SET_TCP_PORTS ;Send segment
ld bc,0
call SEND_SEGMENT
ld a,SYN.RECEIVED ;Update the rest of TCB and terminate
ld (TCP_STATE),a
ld hl,(USER_TOUT_V)
ld (USER_TOUT),hl
ld a,#FF
ld (SYN_SENT_FLAG),a
ld hl,RTO_V*60
ld (RTO_T),hl
jp END_GET_TCP
TCP_NO_LISTEN:
;>>> Processing in SYN-SENT state <<<
;ld a,(TCP_STATE)
cp SYN.SENT
jp nz,TCP_NO_SYNS
;--- Check ACK:
; if SEG_ACK<=ISS or SEG_ACK>SND_NXT, send RST
; (OK if ISS<SEG_ACK<=SND_NXT)
ld a,(IN_BUFFER+37)
and %10000
ld b,0
jr z,TCP_SYNS_OKACK
ld de,IN_BUFFER+32 ;Error if SEG_ACK<=ISS
ld hl,ISS
call COMP_SEQ
jr c,TCP_SYNS_BADACK
ld hl,SND_NXT ;OK if SND_NXT>=SEG_ACK
ld de,IN_BUFFER+32
call COMP_SEQ
ld b,1
jr c,TCP_SYNS_OKACK
;Invalid ACK:
;send reset unless segment has already one
TCP_SYNS_BADACK:
ld a,(IN_BUFFER+37)
and %100
ld a,2
call z,SEND_RST
jp END_GET_PACK
TCP_SYNS_OKACK:
;--- Check RST: if present, discard segment
; if it had no ACK (B=0);
; otherwise (B=1) close connection.
ld a,(IN_BUFFER+37)
bit 2,a
jr z,TCP_SYNS_OKRST
bit 0,b
jp z,END_GET_PACK
xor a
ld (TCP_STATE),a
ld a,3
ld (LAST_CLOSE),a
jp END_GET_TCP
TCP_SYNS_OKRST:
;--- Check SYN
and %10 ;Discard segment if it has no SYN
jp z,END_GET_PACK
ld hl,(RECEIVED_MSS)
ld (MSS),hl
ld hl,IN_BUFFER+28 ;Do RCV_NXT=SEG_SEQ+1
ld bc,RCV_NXT
call INC32
ld hl,IN_BUFFER+28 ;Do IRS=SEG_SEQ
ld de,IRS
ld bc,4
ldir
ld a,(IN_BUFFER+37) ;Do SND_UNA=SEG_ACK if ACK present
and %10000
jr z,TCP_SYNS_OKACK2
ld hl,IN_BUFFER+32
ld de,SND_UNA
ld bc,4
ldir
TCP_SYNS_OKACK2:
ld de,SND_UNA ;Compare SND_UNA and ISS
ld hl,ISS
call COMP_SEQ
jr c,TCP_SYNS_2
;Processing if SND_UNA>ISS
ld a,ESTABLISHED
ld (TCP_STATE),a
xor a
ld (SYN_SENT_FLAG),a
call SET_WNDWL
call SEND_ACK
jp END_GET_TCP
;Processing if SND_UNA<=ISS
TCP_SYNS_2:
ld a,SYN.RECEIVED
ld (TCP_STATE),a
ld a,#12
call SEND_SYN
jp END_GET_TCP
TCP_NO_SYNS:
;>>> Check sequence number <<<
call CHECK_SEQ
jp nc,END_GET_TCP ;???
;>>> Check RST flag <<<
ld a,(IN_BUFFER+37)
bit 2,a
jr z,TCP_NO_RST
;--- SYN-RECEIVED state and connection was passive:
; return to LISTEN state
call RET_LISTEN
jp c,END_GET_TCP
;--- Other states or active connection:
; close connection
SET_CLOSED:
xor a
ld (TCP_STATE),a
ld a,3
ld (LAST_CLOSE),a
jp END_GET_TCP
TCP_NO_RST:
;>>> Check SYN flag <<<
;ld a,(IN_BUFFER+37)
bit 1,a
jr z,TCP_NO_SYN
;--- SYN-RECEIVED state and connection is passive:
; send RST and return to LISTEN state,
; Other states or active connection:
; close connection
call RET_LISTEN
jp c,END_GET_TCP
xor a
call SEND_RST
jp SET_CLOSED
TCP_NO_SYN:
;>>> Check ACK flag <<<
;--- If no ACK present, ignore segment
;ld a,(IN_BUFFER+37)
and %10000
jp z,END_GET_PACK
;--- SYN-RECEIVED state:
; If SND.UNA =< SEG.ACK =< SND.NXT, enter ESTABLISHED state
; and call SET_WNDWL. Otherwise, send ACK.
ld a,(TCP_STATE)
cp SYN.RECEIVED
jr nz,TCP_NO_SYNR
ld hl,SND_UNA
ld bc,IN_BUFFER+32
ld de,SND_NXT
call RANGE32
jr c,OK_ACK_RANGE
call SEND_ACK ;Not acceptable ACK
jp END_GET_TCP
OK_ACK_RANGE:
ld a,ESTABLISHED ;Acceptable ACK
ld (TCP_STATE),a
xor a
ld (SYN_SENT_FLAG),a
call SET_WNDWL
ld hl,IN_BUFFER+32
ld de,SND_UNA
ld bc,4
ldir
jp END_GET_TCP
TCP_NO_SYNR:
;--- Process all other states
call PROCESS_ACK
jp nc,END_GET_TCP
;--- FIN_WAIT_1: If FIN is ACKed, enter FIN-WAIT-2 state
ld a,(TCP_STATE)
cp FIN.WAIT.1
jr nz,TCP_NO_FW1
ld a,(FIN_SENT_FLAG)
or a
jr nz,TCP_NO_FW1
ld a,FIN.WAIT.2
ld (TCP_STATE),a
jr TCP_NO_CLLA
TCP_NO_FW1:
;--- CLOSING (8) and LAST-ACK (9): If FIN is ACKed,
; close connection.
;NOTE: We should go from CLOSING to TIME-WAIT,
; but we haven't implemented this state.
or 1
cp 9
jr nz,TCP_NO_CLLA
ld a,(FIN_SENT_FLAG)
or a
jp nz,END_GET_TCP
TCP_TO_CLOSED:
xor a
ld (TCP_STATE),a
ld a,1
ld (LAST_CLOSE),a
jp END_GET_TCP
TCP_NO_CLLA:
;>>> Processing of segment data <<<
;--- If not in ESTABLISHED (4), FIN_WAIT_1 (5) or FIN_WAIT_2 (6),
; ignore segment data
ld a,(TCP_STATE)
cp ESTABLISHED
jp c,TCP_OK_TXT
cp FIN.WAIT.2+1
jp nc,TCP_OK_TXT
;--- Queue data and update RCV_NXT
TCP_INSAGAIN:
ld bc,(IN_BUFFER+6)
ld a,b
or c
jr z,TCP_OK_TXT
ld ix,INBUF
ld hl,IN_BUFFER+44
push bc
call RBUF_INSERT
pop de
jr nc,TCP_INSERT_OK
ld bc,(INBUF_FREE) ;In case there is too much data
ld (IN_BUFFER+6),bc
jr TCP_INSAGAIN
TCP_INSERT_OK:
ld hl,RCV_NXT
ld bc,RCV_NXT
call ADD3216
;--- Update variables to send delayed ACK
ld hl,(ACK_RCV_NUM)
ld a,h
or l
jr nz,OK_ACK_TIM
ld a,ACK_DELAY_V
ld (ACK_DELAY_T),a
OK_ACK_TIM:
ld bc,(IN_BUFFER+6)
add hl,bc
ld (ACK_RCV_NUM),hl
TCP_OK_TXT:
;>>> Processing of FIN bit <<<
ld a,(IN_BUFFER+37)
and 1
jr z,TCP_NO_FIN
;--- In LISTEN or SYN-SENT, ignore segment
ld a,(TCP_STATE)
cp LISTEN
jp z,END_GET_TCP
cp SYN.SENT
jp z,END_GET_TCP
;--- Update RCV_NXT and schedule ACK
ld hl,RCV_NXT
ld bc,RCV_NXT
call INC32
call SCHEDULE_ACK
;--- SYN-RECEIVED and ESTABLISHED: enter CLOSE_WAIT state
ld a,(TCP_STATE)
cp ESTABLISHED
jr z,TCP_TO_CW
cp SYN.RECEIVED
jr nz,TCP_NO_CW
TCP_TO_CW: ld a,CLOSE.WAIT
ld (TCP_STATE),a
jp END_GET_TCP
TCP_NO_CW:
;--- FIN-WAIT-1 state: If FIN is ACKed, close connection;
; otherwise enter CLOSING state
cp FIN.WAIT.1
jr nz,TCP_NO_FW1_2
ld a,(FIN_SENT_FLAG)
or a
jp z,TCP_TO_CLOSED
ld a,CLOSING
ld (TCP_STATE),a
jp END_GET_TCP
TCP_NO_FW1_2:
;--- FIN-WAIT-2 state: Close connection
cp FIN.WAIT.2
jp z,TCP_TO_CLOSED
TCP_NO_FIN:
;>>> End <<<
END_GET_TCP:
call SAVE_TCB
jp END_GET_PACK