Click here to Skip to main content
15,886,026 members
Articles / Programming Languages / Visual Basic

Certification by Example

Rate me:
Please Sign up or sign in to vote.
4.93/5 (95 votes)
8 Jan 2008CPOL55 min read 185.3K   714   233  
How to prepare a .NET application to obtain the Certified for Windows Vista logo, including the source code (Visual Studio 2005 solution) of a simple but complete application
	;=====================================================
	;===  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

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer SunHotels
Spain Spain
Under the secret identity of a C# programmer, a freaky guy who loves MSX computers and japanese culture is hidden. Beware!

Comments and Discussions