From 20a5fcfeb198143ca55cc21cf5ceeb6af80c2548 Mon Sep 17 00:00:00 2001 From: Liu Aleaxander Date: Wed, 19 Aug 2009 18:47:04 +0800 Subject: [PATCH] Core:PXELINUX: dnsrelov.inc converted The dnsrelov converted to C. Signed-off-by: Liu Aleaxander --- core/comboot.inc | 3 +- core/dnsresolv.inc | 380 +--------------------------------------------- core/fs/pxe/dhcp_option.c | 14 +- core/fs/pxe/dnsresolv.c | 276 +++++++++++++++++++++++++++++++++ core/fs/pxe/pxe.c | 17 +-- core/fs/pxe/pxe.h | 21 ++- core/include/core.h | 8 +- 7 files changed, 317 insertions(+), 402 deletions(-) create mode 100644 core/fs/pxe/dnsresolv.c diff --git a/core/comboot.inc b/core/comboot.inc index 60c5d6f..28f0bc2 100644 --- a/core/comboot.inc +++ b/core/comboot.inc @@ -686,10 +686,11 @@ comapi_ipappend: ; INT 22h AX=0010h Resolve hostname ; %if IS_PXELINUX + extern pxe_dns_resolv comapi_dnsresolv: mov ds,P_ES mov si,P_BX - call dns_resolv + pm_call pxe_dns_resolv mov P_EAX,eax clc ret diff --git a/core/dnsresolv.inc b/core/dnsresolv.inc index 9733282..6d6c67a 100644 --- a/core/dnsresolv.inc +++ b/core/dnsresolv.inc @@ -13,381 +13,15 @@ ; ; 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 .text16 - -; -; 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. -; - global dns_mangle -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 .bss16 - global LocalDomain, DNSServers +; +DNS_MAX_PACKET equ 512 ;Defined by protocol + + section .bss16 + global LocalDomain + global DNSSendBuf, DNSRecvBuf alignb 2 DNSSendBuf resb DNS_MAX_PACKET DNSRecvBuf resb DNS_MAX_PACKET LocalDomain resb 256 ; Max possible length -DNSServers resd DNS_MAX_SERVERS - - section .data16 -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 - - global LastDNSServer -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 .text16 - global dns_resolv -dns_resolv: - push ds - push es - push di - push bx - 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 bx,TimeoutTable -.backoff: - movzx dx,byte [bx] - mov si,DNSServers -.servers: - cmp si,[LastDNSServer] - jb .moreservers - -.nomoreservers: - inc bx - cmp bx,TimeoutTableEnd - jb .backoff - - xor eax,eax ; Nothing... -.done: - pop si - pop dx - pop cx - pop bx - pop di - pop es - pop ds - ret - -.moreservers: - lodsd ; EAX <- next server - push si - push bx - 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,8 ; 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 bx - pop si - jmp .servers + section .text16 \ No newline at end of file diff --git a/core/fs/pxe/dhcp_option.c b/core/fs/pxe/dhcp_option.c index 5c81fbd..b44f20d 100644 --- a/core/fs/pxe/dhcp_option.c +++ b/core/fs/pxe/dhcp_option.c @@ -29,25 +29,19 @@ static void dns_servers(void *data, int opt_len) num = DNS_MAX_SERVERS; for (i = 0; i < num; i++) { - DNSServers[i] = *(uint32_t *)data; + dns_server[i] = *(uint32_t *)data; data += 4; } - - /* NOT SURE FOR NOW */ - LastDNSServer = OFFS_WRT(&DNSServers[num - 1], 0); } static void local_domain(void *data, int opt_len) { - com32sys_t regs; char *p = (char *)data + opt_len; + char *ld = LocalDomain; char end = *p; - - memset(®s, 0, sizeof regs); + *p = '\0'; /* Zero-terminate option */ - regs.esi.w[0] = OFFS_WRT(data, 0); - regs.edi.w[0] = OFFS_WRT(LocalDomain, 0); - call16(dns_mangle, ®s, NULL); + dns_mangle(&ld, (char **)&data); *p = end; /* Resotre ending byte */ } diff --git a/core/fs/pxe/dnsresolv.c b/core/fs/pxe/dnsresolv.c new file mode 100644 index 0000000..081b049 --- /dev/null +++ b/core/fs/pxe/dnsresolv.c @@ -0,0 +1,276 @@ +#include +#include +#include +#include "pxe.h" + +/* + * The DNS header structure + */ +struct dnshdr { + uint16_t id; + uint16_t flags; + /* number of entries in the question section */ + uint16_t qdcount; + /* number of resource records in the answer section */ + uint16_t ancount; + /* number of name server resource records in the authority records section*/ + uint16_t nscount; + /* number of resource records in the additional records section */ + uint16_t arcount; +} __attribute__ ((packed)); + +/* + * The DNS query structure + */ +struct dnsquery { + uint16_t qtype; + uint16_t qclass; +} __attribute__ ((packed)); + +/* + * The DNS Resource recodes structure + */ +struct dnsrr { + uint16_t type; + uint16_t class; + uint32_t ttl; + uint16_t rdlength; /* The lenght of this rr data */ + char rdata[]; +} __attribute__ ((packed)); + + +uint32_t dns_server[DNS_MAX_SERVERS] = {0, }; + +/* + * Turn a string in _src_ into a DNS "label set" in _dst_; returns the + * number of dots encountered. On return, both src and dst are updated. + */ +int dns_mangle(char **dst, char **src) +{ + char *p = *src; + char *q = *dst; + int dots = 0; + int flag = 0; + char c; + + while (1) { + c = *p++; + if (c == 0 || c == ':') + break; + if (c == '.') { + dots++; + flag = *q; + *q++ = 0; + continue; + } + + flag++; + *q++ = c; + } + + if (flag) + *dst++ = 0; + + /* update the strings */ + *src = --p; + *dst = q; + return dots; +} + + +/* + * Compare two sets of DNS labels, in _s1_ and _s2_; the one in _s1_ + * is allowed pointers relative to a packet in DNSRecvBuf. + * + */ +static int dns_compare(char *s1, char *s2) +{ +#if 0 + while (1) { + if (*s1 < 0xc0) + break; + s1 = DNSRecvBuf + (((*s1++ & 0x3f) << 8) | (*s1++)); + } + if (*s1 == 0) + return 1; + else if (*s1++ != *s2++) + return 0; /* not same */ + else + return !strcmp(s1, s2); +#else + (void)s1; + (void)s2; + return 1; +#endif +} + +/* + * Skip past a DNS label set in DS:SI + */ +static char *dns_skiplabel(char *dns) +{ + uint8_t c; + + while (1) { + c = *dns++; + if (c >= 0xc0) + return ++dns; /* pointer is two bytes */ + if (c == 0) + return dns; + dns += c; + } +} + +/* + * Actual resolver function + * Points to a null-terminated or :-terminated string in _name_ + * and returns the ip addr in _ip_ if it exists and can be found. + * If _ip_ = 0 on exit, the lookup failed. _name_ will be updated + * + */ +uint32_t dns_resolv(char **name) +{ + char *p; + int err; + int dots; + int same; + int rd_len; + int ques, reps; /* number of questions and replies */ + uint8_t timeout; + const uint8_t *timeout_ptr = TimeoutTable; + uint16_t oldtime; + uint32_t srv; + uint32_t *srv_ptr = dns_server; + struct dnshdr *hd1 = (struct dnshdr *)DNSSendBuf; + struct dnshdr *hd2 = (struct dnshdr *)DNSRecvBuf; + struct dnsquery *query; + struct dnsrr *rr; + static __lowmem struct pxe_udp_write_pkt uw_pkt; + static __lowmem struct pxe_udp_read_pkt ur_pkt; + + /* First, fill the DNS header struct */ + hd1->id++; /* New query ID */ + hd1->flags = htons(0x0100); /* Recursion requested */ + hd1->qdcount = htons(1); /* One question */ + hd1->ancount = 0; /* No answers */ + hd1->nscount = 0; /* No NS */ + hd1->arcount = 0; /* No AR */ + + p = DNSSendBuf + sizeof(struct dnshdr); + dots = dns_mangle(&p, name); /* store the CNAME */ + + if (!dots) { + p--; /* Remove final null */ + /* Uncompressed DNS label set so it ends in null */ + strcpy(p, LocalDomain); + } + + /* Fill the DNS query packet */ + query = (struct dnsquery *)p; + query->qtype = htons(1); /* QTYPE = 1 = A */ + query->qclass = htons(1); /* QCLASS = 1 = IN */ + p += sizeof(struct dnsquery); + + /* Now send it to name server */ + timeout_ptr = TimeoutTable; + timeout = *timeout_ptr++; + while ((srv = *srv_ptr++)) { + uw_pkt.status = 0; + uw_pkt.sip = srv; + uw_pkt.gip = ((srv ^ MyIP) & Netmask) ? Gateway : 0; + uw_pkt.lport = DNS_LOCAL_PORT; + uw_pkt.rport = DNS_PORT; + uw_pkt.buffersize = p - DNSSendBuf; + uw_pkt.buffer[0] = OFFS_WRT(DNSSendBuf, 0); + uw_pkt.buffer[1] = 0; + err = pxe_call(PXENV_UDP_WRITE, &uw_pkt); + if (err || uw_pkt.status != 0) + continue; + + oldtime = BIOS_timer; + while (oldtime + timeout <= BIOS_timer) { + ur_pkt.status = 0; + ur_pkt.sip = srv; + ur_pkt.dip = MyIP; + ur_pkt.rport = DNS_PORT; + ur_pkt.lport = DNS_LOCAL_PORT; + ur_pkt.buffersize = DNS_MAX_PACKET; + ur_pkt.buffer[0] = OFFS_WRT(DNSRecvBuf, 0); + ur_pkt.buffer[1] = 0; + err = pxe_call(PXENV_UDP_READ, &ur_pkt); + if (err || ur_pkt.status) + continue; + + /* Got a packet, deal with it... */ + if (hd2->id == hd1->id) + break; + } + if (BIOS_timer > oldtime + timeout) { + /* time out */ + timeout = *timeout_ptr++; + if (!timeout) + return 0; /* All time ticks run out */ + else + continue; /* try next */ + } + if ((hd2->flags ^ 0x80) & htons(0xf80f)) + goto badness; + + ques = htons(hd2->qdcount); /* Questions */ + reps = htons(hd2->ancount); /* Replies */ + p = DNSRecvBuf + sizeof(struct dnshdr); + while (ques--) { + p = dns_skiplabel(p); /* Skip name */ + p += 4; /* Skip question trailer */ + } + + /* Parse the replies */ + while (reps--) { + same = dns_compare(p, (char *)(DNSSendBuf + sizeof(struct dnshdr))); + rr = (struct dnsrr *)dns_skiplabel(p); + rd_len = htons(rr->rdlength); + if (same && rd_len == 4 && + htons(rr->type) == 1 && /* TYPE == A */ + htons(rr->class) == 1 ) /* CLASS == IN */ + return *(uint32_t *)rr->rdata; + + /* not the one we want, try next */ + p += sizeof(struct dnsrr) + rd_len; + } + + 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... + */ + if (hd2->flags == htons(0x480)) + continue; + + break; /* failed */ + } + + return 0; +} + + +/* + * the one should be called from ASM file + */ +void pxe_dns_resolv(com32sys_t *regs) +{ + char *name = MK_PTR(regs->ds, regs->esi.w[0]); + + regs->eax.l = dns_resolv(&name); +} diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 3734e9d..87d03eb 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -13,7 +13,7 @@ static int has_gpxe; static uint8_t uuid_dashes[] = {4, 2, 2, 2, 6, 0}; int HaveUUID = 0; -static const uint8_t TimeoutTable[] = { +const uint8_t TimeoutTable[] = { 2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31, 37, 44, 53, 64, 77, 92, 110, 132, 159, 191, 229, 255, 255, 255, 255, 0 }; @@ -226,7 +226,7 @@ static const char *parse_dotquad(const char *ip_str, uint32_t *res) * the ASM pxenv function wrapper, return 1 if error, or 0 * */ -static int pxe_call(int opcode, void *data) +int pxe_call(int opcode, void *data) { extern void pxenv(void); com32sys_t in_regs, out_regs; @@ -404,7 +404,6 @@ static void get_packet_gpxe(struct open_file_t *file) */ static void pxe_mangle_name(char *dst, const char *src) { - extern void dns_resolv(void); const char *p = src; uint32_t ip = ServerIP; int i = 0; @@ -431,15 +430,9 @@ static void pxe_mangle_name(char *dst, const char *src) p = src; if ((p = parse_dotquad(p, &ip)) && !strncmp(p, "::", 2)) { p += 2; - } else { - com32sys_t regs; - - memset(®s, 0, sizeof regs); - regs.esi.w[0] = OFFS_WRT(p, 0); - call16(dns_resolv, ®s, ®s); - p = MK_PTR(regs.ds, regs.esi.w[0]); - ip = regs.eax.l; - if (!strncmp(p, "::", 2) && ip) { + } else { + ip = dns_resolv(&p); + if (ip && !strncmp(p, "::", 2)) { p += 2; } else { /* no ip, too */ diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index 1f66032..e1138f1 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -29,7 +29,6 @@ #define TFTP_BLOCKSIZE_LG2 9 #define TFTP_BLOCKSIZE (1 << TFTP_BLOCKSIZE_LG2) #define PKTBUF_SEG 0x4000 -#define DNS_MAX_SERVERS 4 #define is_digit(c) (((c) >= '0') && ((c) <= '9')) @@ -59,6 +58,14 @@ #define BOOTP_OPTION_MAGIC htonl(0x63825363) #define MAC_MAX 32 +/* Defines for DNS */ +#define DNS_PORT htons(53) /* Default DNS port */ +#define DNS_MAX_PACKET 512 /* Defined by protocol */ +/* All local DNS queries come from this port */ +#define DNS_LOCAL_PORT htons(60053) +#define DNS_MAX_SERVERS 4 /* Max no of DNS servers */ + + /* * structures */ @@ -210,10 +217,20 @@ struct gpxe_file_read { } __attribute__ ((packed)); /* - * functions + * functions */ + +/* pxe.c */ int ip_ok(uint32_t); +int pxe_call(int, void *); + +/* dhcp_options.c */ void parse_dhcp(int); void parse_dhcp_options(void *, int, int); +/* dnsresolv.c */ +int dns_mangle(char **, char **); +uint32_t dns_resolve(char **); + + #endif /* pxe.h */ diff --git a/core/include/core.h b/core/include/core.h index ad00492..f8be691 100644 --- a/core/include/core.h +++ b/core/include/core.h @@ -44,8 +44,6 @@ __noreturn _kaboom(void); /* * externs for pxelinux */ -extern void dns_mangle(void); - extern uint32_t ServerIP; extern uint32_t MyIP; extern uint32_t Netmask; @@ -73,8 +71,9 @@ extern char packet_buf[]; extern char IPOption[]; extern char DotQuadBuf[]; -extern uint32_t DNSServers[]; -extern uint16_t LastDNSServer; +extern uint32_t dns_server[]; +extern char DNSSendBuf[]; +extern char DNSRecvBuf[]; extern uint16_t RealBaseMem; extern uint16_t APIVer; @@ -87,5 +86,6 @@ extern uint8_t UUIDType; extern char UUID[]; extern volatile uint16_t BIOS_timer; +extern const uint8_t TimeoutTable[]; #endif /* CORE_H */ -- 2.7.4