;
;    DNS Domain name -> IP lookup
;
;    Compile with FASM for Menuet
;


; If you like, you camd change the DNS server default by changing the
; IP address in the dnsServer string.


; Enabling debugging puts the received response to the
; debug board
DEBUGGING_ENABLED	    equ     1
DEBUGGING_DISABLED	    equ     0
DEBUGGING_STATE 	    equ     DEBUGGING_DISABLED


use32
 org	0x0
 db	'MENUET01'    ; header
 dd	0x01	      ; header version
 dd	START	      ; entry point
 dd	I_END	      ; image size
 dd	I_END+0x10000 ; required memory
 dd	I_END+0x10000 ; esp
 dd	0x0 , 0x0     ; I_Param , I_Path

include 'lang.inc'
include 'macros.inc'

START:					    ; start of execution
    mov     eax,40			    ; Report events
    mov     ebx,10000111b		    ; Stack 8 + defaults
    int     0x40

    mov     dword [prompt], p1
    mov     dword [promptlen], p1len - p1   ; 'waiting for command'

red:
    call    draw_window 		    ; at first, draw the window

still:
    mov     eax,10			    ; wait here for event
    mcall

    cmp     eax,1			    ; redraw request ?
    jz	    red
    cmp     eax,2			    ; key in buffer ?
    jz	    key
    cmp     eax,3			    ; button in buffer ?
    jz	    button

    jmp     still
key:				; Keys are not valid at this part of the
    mov     eax,2		; loop. Just read it and ignore
    mcall
    jmp     still

button: 			; button
    mov     eax,17		; get id
    mcall

    cmp     ah,1		; button id=1 ?
    jnz     noclose

    ; close socket before exiting
    mov     eax, 53
    mov     ebx, 1
    mov     ecx, [socketNum]
    mcall

    mov     eax,0xffffffff	; close this program
    mcall

noclose:
    cmp     ah,3		; Resolve address?
    jnz     noresolve

    mov     dword [prompt], p5
    mov     dword [promptlen], p5len - p5   ; display 'Resolving'
    call    draw_window

    call    translateData	; Convert domain & DNS IP address

    call    resolveDomain

    jmp     still


noresolve:
    cmp     ah,4
    jz	    f1			; Enter domain name
    cmp     ah,5
    jz	    f2			; enter DNS Server IP
    jmp     still


f1:
    mov     [addr],dword query
    mov     [ya],dword 35
    jmp     rk

f2:
    mov     [addr],dword dnsServer
    mov     [ya],dword 35+16

rk:
    mov     ecx,26
    mov     edi,[addr]
    mov     al,' '
    rep     stosb

    call    print_text

    mov     edi,[addr]

f11:
    mov     eax,10
    mcall
    cmp     eax,2
    jz	    fbu
    jmp     still

fbu:
    mov     eax,2
    mcall  ; get key
    shr     eax,8
    cmp     eax,8
    jnz     nobs
    cmp     edi,[addr]
    jz	    f11
    sub     edi,1
    mov     [edi],byte ' '
    call    print_text
    jmp     f11

nobs:
    cmp     eax,dword 31
    jbe     f11
    cmp     eax,dword 95
    jb	    keyok
    sub     eax,32

keyok:
    mov     [edi],al

    call    print_text

    add     edi,1
    mov     esi,[addr]
    add     esi,26
    cmp     esi,edi
    jnz     f11

    jmp  still



print_text:
    mov     eax,13
    mov     ebx,103*65536+26*6
    mov     ecx,[ya]
    shl     ecx,16
    mov     cx,8
    mov     edx,0x224466
    mcall

    mov     eax,4
    mov     ebx,103*65536
    add     ebx,[ya]
    mov     ecx,0xffffff
    mov     edx,[addr]
    mov     esi,26
    mcall

    ret



;***************************************************************************
;   Function
;      translateData
;
;   Description
;      Coverts the domain name and DNS IP address typed in by the user into
;      a format suitable for the IP layer.
;
;    The ename, in query, is converted and stored in dnsMsg
;      The DNS ip, in dnsServer, is converted and stored in dnsIP
;
;***************************************************************************
translateData:

    ; first, get the IP address of the DNS server
    ; Then, build up the request string.

    xor     eax, eax
    mov     dh, 10
    mov     dl, al
    mov     [dnsIP], eax

    mov     esi, dnsServer
    mov     edi, dnsIP

    mov     ecx, 4

