home *** CD-ROM | disk | FTP | other *** search
- ; -*- 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
-