From e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb Mon Sep 17 00:00:00 2001 From: Yang Zhang Date: Fri, 28 Aug 2015 09:58:54 +0800 Subject: Add qemu 2.4.0 Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5 Signed-off-by: Yang Zhang --- qemu/roms/SLOF/clients/net-snk/app/netlib/dns.c | 527 ++++++++++++++++++++++++ 1 file changed, 527 insertions(+) create mode 100644 qemu/roms/SLOF/clients/net-snk/app/netlib/dns.c (limited to 'qemu/roms/SLOF/clients/net-snk/app/netlib/dns.c') diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/dns.c b/qemu/roms/SLOF/clients/net-snk/app/netlib/dns.c new file mode 100644 index 000000000..0ab1346c9 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/dns.c @@ -0,0 +1,527 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/********************** DEFINITIONS & DECLARATIONS ***********************/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define DNS_FLAG_MSGTYPE 0xF800 /**< Message type mask (opcode) */ +#define DNS_FLAG_SQUERY 0x0000 /**< Standard query type */ +#define DNS_FLAG_SRESPONSE 0x8000 /**< Standard response type */ +#define DNS_FLAG_RD 0x0100 /**< Recursion desired flag */ +#define DNS_FLAG_RCODE 0x000F /**< Response code mask + (stores err.cond.) code */ +#define DNS_RCODE_NERROR 0 /**< "No errors" code */ + +#define DNS_QTYPE_A 1 /**< A 32-bit IP record type */ +#define DNS_QTYPE_AAAA 0x1c /**< 128-bit IPv6 record type */ +#define DNS_QTYPE_CNAME 5 /**< Canonical name record type */ + +#define DNS_QCLASS_IN 1 /**< Query class for internet msgs */ + +/** \struct dnshdr + * A header for DNS-messages (see RFC 1035, paragraph 4.1.1). + *

+ * DNS-message consist of DNS-header and 4 optional sections, + * arranged in the following order:

    + *
  • DNS-header + *
  • question section + *
  • answer section + *
  • authority section + *
  • additional section + *