td003:
    lodsb
    sub     al, '0'
    add     dl, al
    lodsb
    cmp     al, '.'
    je	    ipNext
    cmp     al, ' '
    je	    ipNext
    mov     dh, al
    sub     dh, '0'
    mov     al, 10
    mul     dl
    add     al, dh
    mov     dl, al
    lodsb
    cmp     al, '.'
    je	    ipNext
    cmp     al, ' '
    je	    ipNext
    mov     dh, al
    sub     dh, '0'
    mov     al, 10
    mul     dl
    add     al, dh
    mov     dl, al
    lodsb

ipNext:
    mov     [edi], dl
    inc     edi
    mov     dl, 0
    loop    td003

    ; Build the request string


    mov     eax, 0x00010100
    mov     [dnsMsg], eax
    mov     eax, 0x00000100
    mov     [dnsMsg+4], eax
    mov     eax, 0x00000000
    mov     [dnsMsg+8], eax

    ; domain name goes in at dnsMsg+12
    mov     esi, dnsMsg + 12	    ; location of label length
    mov     edi, dnsMsg + 13	    ; label start
    mov     edx, query
    mov     ecx, 12		     ; total string length so far

td002:
    mov     [esi], byte 0
    inc     ecx

td0021:
    mov     al, [edx]
    cmp     al, ' '
    je	    td001		    ; we have finished the string translation
    cmp     al, '.'		    ; we have finished the label
    je	    td004

    inc     byte [esi]
    inc     ecx
    mov     [edi], al
    inc     edi
    inc     edx
    jmp     td0021

td004:
    mov     esi, edi
    inc     edi
    inc     edx
    jmp     td002



    ; write label len + label text

td001:
    mov     [edi], byte 0
    inc     ecx
    inc     edi
    mov     [edi], dword 0x01000100
    add     ecx, 4

    mov     [dnsMsgLen], ecx

    ret





;***************************************************************************
;   Function
;      resolveDomain
;
;   Description
;       Sends a question to the dns server
;       works out the IP address from the response from the DNS server
;
;***************************************************************************
resolveDomain:
    ; Get a free port number
 mov	 ecx, 1000  ; local port starting at 1000
getlp:
 inc	 ecx
 push ecx
 mov	 eax, 53
 mov	 ebx, 9
 mcall
 pop	 ecx
 cmp	 eax, 0   ; is this local port in use?
 jz  getlp	; yes - so try next

    ; First, open socket
    mov     eax, 53
    mov     ebx, 0
    mov     edx, 53    ; remote port - dns
    mov     esi, [dnsIP]
    mcall

    mov     [socketNum], eax

    ; write to socket ( request DNS lookup )
    mov     eax, 53
    mov     ebx, 4
    mov     ecx, [socketNum]
    mov     edx, [dnsMsgLen]
    mov     esi, dnsMsg
    mcall

    ; Setup the DNS response buffer

    mov     eax, dnsMsg
    mov     [dnsMsgLen], eax

    ; now, we wait for
    ; UI redraw
    ; UI close
    ; or data from remote

ctr001:
    mov     eax,10		   ; wait here for event
    mcall

    cmp     eax,1		   ; redraw request ?
    je	    ctr003
    cmp     eax,2		   ; key in buffer ?
    je	    ctr004
    cmp     eax,3		   ; button in buffer ?
    je	    ctr005


    ; Any data in the UDP receive buffer?
    mov     eax, 53
    mov     ebx, 2
    mov     ecx, [socketNum]
    mcall

    cmp     eax, 0
    je	    ctr001

    ; we have data - this will be the response
ctr002:
    mov     eax, 53
    mov     ebx, 3
    mov     ecx, [socketNum]
    mcall		 ; read byte - block (high byte)

    ; Store the data in the response buffer
    mov     eax, [dnsMsgLen]
    mov     [eax], bl
    inc     dword [dnsMsgLen]


if DEBUGGING_STATE = DEBUGGING_ENABLED
    call debug_print_rx_ip
