syslinux-3.08-2 sources from FC4
[bootcd.git] / syslinux / dnsresolv.inc
1 ; -*- fundamental -*-
2 ; -----------------------------------------------------------------------
3 ;   
4 ;   Copyright 2004 H. Peter Anvin - All Rights Reserved
5 ;
6 ;   This program is free software; you can redistribute it and/or modify
7 ;   it under the terms of the GNU General Public License as published by
8 ;   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
9 ;   Bostom MA 02111-1307, USA; either version 2 of the License, or
10 ;   (at your option) any later version; incorporated herein by reference.
11 ;
12 ; -----------------------------------------------------------------------
13
14 ;
15 ; dnsresolv.inc
16 ;
17 ; Very simple DNS resolver (assumes recursion-enabled DNS server;
18 ; this should be the normal thing for client-serving DNS servers.)
19 ;
20
21 DNS_PORT        equ htons(53)           ; Default DNS port 
22 DNS_MAX_PACKET  equ 512                 ; Defined by protocol
23 ; TFTP uses the range 49152-57343
24 DNS_LOCAL_PORT  equ htons(60053)        ; All local DNS queries come from this port #
25 DNS_MAX_SERVERS equ 4                   ; Max no of DNS servers
26
27                 section .text
28
29 ;
30 ; Turn a string in DS:SI into a DNS "label set" in ES:DI.
31 ; On return, DI points to the first byte after the label set,
32 ; and SI to the terminating byte.
33 ;
34 ; On return, DX contains the number of dots encountered.
35 ;
36 dns_mangle:
37                 push ax
38                 push bx
39                 xor dx,dx
40 .isdot:
41                 inc dx
42                 xor al,al
43                 mov bx,di
44                 stosb
45 .getbyte:
46                 lodsb
47                 and al,al
48                 jz .endstring
49                 cmp al,':'
50                 jz .endstring
51                 cmp al,'.'
52                 je .isdot
53                 inc byte [es:bx]
54                 stosb
55                 jmp .getbyte
56 .endstring:
57                 dec si
58                 dec dx                  ; We always counted one high
59                 cmp byte [es:bx],0
60                 jz .done
61                 xor al,al
62                 stosb
63 .done:
64                 pop bx
65                 pop ax
66                 ret
67
68 ;
69 ; Compare two sets of DNS labels, in DS:SI and ES:DI; the one in SI
70 ; is allowed pointers relative to a packet in DNSRecvBuf.
71 ;
72 ; Assumes DS == ES.  ZF = 1 if same; no registers changed.
73 ; (Note: change reference to [di] to [es:di] to remove DS == ES assumption)
74 ;
75 dns_compare:
76                 pusha
77 %if 0
78
79 .label:
80                 lodsb
81                 cmp al,0C0h
82                 jb .noptr
83                 and al,03Fh                     ; Get pointer value
84                 mov ah,al                       ; ... in network byte order!
85                 lodsb
86                 mov si,DNSRecvBuf
87                 add si,ax
88                 jmp .label
89 .noptr:
90                 cmp al,[di]
91                 jne .done                       ; Mismatch
92                 inc di
93                 movzx cx,al                     ; End label?
94                 and cx,cx                       ; ZF = 1 if match
95                 jz .done
96                 
97                 ; We have a string of bytes that need to match now
98                 repe cmpsb
99                 je .label
100
101 .done:
102 %else
103                 xor ax,ax
104 %endif
105                 popa
106                 ret
107
108 ;
109 ; Skip past a DNS label set in DS:SI.
110 ;
111 dns_skiplabel:
112                 push ax
113                 xor ax,ax                       ; AH == 0
114 .loop:
115                 lodsb
116                 cmp al,0C0h                     ; Pointer?
117                 jae .ptr
118                 and al,al
119                 jz .done
120                 add si,ax
121                 jmp .loop
122 .ptr:
123                 inc si                          ; Pointer is two bytes
124 .done:
125                 pop ax
126                 ret
127
128                 ; DNS header format
129                 struc dnshdr
130 .id:            resw 1
131 .flags:         resw 1
132 .qdcount:       resw 1
133 .ancount:       resw 1
134 .nscount:       resw 1
135 .arcount:       resw 1
136                 endstruc
137
138                 ; DNS query
139                 struc dnsquery
140 .qtype:         resw 1
141 .qclass:        resw 1
142                 endstruc
143
144                 ; DNS RR
145                 struc dnsrr
146 .type:          resw 1
147 .class:         resw 1
148 .ttl:           resd 1
149 .rdlength:      resw 1
150 .rdata:         equ $
151                 endstruc
152
153                 section .bss
154                 alignb 2, db 0
155 DNSSendBuf      resb DNS_MAX_PACKET
156 DNSRecvBuf      resb DNS_MAX_PACKET
157 LocalDomain     resb 256                ; Max possible length
158 DNSServers      resd DNS_MAX_SERVERS
159
160                 section .data
161 pxe_udp_write_pkt_dns:
162 .status:        dw 0                    ; Status
163 .sip:           dd 0                    ; Server IP
164 .gip:           dd 0                    ; Gateway IP
165 .lport:         dw DNS_LOCAL_PORT       ; Local port
166 .rport:         dw DNS_PORT             ; Remote port
167 .buffersize:    dw 0                    ; Size of packet
168 .buffer:        dw DNSSendBuf, 0        ; off, seg of buffer
169
170 pxe_udp_read_pkt_dns:
171 .status:        dw 0                    ; Status
172 .sip:           dd 0                    ; Source IP
173 .dip:           dd 0                    ; Destination (our) IP
174 .rport:         dw DNS_PORT             ; Remote port
175 .lport:         dw DNS_LOCAL_PORT       ; Local port
176 .buffersize:    dw DNS_MAX_PACKET       ; Max packet size
177 .buffer:        dw DNSRecvBuf, 0        ; off, seg of buffer
178
179 LastDNSServer   dw DNSServers
180
181 ; Actual resolver function
182 ; Points to a null-terminated or :-terminated string in DS:SI
183 ; and returns the name in EAX if it exists and can be found.
184 ; If EAX = 0 on exit, the lookup failed.
185
186                 section .text
187 dns_resolv:
188                 push ds
189                 push es
190                 push di
191                 push cx
192                 push dx
193
194                 push cs
195                 pop es                  ; ES <- CS
196
197                 ; First, build query packet
198                 mov di,DNSSendBuf+dnshdr.flags
199                 inc word [es:di-2]      ; New query ID
200                 mov ax,htons(0100h)     ; Recursion requested
201                 stosw
202                 mov ax,htons(1)         ; One question
203                 stosw
204                 xor ax,ax               ; No answers, NS or ARs
205                 stosw
206                 stosw
207                 stosw
208                 
209                 call dns_mangle         ; Convert name to DNS labels
210
211                 push cs                 ; DS <- CS
212                 pop ds
213
214                 push si                 ; Save pointer to after DNS string
215
216                 ; Initialize...
217                 mov eax,[MyIP]
218                 mov [pxe_udp_read_pkt_dns.dip],eax
219
220                 and dx,dx
221                 jnz .fqdn               ; If we have dots, assume it's FQDN
222                 dec di                  ; Remove final null
223                 mov si,LocalDomain
224                 call strcpy             ; Uncompressed DNS label set so it ends in null
225 .fqdn:
226
227                 mov ax,htons(1)
228                 stosw                   ; QTYPE  = 1 = A
229                 stosw                   ; QCLASS = 1 = IN
230
231                 sub di,DNSSendBuf
232                 mov [pxe_udp_write_pkt_dns.buffersize],di
233
234                 ; Now, send it to the nameserver(s)
235                 ; Outer loop: exponential backoff
236                 ; Inner loop: scan the various DNS servers
237
238                 mov dx,PKT_TIMEOUT
239                 mov cx,PKT_RETRY
240 .backoff:
241                 mov si,DNSServers
242 .servers:
243                 cmp si,[LastDNSServer]
244                 jb .moreservers
245
246 .nomoreservers:
247                 add dx,dx                       ; Exponential backoff
248                 loop .backoff
249
250                 xor eax,eax                     ; Nothing...
251 .done:
252                 pop si
253                 pop dx
254                 pop cx
255                 pop di
256                 pop es
257                 pop ds
258                 ret
259
260 .moreservers:
261                 lodsd                           ; EAX <- next server
262                 push si
263                 push cx
264                 push dx
265
266                 mov word [pxe_udp_write_pkt_dns.status],0       
267
268                 mov [pxe_udp_write_pkt_dns.sip],eax
269                 mov [pxe_udp_read_pkt_dns.sip],eax
270                 xor eax,[MyIP]
271                 and eax,[Netmask]
272                 jz .nogw
273                 mov eax,[Gateway]
274 .nogw:
275                 mov [pxe_udp_write_pkt_dns.gip],eax
276
277                 mov di,pxe_udp_write_pkt_dns
278                 mov bx,PXENV_UDP_WRITE
279                 call pxenv
280                 jc .timeout                             ; Treat failed transmit as timeout
281                 cmp word [pxe_udp_write_pkt_dns.status],0
282                 jne .timeout
283
284                 mov cx,[BIOS_timer]
285 .waitrecv:
286                 mov ax,[BIOS_timer]
287                 sub ax,cx
288                 cmp ax,dx
289                 jae .timeout
290
291                 mov word [pxe_udp_read_pkt_dns.status],0
292                 mov word [pxe_udp_read_pkt_dns.buffersize],DNS_MAX_PACKET
293                 mov di,pxe_udp_read_pkt_dns
294                 mov bx,PXENV_UDP_READ
295                 call pxenv
296                 and ax,ax
297                 jnz .waitrecv
298                 cmp [pxe_udp_read_pkt_dns.status],ax
299                 jnz .waitrecv
300
301                 ; Got a packet, deal with it...
302                 mov si,DNSRecvBuf
303                 lodsw
304                 cmp ax,[DNSSendBuf]             ; ID
305                 jne .waitrecv                   ; Not ours
306
307                 lodsw                           ; flags
308                 xor al,80h                      ; Query#/Answer bit
309                 test ax,htons(0F80Fh)
310                 jnz .badness
311                 
312                 lodsw
313                 xchg ah,al                      ; ntohs
314                 mov cx,ax                       ; Questions echoed
315                 lodsw
316                 xchg ah,al                      ; ntohs
317                 push ax                         ; Replies
318                 lodsw                           ; NS records
319                 lodsw                           ; Authority records
320
321                 jcxz .qskipped
322 .skipq:
323                 call dns_skiplabel              ; Skip name
324                 add si,4                        ; Skip question trailer
325                 loop .skipq
326
327 .qskipped:
328                 pop cx                          ; Number of replies
329                 jcxz .badness
330
331 .parseanswer:
332                 mov di,DNSSendBuf+dnshdr_size
333                 call dns_compare
334                 pushf
335                 call dns_skiplabel
336                 mov ax,[si+8]                   ; RDLENGTH
337                 xchg ah,al                      ; ntohs
338                 popf
339                 jnz .notsame
340                 cmp dword [si],htons(1)*0x10001 ; TYPE = A, CLASS = IN?
341                 jne .notsame
342                 cmp ax,4                        ; RDLENGTH = 4?
343                 jne .notsame
344                 ;
345                 ; We hit paydirt here...
346                 ;
347                 mov eax,[si+10]
348 .gotresult:
349                 add sp,6                        ; Drop timeout information
350                 jmp .done
351
352 .notsame:
353                 add si,10
354                 add si,ax
355                 loop .parseanswer
356
357 .badness:
358                 ; We got back no data from this server.  Unfortunately, for a recursive,
359                 ; non-authoritative query there is no such thing as an NXDOMAIN reply,
360                 ; which technically means we can't draw any conclusions.  However,
361                 ; in practice that means the domain doesn't exist.  If this turns out
362                 ; to be a problem, we may want to add code to go through all the servers
363                 ; before giving up.
364
365                 ; If the DNS server wasn't capable of recursion, and isn't capable
366                 ; of giving us an authoritative reply (i.e. neither AA or RA set),
367                 ; then at least try a different setver...
368                 test word [DNSRecvBuf+dnshdr.flags],htons(0480h)
369                 jz .timeout
370                 
371                 xor eax,eax
372                 jmp .gotresult
373
374 .timeout:
375                 pop dx
376                 pop cx
377                 pop si
378                 jmp .servers