1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
|
; -*- fundamental -*-
; -----------------------------------------------------------------------
;
; Copyright 2004-2005 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.
;
; -----------------------------------------------------------------------
; $Id$
;
; 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 .latebss
alignb 2
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
|