end if

    mov     eax, 53
    mov     ebx, 2
    mov     ecx, [socketNum]
    mcall		 ; any more data?

    cmp     eax, 0
    jne     ctr002		; yes, so get it

    ; close socket
    mov     eax, 53
    mov     ebx, 1
    mov     ecx, [socketNum]
    mcall

    mov     [socketNum], dword 0xFFFF

    ; Now parse the message to get the host IP
    ; Man, this is complicated. It's described in
    ; RFC 1035

    ; 1) Validate that we have an answer with > 0 responses
    ; 2) Find the answer record with TYPE 0001 ( host IP )
    ; 3) Finally, copy the IP address to the display
    ; Note: The response is in dnsMsg
    ;       The end of the buffer is pointed to by [dnsMsgLen]

    ; Clear the IP address text
    mov     [hostIP], dword 0

    mov     esi, dnsMsg

    ; Is this a response to my question?
    mov     al, [esi+2]
    and     al, 0x80
    cmp     al, 0x80
    jne     ctr002a

    ; Were there any errors?
    mov     al, [esi+3]
    and     al, 0x0F
    cmp     al, 0x00
    jne     ctr002a

    ; Is there ( at least 1 ) answer?
    mov     ax, [esi+6]
    cmp     ax, 0x00
    je	    ctr002a

    ; Header validated. Scan through and get my answer

    add     esi, 12		; Skip to the question field

    ; Skip through the question field
    call    skipName
    add     esi, 4		; skip past the questions qtype, qclass

ctr002z:
    ; Now at the answer. There may be several answers,
    ; find the right one ( TYPE = 0x0001 )
    call    skipName
    mov     ax, [esi]
    cmp     ax, 0x0100		; Is this the IP address answer?
    jne     ctr002c

    ; Yes! Point esi to the first byte of the IP address
    add     esi, 10

    mov     eax, [esi]
    mov     [hostIP], eax
    jmp     ctr002a		; And exit...


ctr002c:			; Skip through the answer, move to the next
    add     esi, 8
    movzx   eax, byte [esi+1]
    mov     ah, [esi]
    add     esi, eax
    add     esi, 2

    ; Have we reached the end of the msg?
    ; This is an error condition, should not happen
    cmp     esi, [dnsMsgLen]
    jl	    ctr002z		; Check next answer
    jmp     ctr002a		; abort


ctr002a:
    mov     dword [prompt], p4	; Display IP address
    mov     dword [promptlen], p4len - p4
    call    draw_window

    jmp     ctr001

ctr003: 			; redraw
    call    draw_window
    jmp     ctr001

ctr004: 			; key
    mov     eax,2		; just read it and ignore
    mcall
    jmp     ctr001

ctr005: 			; button
    mov     eax,17		; get id
    mcall

    ; close socket
    mov     eax, 53
    mov     ebx, 1
    mov     ecx, [socketNum]
    mcall

    mov     [socketNum], dword 0xFFFF
    mov     [hostIP], dword 0

    mov     dword [prompt], p1
    mov     dword [promptlen], p1len - p1   ; 'waiting for command'

    call    draw_window 		    ; at first, draw the window

    ret



;***************************************************************************
;   Function
;      skipName
;
;   Description
;       Increment esi to the first byte past the name field
;       Names may use compressed labels. Normally do.
;       RFC 1035 page 30 gives details
;
;***************************************************************************
skipName:
    mov     al, [esi]
    cmp     al, 0
    je	    sn_exit
    and     al, 0xc0
    cmp     al, 0xc0
    je	    sn001

    movzx   eax, byte [esi]
    inc     eax
    add     esi, eax
    jmp     skipName

sn001:
    add     esi, 2			    ; A pointer is always at the end
    ret

sn_exit:
    inc     esi
    ret


;   *********************************************
;   *******  WINDOW DEFINITIONS AND DRAW ********
;   *********************************************


draw_window:
    mov     eax,12		      ; function 12:tell os about windowdraw
    mov     ebx,1		      ; 1, start of draw
    mcall
				      ; DRAW WINDOW
    mov     eax,0		      ; function 0 : define and draw window
    mov     ebx,100*65536+300	      ; [x start] *65536 + [x size]
    mov     ecx,100*65536+140	      ; [y start] *65536 + [y size]
    mov     edx,0x14224466	      ; color of work area RRGGBB
    mov     edi,title		      ; WINDOW LABEL;
    mcall

    mov     eax,8		      ; Resolve
    mov     ebx,20*65536+190
    mov     ecx,79*65536+15
    mov     edx,3
    mov     esi,0x557799
    mcall

    ;mov     eax,8
    mov     ebx,270*65536+10
    mov     ecx,34*65536+10
    inc     edx
    mcall

    ;mov     eax,8
    mov     ebx,270*65536+10
    mov     ecx,50*65536+10
    inc     edx
    mcall

    ; Copy the file name to the screen buffer
    ; file name is same length as IP address, to
    ; make the math easier later.
    cld
    mov     esi,query
    mov     edi,text+13
    mov     ecx,26
    rep     movsb


    ; copy the IP address to the screen buffer
    mov     esi,dnsServer
    mov     edi,text+40+13
    mov     ecx,26
    rep     movsb

    ; copy the prompt to the screen buffer
    mov     esi,[prompt]
    mov     edi,text+200
    mov     ecx,[promptlen]
    rep     movsb

    ; Re-draw the screen text
    cld
    mov     eax,4
    mov     ebx,25*65536+35	      ; draw info text with function 4
    mov     ecx,0xffffff
    mov     edx,text
    mov     esi,40
