aboutsummaryrefslogtreecommitdiffstats
path: root/core/dnsresolv.inc
blob: f31c578b82af335bbf064635c125d05c7181dcc5 (plain)
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
380
; -*- fundamental -*-
; -----------------------------------------------------------------------
;
;   Copyright 2004-2008 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 .bss2
		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.
;
; No segment assumptions permitted.
;
		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