summaryrefslogtreecommitdiffstats
path: root/src/roms/SLOF/clients/net-snk/app/netlib/dns.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/roms/SLOF/clients/net-snk/app/netlib/dns.c')
-rw-r--r--src/roms/SLOF/clients/net-snk/app/netlib/dns.c527
1 files changed, 527 insertions, 0 deletions
diff --git a/src/roms/SLOF/clients/net-snk/app/netlib/dns.c b/src/roms/SLOF/clients/net-snk/app/netlib/dns.c
new file mode 100644
index 0000000..0ab1346
--- /dev/null
+++ b/src/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 <dns.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sys/socket.h>
+
+#include <ethernet.h>
+#include <ipv4.h>
+#include <ipv6.h>
+#include <udp.h>
+
+#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).
+ * <p>
+ * DNS-message consist of DNS-header and 4 optional sections,
+ * arranged in the following order:<ul>
+ * <li> DNS-header
+ * <li> question section
+ * <li> answer section
+ * <li> authority section
+ * <li> additional section
+ * </ul>
+ */
+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.
+ * <p>
+ * URL can be given in one of the following form: <ul>
+ * <li> scheme with full path with (without) user and password
+ * <br>(e.g. "http://user:pass@www.host.org/url-path");
+ * <li> host name with url-path
+ * <br>(e.g. "www.host.org/url-path");
+ * <li> nothing but host name
+ * <br>(e.g. "www.host.org");
+ * </ul>
+ *
+ * @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
+ * <br>(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(&ether_packet[
+ iphdr_len + sizeof(struct udphdr)],
+ domain_name,
+ ip_version);
+ fill_udphdr(&ether_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.
+ * <p>
+ * 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
+ * <br>(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: <ul>
+ * <li> scheme with full path with (without) user and password
+ * <br>(e.g. "http://user:pass@www.host.org/url-path");
+ * <li> host name with url-path
+ * <br>(e.g. "www.host.org/url-path");
+ * <li> nothing but host name
+ * <br>(e.g. "www.host.org");
+ * </ul>
+ *
+ * @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"
+ * <p>
+ * 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
+}
OpenPOWER on IntegriCloud