newline:
    mcall
    add     ebx,16
    add     edx,40
    cmp     [edx],byte 'x'
    jnz     newline


    ; Write the host IP, if we have one
    mov     eax, [hostIP]
    cmp     eax, 0
    je	    dw001

    ; We have an IP address... display it
    mov     edi,hostIP
    mov     edx,97*65536+115
    mov     esi,0x00ffffff
    mov     ebx,3*65536

ipdisplay:
    mov     eax,47
    movzx   ecx,byte [edi]
    mcall
    add     edx,6*4*65536
    inc     edi
    cmp     edi,hostIP+4
    jb	    ipdisplay

dw001:
    mov     eax,12		      ; function 12:tell os about windowdraw
    mov     ebx,2		      ; 2, end of draw
    mcall

    ret


if DEBUGGING_STATE = DEBUGGING_ENABLED
;****************************************************************************
;    Function
;       debug_print_string
;
;   Description
;       prints a string to the debug board
;
;       esi holds ptr to msg to display
;
;       Nothing preserved; I'm assuming a pusha/popa is done before calling
;
;****************************************************************************
debug_print_string:
    mov     cl, [esi]
    cmp     cl, 0
    jnz     dps_001
    ret

dps_001:
    mov     eax,63
    mov     ebx, 1
    push    esi
    mcall

    inc   word [ind]
    mov  ax, [ind]
    and ax, 0x1f
    cmp  ax, 0
    jne  ds1

    mov   cl, 13
    mov     eax,63
    mov     ebx, 1
    mcall
    mov   cl, 10
    mov     eax,63
    mov     ebx, 1
    mcall


ds1:
    pop     esi
    inc     esi
    jmp     debug_print_string


ind: dw 0
; This is used for translating hex to ASCII for display or output
hexchars db '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
IP_STR		    db	'xx',0


debug_print_rx_ip:
    pusha
    mov     edi, IP_STR

    xor     eax, eax
    mov     al, bl
    shr     al, 4
    mov     ah, [eax + hexchars]
    mov     [edi], ah
    inc     edi

    xor     eax, eax
    mov     al, bl
    and     al, 0x0f
    mov     ah, [eax + hexchars]
    mov     [edi], ah
    mov     esi, IP_STR

    call    debug_print_string
    popa
    ret
end if


; DATA AREA

addr		dd  0x0
ya		dd  0x0

text:

    if lang eq ru
    db '��� ���  : xxxxxxxxxxxxxxx            '
    db 'DNS �ࢥ� : xxx.xxx.xxx.xxx            '
    db '                                        '
    db '       ������� ����                   '
    db '                                        '
    db '                                        '
    db 'x <- END MARKER, DONT DELETE            '

    else
    db 'Host name  : xxxxxxxxxxxxxxx            '
    db 'DNS server : xxx.xxx.xxx.xxx            '
    db '                                        '
    db '     RESOLVE ADDRESS                    '
    db '                                        '
    db '                                        '
    db 'x <- END MARKER, DONT DELETE            '
    end if

if lang eq ru
title	 db   'DNS ������',0

else
title	 db   'DNS Client',0
end if


prompt: dd 0
promptlen: dd 0


if lang eq ru
p1:		db '��� �������                '
p1len:


else
p1:		db 'Waiting for Command        '
p1len:
end if

p4:		db 'IP Address:    .   .   .   '
p4len:

p5:		db 'Resolving...               '
p5len:


dnsServer	db  '194.145.128.1              ' ; iolfree.ie DNS
query		db  'WWW.MENUETOS.NET           '

hostIP: 	dd 0
dnsIP:		dd 0
dnsMsgLen:	dd 0
socketNum:	dd 0xFFFF
dnsMsg:
I_END: