379 lines
7.6 KiB
Plaintext
379 lines
7.6 KiB
Plaintext
; -*- fundamental -*-
|
|
; -----------------------------------------------------------------------
|
|
;
|
|
; Copyright 2004 H. Peter Anvin - All Rights Reserved
|
|
;
|
|
; This program is free software; you can redistribute it and/or modify
|
|
; it under the terms of the GNU General Public License as published by
|
|
; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
|
|
; Bostom MA 02111-1307, USA; either version 2 of the License, or
|
|
; (at your option) any later version; incorporated herein by reference.
|
|
;
|
|
; -----------------------------------------------------------------------
|
|
|
|
;
|
|
; dnsresolv.inc
|
|
;
|
|
; Very simple DNS resolver (assumes recursion-enabled DNS server;
|
|
; this should be the normal thing for client-serving DNS servers.)
|
|
;
|
|
|
|
DNS_PORT equ htons(53) ; Default DNS port
|
|
DNS_MAX_PACKET equ 512 ; Defined by protocol
|
|
; TFTP uses the range 49152-57343
|
|
DNS_LOCAL_PORT equ htons(60053) ; All local DNS queries come from this port #
|
|
DNS_MAX_SERVERS equ 4 ; Max no of DNS servers
|
|
|
|
section .text
|
|
|
|
;
|
|
; Turn a string in DS:SI into a DNS "label set" in ES:DI.
|
|
; On return, DI points to the first byte after the label set,
|
|
; and SI to the terminating byte.
|
|
;
|
|
; On return, DX contains the number of dots encountered.
|
|
;
|
|
dns_mangle:
|
|
push ax
|
|
push bx
|
|
xor dx,dx
|
|
.isdot:
|
|
inc dx
|
|
xor al,al
|
|
mov bx,di
|
|
stosb
|
|
.getbyte:
|
|
lodsb
|
|
and al,al
|
|
jz .endstring
|
|
cmp al,':'
|
|
jz .endstring
|
|
cmp al,'.'
|
|
je .isdot
|
|
inc byte [es:bx]
|
|
stosb
|
|
jmp .getbyte
|
|
.endstring:
|
|
dec si
|
|
dec dx ; We always counted one high
|
|
cmp byte [es:bx],0
|
|
jz .done
|
|
xor al,al
|
|
stosb
|
|
.done:
|
|
pop bx
|
|
pop ax
|
|
ret
|
|
|
|
;
|
|
; Compare two sets of DNS labels, in DS:SI and ES:DI; the one in SI
|
|
; is allowed pointers relative to a packet in DNSRecvBuf.
|
|
;
|
|
; Assumes DS == ES. ZF = 1 if same; no registers changed.
|
|
; (Note: change reference to [di] to [es:di] to remove DS == ES assumption)
|
|
;
|
|
dns_compare:
|
|
pusha
|
|
%if 0
|
|
|
|
.label:
|
|
lodsb
|
|
cmp al,0C0h
|
|
jb .noptr
|
|
and al,03Fh ; Get pointer value
|
|
mov ah,al ; ... in network byte order!
|
|
lodsb
|
|
mov si,DNSRecvBuf
|
|
add si,ax
|
|
jmp .label
|
|
.noptr:
|
|
cmp al,[di]
|
|
jne .done ; Mismatch
|
|
inc di
|
|
movzx cx,al ; End label?
|
|
and cx,cx ; ZF = 1 if match
|
|
jz .done
|
|
|
|
; We have a string of bytes that need to match now
|
|
repe cmpsb
|
|
je .label
|
|
|
|
.done:
|
|
%else
|
|
xor ax,ax
|
|
%endif
|
|
popa
|
|
ret
|
|
|
|
;
|
|
; Skip past a DNS label set in DS:SI.
|
|
;
|
|
dns_skiplabel:
|
|
push ax
|
|
xor ax,ax ; AH == 0
|
|
.loop:
|
|
lodsb
|
|
cmp al,0C0h ; Pointer?
|
|
jae .ptr
|
|
and al,al
|
|
jz .done
|
|
add si,ax
|
|
jmp .loop
|
|
.ptr:
|
|
inc si ; Pointer is two bytes
|
|
.done:
|
|
pop ax
|
|
ret
|
|
|
|
; DNS header format
|
|
struc dnshdr
|
|
.id: resw 1
|
|
.flags: resw 1
|
|
.qdcount: resw 1
|
|
.ancount: resw 1
|
|
.nscount: resw 1
|
|
.arcount: resw 1
|
|
endstruc
|
|
|
|
; DNS query
|
|
struc dnsquery
|
|
.qtype: resw 1
|
|
.qclass: resw 1
|
|
endstruc
|
|
|
|
; DNS RR
|
|
struc dnsrr
|
|
.type: resw 1
|
|
.class: resw 1
|
|
.ttl: resd 1
|
|
.rdlength: resw 1
|
|
.rdata: equ $
|
|
endstruc
|
|
|
|
section .bss
|
|
alignb 2, db 0
|
|
DNSSendBuf resb DNS_MAX_PACKET
|
|
DNSRecvBuf resb DNS_MAX_PACKET
|
|
LocalDomain resb 256 ; Max possible length
|
|
DNSServers resd DNS_MAX_SERVERS
|
|
|
|
section .data
|
|
pxe_udp_write_pkt_dns:
|
|
.status: dw 0 ; Status
|
|
.sip: dd 0 ; Server IP
|
|
.gip: dd 0 ; Gateway IP
|
|
.lport: dw DNS_LOCAL_PORT ; Local port
|
|
.rport: dw DNS_PORT ; Remote port
|
|
.buffersize: dw 0 ; Size of packet
|
|
.buffer: dw DNSSendBuf, 0 ; off, seg of buffer
|
|
|
|
pxe_udp_read_pkt_dns:
|
|
.status: dw 0 ; Status
|
|
.sip: dd 0 ; Source IP
|
|
.dip: dd 0 ; Destination (our) IP
|
|
.rport: dw DNS_PORT ; Remote port
|
|
.lport: dw DNS_LOCAL_PORT ; Local port
|
|
.buffersize: dw DNS_MAX_PACKET ; Max packet size
|
|
.buffer: dw DNSRecvBuf, 0 ; off, seg of buffer
|
|
|
|
LastDNSServer dw DNSServers
|
|
|
|
; Actual resolver function
|
|
; Points to a null-terminated or :-terminated string in DS:SI
|
|
; and returns the name in EAX if it exists and can be found.
|
|
; If EAX = 0 on exit, the lookup failed.
|
|
|
|
section .text
|
|
dns_resolv:
|
|
push ds
|
|
push es
|
|
push di
|
|
push cx
|
|
push dx
|
|
|
|
push cs
|
|
pop es ; ES <- CS
|
|
|
|
; First, build query packet
|
|
mov di,DNSSendBuf+dnshdr.flags
|
|
inc word [es:di-2] ; New query ID
|
|
mov ax,htons(0100h) ; Recursion requested
|
|
stosw
|
|
mov ax,htons(1) ; One question
|
|
stosw
|
|
xor ax,ax ; No answers, NS or ARs
|
|
stosw
|
|
stosw
|
|
stosw
|
|
|
|
call dns_mangle ; Convert name to DNS labels
|
|
|
|
push cs ; DS <- CS
|
|
pop ds
|
|
|
|
push si ; Save pointer to after DNS string
|
|
|
|
; Initialize...
|
|
mov eax,[MyIP]
|
|
mov [pxe_udp_read_pkt_dns.dip],eax
|
|
|
|
and dx,dx
|
|
jnz .fqdn ; If we have dots, assume it's FQDN
|
|
dec di ; Remove final null
|
|
mov si,LocalDomain
|
|
call strcpy ; Uncompressed DNS label set so it ends in null
|
|
.fqdn:
|
|
|
|
mov ax,htons(1)
|
|
stosw ; QTYPE = 1 = A
|
|
stosw ; QCLASS = 1 = IN
|
|
|
|
sub di,DNSSendBuf
|
|
mov [pxe_udp_write_pkt_dns.buffersize],di
|
|
|
|
; Now, send it to the nameserver(s)
|
|
; Outer loop: exponential backoff
|
|
; Inner loop: scan the various DNS servers
|
|
|
|
mov dx,PKT_TIMEOUT
|
|
mov cx,PKT_RETRY
|
|
.backoff:
|
|
mov si,DNSServers
|
|
.servers:
|
|
cmp si,[LastDNSServer]
|
|
jb .moreservers
|
|
|
|
.nomoreservers:
|
|
add dx,dx ; Exponential backoff
|
|
loop .backoff
|
|
|
|
xor eax,eax ; Nothing...
|
|
.done:
|
|
pop si
|
|
pop dx
|
|
pop cx
|
|
pop di
|
|
pop es
|
|
pop ds
|
|
ret
|
|
|
|
.moreservers:
|
|
lodsd ; EAX <- next server
|
|
push si
|
|
push cx
|
|
push dx
|
|
|
|
mov word [pxe_udp_write_pkt_dns.status],0
|
|
|
|
mov [pxe_udp_write_pkt_dns.sip],eax
|
|
mov [pxe_udp_read_pkt_dns.sip],eax
|
|
xor eax,[MyIP]
|
|
and eax,[Netmask]
|
|
jz .nogw
|
|
mov eax,[Gateway]
|
|
.nogw:
|
|
mov [pxe_udp_write_pkt_dns.gip],eax
|
|
|
|
mov di,pxe_udp_write_pkt_dns
|
|
mov bx,PXENV_UDP_WRITE
|
|
call pxenv
|
|
jc .timeout ; Treat failed transmit as timeout
|
|
cmp word [pxe_udp_write_pkt_dns.status],0
|
|
jne .timeout
|
|
|
|
mov cx,[BIOS_timer]
|
|
.waitrecv:
|
|
mov ax,[BIOS_timer]
|
|
sub ax,cx
|
|
cmp ax,dx
|
|
jae .timeout
|
|
|
|
mov word [pxe_udp_read_pkt_dns.status],0
|
|
mov word [pxe_udp_read_pkt_dns.buffersize],DNS_MAX_PACKET
|
|
mov di,pxe_udp_read_pkt_dns
|
|
mov bx,PXENV_UDP_READ
|
|
call pxenv
|
|
and ax,ax
|
|
jnz .waitrecv
|
|
cmp [pxe_udp_read_pkt_dns.status],ax
|
|
jnz .waitrecv
|
|
|
|
; Got a packet, deal with it...
|
|
mov si,DNSRecvBuf
|
|
lodsw
|
|
cmp ax,[DNSSendBuf] ; ID
|
|
jne .waitrecv ; Not ours
|
|
|
|
lodsw ; flags
|
|
xor al,80h ; Query#/Answer bit
|
|
test ax,htons(0F80Fh)
|
|
jnz .badness
|
|
|
|
lodsw
|
|
xchg ah,al ; ntohs
|
|
mov cx,ax ; Questions echoed
|
|
lodsw
|
|
xchg ah,al ; ntohs
|
|
push ax ; Replies
|
|
lodsw ; NS records
|
|
lodsw ; Authority records
|
|
|
|
jcxz .qskipped
|
|
.skipq:
|
|
call dns_skiplabel ; Skip name
|
|
add si,4 ; Skip question trailer
|
|
loop .skipq
|
|
|
|
.qskipped:
|
|
pop cx ; Number of replies
|
|
jcxz .badness
|
|
|
|
.parseanswer:
|
|
mov di,DNSSendBuf+dnshdr_size
|
|
call dns_compare
|
|
pushf
|
|
call dns_skiplabel
|
|
mov ax,[si+8] ; RDLENGTH
|
|
xchg ah,al ; ntohs
|
|
popf
|
|
jnz .notsame
|
|
cmp dword [si],htons(1)*0x10001 ; TYPE = A, CLASS = IN?
|
|
jne .notsame
|
|
cmp ax,4 ; RDLENGTH = 4?
|
|
jne .notsame
|
|
;
|
|
; We hit paydirt here...
|
|
;
|
|
mov eax,[si+10]
|
|
.gotresult:
|
|
add sp,6 ; Drop timeout information
|
|
jmp .done
|
|
|
|
.notsame:
|
|
add si,10
|
|
add si,ax
|
|
loop .parseanswer
|
|
|
|
.badness:
|
|
; We got back no data from this server. Unfortunately, for a recursive,
|
|
; non-authoritative query there is no such thing as an NXDOMAIN reply,
|
|
; which technically means we can't draw any conclusions. However,
|
|
; in practice that means the domain doesn't exist. If this turns out
|
|
; to be a problem, we may want to add code to go through all the servers
|
|
; before giving up.
|
|
|
|
; If the DNS server wasn't capable of recursion, and isn't capable
|
|
; of giving us an authoritative reply (i.e. neither AA or RA set),
|
|
; then at least try a different setver...
|
|
test word [DNSRecvBuf+dnshdr.flags],htons(0480h)
|
|
jz .timeout
|
|
|
|
xor eax,eax
|
|
jmp .gotresult
|
|
|
|
.timeout:
|
|
pop dx
|
|
pop cx
|
|
pop si
|
|
jmp .servers
|