+ */ +struct dnshdr { + uint16_t id; /**< an identifier used to match up replies */ + uint16_t flags; /**< contains op_code, err_code, etc. */ + uint16_t qdcount; /**< specifies the number of entries in the + question section */ + uint16_t ancount; /**< specifies the number of entries in the + answer section */ + uint16_t nscount; /**< specifies the number of entries in the + authority section */ + uint16_t arcount; /**< specifies the number of entries in the + additional section */ +}; + + +/***************************** PROTOTYPES ********************************/ + +static void +dns_send_query(int fd, int8_t * domain_name, uint8_t ip_version); + +static void +fill_dnshdr(uint8_t * packet, int8_t * domain_name, uint8_t ip_version); + +static uint8_t * +dns_extract_name(uint8_t * dnsh, int8_t * head, int8_t * domain_name); + +static int8_t +urltohost(char * url, char * host_name); + +static int8_t +hosttodomain(char * host_name, char * domain_name); + +/**************************** LOCAL VARIABLES ****************************/ + +static uint8_t ether_packet[ETH_MTU_SIZE]; +static int32_t dns_server_ip = 0; +static uint8_t dns_server_ipv6[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static int32_t dns_result_ip = 0; +static uint8_t dns_result_ipv6[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static int8_t dns_error = 0; /**< Stores error code or 0 */ +static int8_t dns_domain_name[0x100]; /**< Raw domain name */ +static int8_t dns_domain_cname[0x100]; /**< Canonical domain name */ + +/**************************** IMPLEMENTATION *****************************/ + +/** + * DNS: Initialize the environment for DNS client. + * To perfrom DNS-queries use the function dns_get_ip. + * + * @param device_socket a socket number used to send and receive packets + * @param server_ip DNS-server IPv4 address (e.g. 127.0.0.1) + * @return TRUE in case of successful initialization; + * FALSE in case of fault (e.g. can't obtain MAC). + * @see dns_get_ip + */ +int8_t +dns_init(uint32_t _dns_server_ip, uint8_t _dns_server_ipv6[16], uint8_t ip_version) +{ + if(ip_version == 6) + memcpy(dns_server_ipv6, _dns_server_ipv6, 16); + else + dns_server_ip = _dns_server_ip; + return 0; +} + +/** + * DNS: For given URL retrieves IPv4/IPv6 from DNS-server. + *

+ * URL can be given in one of the following form:

    + *
  • scheme with full path with (without) user and password + *
    (e.g. "http://user:pass@www.host.org/url-path"); + *
  • host name with url-path + *
    (e.g. "www.host.org/url-path"); + *
  • nothing but host name + *
    (e.g. "www.host.org"); + *
+ * + * @param fd socket descriptor + * @param url the URL to be resolved + * @param domain_ip In case of SUCCESS stores extracted IP. + * In case of FAULT stores zeros (0.0.0.0). + * @return TRUE - IP successfuly retrieved; + * FALSE - error condition occurs. + */ +int8_t +dns_get_ip(int fd, int8_t * url, uint8_t * domain_ip, uint8_t ip_version) +{ + /* this counter is used so that we abort after 30 DNS request */ + int32_t i; + /* this buffer stores host name retrieved from url */ + static int8_t host_name[0x100]; + + (* domain_ip) = 0; + + // Retrieve host name from URL + if (!urltohost((char *) url, (char *) host_name)) { + printf("\nERROR:\t\t\tBad URL!\n"); + return 0; + } + + // Reformat host name into a series of labels + if (!hosttodomain((char *) host_name, (char *) dns_domain_name)) { + printf("\nERROR:\t\t\tBad host name!\n"); + return 0; + } + + // Check if DNS server is presented and accessible + if (dns_server_ip == 0) { + printf("\nERROR:\t\t\tCan't resolve domain name " + "(DNS server is not presented)!\n"); + return 0; + } + + // Use DNS-server to obtain IP + if (ip_version == 6) + memset(dns_result_ipv6, 0, 16); + else + dns_result_ip = 0; + dns_error = 0; + strcpy((char *) dns_domain_cname, ""); + + for(i = 0; i < 30; ++i) { + // Use canonical name in case we obtained it + if (strlen((char *) dns_domain_cname)) + dns_send_query(fd, dns_domain_cname, ip_version); + else + dns_send_query(fd, dns_domain_name, ip_version); + + // setting up a timer with a timeout of one seconds + set_timer(TICKS_SEC); + do { + receive_ether(fd); + if (dns_error) + return 0; // FALSE - error + if ((dns_result_ip != 0) && (ip_version == 4)) { + memcpy(domain_ip, &dns_result_ip, 4); + return 1; // TRUE - success (domain IP retrieved) + } + else if ((dns_result_ipv6[0] != 0) && (ip_version == 6)) { + memcpy(domain_ip, dns_result_ipv6, 16); + return 1; // TRUE - success (domain IP retrieved) + } + } while (get_timer() > 0); + } + + printf("\nGiving up after %d DNS requests\n", i); + return 0; // FALSE - domain name wasn't retrieved +} + +/** + * DNS: Handles DNS-messages according to Receive-handle diagram. + * Sets dns_result_ip for given dns_domain_name (see dns_get_ip) + * or signals error condition occurs during DNS-resolving process + * by setting dns_error flag. + * + * @param packet DNS-packet to be handled + * @param packetsize length of the packet + * @return ZERO - packet handled successfully; + * NON ZERO - packet was not handled (e.g. bad format) + * @see dns_get_ip + * @see receive_ether + * @see dnshdr + */ +int32_t +handle_dns(uint8_t * packet, int32_t packetsize) +{ + struct dnshdr * dnsh = (struct dnshdr *) packet; + uint8_t * resp_section = packet + sizeof(struct dnshdr); + /* This string stores domain name from DNS-packets */ + static int8_t handle_domain_name[0x100]; + int i; + + // verify ID - is it response for our query? + if (dnsh -> id != htons(0x1234)) + return 0; + + // Is it DNS response? + if ((dnsh -> flags & htons(DNS_FLAG_MSGTYPE)) != htons(DNS_FLAG_SRESPONSE)) + return 0; + + // Is error condition occurs? (check error field in incoming packet) + if ((dnsh -> flags & htons(DNS_FLAG_RCODE)) != DNS_RCODE_NERROR) { + dns_error = 1; + return 0; + } + + /* Pass all (qdcount) records in question section */ + + for (i = 0; i < htons(dnsh -> qdcount); i++) { + // pass QNAME + resp_section = dns_extract_name((uint8_t *) dnsh, (int8_t *) resp_section, + handle_domain_name); + if (resp_section == NULL) { + return -1; // incorrect domain name (bad packet) + } + // pass QTYPE & QCLASS + resp_section += 4; + } + + /* Handle all (ancount) records in answer section */ + + for (i = 0; i < htons(dnsh -> ancount); i++) { + // retrieve domain name from the packet + resp_section = dns_extract_name((uint8_t *) dnsh, (int8_t *) resp_section, + handle_domain_name); + + if (resp_section == NULL) { + return -1; // incorrect domain name (bad packet) + } + + // Check the class of the query (should be IN for Internet) + if (* (uint16_t *) (resp_section + 2) == htons(DNS_QCLASS_IN)) { + // check if retrieved name fit raw or canonical domain name + if (!strcmp((char *) handle_domain_name, (char *) dns_domain_name) || + !strcmp((char *) handle_domain_name, (char *) dns_domain_cname)) { + switch (htons(* (uint16_t *) resp_section)) { + + case DNS_QTYPE_A : + // rdata contains IP + dns_result_ip = htonl(* (uint32_t *) (resp_section + 10)); + return 0; // IP successfully obtained + + case DNS_QTYPE_CNAME : + // rdata contains canonical name, store it for further requests + if (dns_extract_name((uint8_t *) dnsh, (int8_t *) resp_section + 10, + dns_domain_cname) == NULL) { + // incorrect domain name (bad packet) + return -1; + } + break; + case DNS_QTYPE_AAAA : + memcpy(dns_result_ipv6, (resp_section + 10), 16); + return 0; // IP successfully obtained + break; + } + } + // continue with next record in answer section + resp_section += htons(* (uint16_t *) (resp_section + 8)) + 10; + } + } + return 0; // Packet successfully handled but IP wasn't obtained +} + +/** + * DNS: Sends a standard DNS-query (read request package) to a DNS-server. + * DNS-server respones with host IP or signals some error condition. + * Responses from the server are handled by handle_dns function. + * + * @param fd socket descriptor + * @param domain_name the domain name given as series of labels preceded + * with length(label) and terminated with 0 + *
(e.g. "\3,w,w,w,\4,h,o,s,t,\3,o,r,g,\0") + * @see handle_dns + */ +static void +dns_send_query(int fd, int8_t * domain_name, uint8_t ip_version) +{ + int qry_len = strlen((char *) domain_name) + 5; + int iphdr_len = (ip_version == 4) ? sizeof(struct iphdr) : sizeof(struct ip6hdr); + ip6_addr_t server_ipv6; + + uint32_t packetsize = iphdr_len + + sizeof(struct udphdr) + sizeof(struct dnshdr) + + qry_len; + + memset(ether_packet, 0, packetsize); + fill_dnshdr(ðer_packet[ + iphdr_len + sizeof(struct udphdr)], + domain_name, + ip_version); + fill_udphdr(ðer_packet[iphdr_len], + sizeof(struct dnshdr) + + sizeof(struct udphdr) + qry_len, + UDPPORT_DNSC, UDPPORT_DNSS); + if (ip_version == 4) { + fill_iphdr(ether_packet, + sizeof(struct dnshdr) + sizeof(struct udphdr) + + iphdr_len + qry_len, + IPTYPE_UDP, 0, dns_server_ip); + } else { + memcpy(server_ipv6.addr, dns_server_ipv6, 16); + fill_ip6hdr(ether_packet, + sizeof(struct dnshdr) + sizeof(struct udphdr) + qry_len, + IPTYPE_UDP, get_ipv6_address(), + &server_ipv6); + } + + send_ip(fd, ether_packet, packetsize); +} + +/** + * DNS: Creates standard DNS-query package. Places DNS-header + * and question section in a packet and fills it with + * corresponding information. + *

+ * Use this function with similar functions for other network layers + * (fill_udphdr, fill_iphdr, fill_ethhdr). + * + * @param packet Points to the place where ARP-header must be placed. + * @param domain_name the domain name given as series of labels preceded + * with length(label) and terminated with 0 + *
(e.g. "\3,w,w,w,\4,h,o,s,t,\3,o,r,g,\0") + * @see fill_udphdr + * @see fill_iphdr + * @see fill_ethhdr + */ +static void +fill_dnshdr(uint8_t * packet, int8_t * domain_name, uint8_t ip_version) +{ + struct dnshdr * dnsh = (struct dnshdr *) packet; + uint8_t * qry_section = packet + sizeof(struct dnshdr); + + dnsh -> id = htons(0x1234); + dnsh -> flags = htons(DNS_FLAG_SQUERY) | htons(DNS_FLAG_RD); + dnsh -> qdcount = htons(1); + + strcpy((char *) qry_section, (char *) domain_name); + qry_section += strlen((char *) domain_name) + 1; + + // fill QTYPE (ask for IP) + if (ip_version == 4) + * (uint16_t *) qry_section = htons(DNS_QTYPE_A); + else + * (uint16_t *) qry_section = htons(DNS_QTYPE_AAAA); + qry_section += 2; + // fill QCLASS (IN is a standard class for Internet) + * (uint16_t *) qry_section = htons(DNS_QCLASS_IN); +} + +/** + * DNS: Extracts domain name from the question or answer section of + * the DNS-message. This function is need to support message + * compression requirement (see RFC 1035, paragraph 4.1.4). + * + * @param dnsh Points at the DNS-header. + * @param head Points at the beginning of the domain_name + * which has to be extracted. + * @param domain_name In case of SUCCESS this string stores extracted name. + * In case of FAULT this string is empty. + * @return NULL in case of FAULT (domain name > 255 octets); + * otherwise pointer to the data following the name. + * @see dnshdr + */ +static uint8_t * +dns_extract_name(uint8_t * dnsh, int8_t * head, int8_t * domain_name) +{ + int8_t * tail = domain_name; + int8_t * ptr = head; + int8_t * next_section = NULL; + + while (1) { + if ((ptr[0] & 0xC0) == 0xC0) { + // message compressed (reference is used) + next_section = ptr + 2; + ptr = (int8_t *) dnsh + (htons(* (uint16_t *) ptr) & 0x3FFF); + continue; + } + if (ptr[0] == 0) { + // message termination + tail[0] = 0; + ptr += 1; + break; + } + // maximum length for domain name is 255 octets w/o termination sym + if (tail - domain_name + ptr[0] + 1 > 255) { + strcpy((char *) domain_name, ""); + return NULL; + } + memcpy(tail, ptr, ptr[0] + 1); + tail += ptr[0] + 1; + ptr += ptr[0] + 1; + } + + if (next_section == NULL) + next_section = ptr; + + return (uint8_t *) next_section; +} + +/** + * DNS: Parses URL and returns host name. + * Input string can be given as:

    + *
  • scheme with full path with (without) user and password + *
    (e.g. "http://user:pass@www.host.org/url-path"); + *
  • host name with url-path + *
    (e.g. "www.host.org/url-path"); + *
  • nothing but host name + *
    (e.g. "www.host.org"); + *
+ * + * @param url string that stores incoming URL + * @param host_name In case of SUCCESS this string stores the host name, + * In case of FAULT this string is empty. + * @return TRUE - host name retrieved, + * FALSE - host name > 255 octets or empty. + */ +static int8_t +urltohost(char * url, char * host_name) +{ + uint16_t length1; + uint16_t length2; + + strcpy(host_name, ""); + + if (strstr(url, "://") != NULL) + url = strstr(url, "//") + 2; // URL + + if (strstr(url, "@") != NULL) // truncate user & password + url = strstr(url, "@") + 1; + + if (strstr(url, "/") != NULL) // truncate url path + length1 = strstr(url, "/") - url; + else + length1 = strlen(url); + + if (strstr(url, ":") != NULL) // truncate port path + length2 = strstr(url, ":") - url; + else + length2 = strlen(url); + + if(length1 > length2) + length1 = length2; + + if (length1 == 0) + return 0; // string is empty + if(length1 >= 256) + return 0; // host name is too big + + strncpy(host_name, url, length1); + host_name[length1] = 0; + + return 1; // Host name is retrieved +} + +/** + * DNS: Transforms host name string into a series of labels + * each of them preceded with length(label). 0 is a terminator. + * "www.domain.dom" -> "\3,w,w,w,\6,d,o,m,a,i,n,\3,c,o,m,\0" + *

+ * This format is used in DNS-messages. + * + * @param host_name incoming string with the host name + * @param domain_name resulting string with series of labels + * or empty string in case of FAULT + * @return TRUE - host name transformed, + * FALSE - host name > 255 octets or label > 63 octets. + */ +static int8_t +hosttodomain(char * host_name, char * domain_name) +{ + char * domain_iter = domain_name; + char * host_iter = host_name; + + strcpy(domain_name, ""); + + if(strlen(host_name) > 255) + return 0; // invalid host name (refer to RFC 1035) + + for(; 1; ++host_iter) { + if(*host_iter != '.' && *host_iter != 0) + continue; + *domain_iter = host_iter - host_name; + if (*domain_iter > 63) { + strcpy(domain_name, ""); + return 0; // invalid host name (refer to RFC 1035) + } + ++domain_iter; + strncpy(domain_iter, host_name, host_iter - host_name); + domain_iter += (host_iter - host_name); + if(*host_iter == 0) { + *domain_iter = 0; + break; + } + host_name = host_iter + 1; + } + return 1; // ok +} -- cgit 1.2.3-korg