text: /** @file
   * @brief DNS resolving library
   *
   * An API for applications to resolve a DNS name.
   */
  
  /*
   * Copyright (c) 2017 Intel Corporation
   *
   * SPDX-License-Identifier: Apache-2.0
   */
  
  #ifndef _DNS_RESOLVE_H
  #define _DNS_RESOLVE_H
  
  #include <net/net_ip.h>
  #include <net/net_context.h>
  
  #ifdef __cplusplus
  extern "C" {
  #endif
  
  /**
   * @brief DNS resolving library
   * @defgroup dns_resolve DNS Resolve Library
   * @{
   */
  
  /**
   * DNS query type enum
   */
  enum dns_query_type {
  	DNS_QUERY_TYPE_A = 1,	 /* IPv4 */
  	DNS_QUERY_TYPE_AAAA = 28 /* IPv6 */
  };
  
  #ifndef DNS_MAX_NAME_SIZE
  #define DNS_MAX_NAME_SIZE 20
  #endif
  
  /* Make sure that we can compile things even if CONFIG_DNS_RESOLVER
   * is not enabled.
   */
  #if !defined(CONFIG_DNS_RESOLVER_MAX_SERVERS)
  #define CONFIG_DNS_RESOLVER_MAX_SERVERS 1
  #endif
  #if !defined(CONFIG_DNS_NUM_CONCUR_QUERIES)
  #define CONFIG_DNS_NUM_CONCUR_QUERIES 1
  #endif
  
  /* If mDNS is enabled, then add some extra well known multicast servers to the
   * server list.
   */
  #if defined(CONFIG_MDNS_RESOLVER)
  #if defined(CONFIG_NET_IPV6) && defined(CONFIG_NET_IPV4)
  #define MDNS_SERVER_COUNT 2
  #else
  #define MDNS_SERVER_COUNT 1
  #endif /* CONFIG_NET_IPV6 && CONFIG_NET_IPV4 */
  #else
  #define MDNS_SERVER_COUNT 0
  #endif /* CONFIG_MDNS_RESOLVER */
  
  /**
   * Address info struct is passed to callback that gets all the results.
   */
  struct dns_addrinfo {
  	struct sockaddr ai_addr;
  	socklen_t       ai_addrlen;
  	u8_t            ai_family;
  	char            ai_canonname[DNS_MAX_NAME_SIZE + 1];
  };
  
  /**
   * Status values for the callback.
   */
  enum dns_resolve_status {
  	DNS_EAI_BADFLAGS    = -1,   /* Invalid value for `ai_flags' field */
  	DNS_EAI_NONAME      = -2,   /* NAME or SERVICE is unknown */
  	DNS_EAI_AGAIN       = -3,   /* Temporary failure in name resolution */
  	DNS_EAI_FAIL        = -4,   /* Non-recoverable failure in name res */
  	DNS_EAI_NODATA      = -5,   /* No address associated with NAME */
  	DNS_EAI_FAMILY      = -6,   /* `ai_family' not supported */
  	DNS_EAI_SOCKTYPE    = -7,   /* `ai_socktype' not supported */
  	DNS_EAI_SERVICE     = -8,   /* SRV not supported for `ai_socktype' */
  	DNS_EAI_ADDRFAMILY  = -9,   /* Address family for NAME not supported */
  	DNS_EAI_MEMORY      = -10,  /* Memory allocation failure */
  	DNS_EAI_SYSTEM      = -11,  /* System error returned in `errno' */
  	DNS_EAI_OVERFLOW    = -12,  /* Argument buffer overflow */
  	DNS_EAI_INPROGRESS  = -100, /* Processing request in progress */
  	DNS_EAI_CANCELED    = -101, /* Request canceled */
  	DNS_EAI_NOTCANCELED = -102, /* Request not canceled */
  	DNS_EAI_ALLDONE     = -103, /* All requests done */
  	DNS_EAI_IDN_ENCODE  = -105, /* IDN encoding failed */
  };
  
  /**
   * @typedef dns_resolve_cb_t
   * @brief DNS resolve callback
   *
   * @details The DNS resolve callback is called after a successful
   * DNS resolving. The resolver can call this callback multiple times, one
   * for each resolved address.
   *
   * @param status The status of the query:
   *  DNS_EAI_INPROGRESS returned for each resolved address
   *  DNS_EAI_ALLDONE    mark end of the resolving, info is set to NULL in
   *                     this case
   *  DNS_EAI_CANCELED   if the query was canceled manually or timeout happened
   *  DNS_EAI_FAIL       if the name cannot be resolved by the server
   *  DNS_EAI_NODATA     if there is no such name
   *  other values means that an error happened.
   * @param info Query results are stored here.
   * @param user_data The user data given in dns_resolve_name() call.
   */
  typedef void (*dns_resolve_cb_t)(enum dns_resolve_status status,
  				 struct dns_addrinfo *info,
  				 void *user_data);
  
  /**
   * DNS resolve context structure.
   */
  struct dns_resolve_context {
  	struct {
  		/** DNS server information */
  		struct sockaddr dns_server;
  
  		/** Connection to the DNS server */
  		struct net_context *net_ctx;
  
  		/** Is this server mDNS one */
  		bool is_mdns;
  	} servers[CONFIG_DNS_RESOLVER_MAX_SERVERS + MDNS_SERVER_COUNT];
  
  	/** This timeout is also used when a buffer is required from the
  	 * buffer pools.
  	 */
  	s32_t buf_timeout;
  
  	/** Result callbacks. We have multiple callbacks here so that it is
  	 * possible to do multiple queries at the same time.
  	 */
  	struct dns_pending_query {
  		/** Timeout timer */
  		struct k_delayed_work timer;
  
  		/** Back pointer to ctx, needed in timeout handler */
  		struct dns_resolve_context *ctx;
  
  		/** Result callback */
  		dns_resolve_cb_t cb;
  
  		/** User data */
  		void *user_data;
  
  		/** TX timeout */
  		s32_t timeout;
  
  		/** String containing the thing to resolve like www.example.com
  		 */
  		const char *query;
  
  		/** Query type */
  		enum dns_query_type query_type;
  
  		/** DNS id of this query */
  		u16_t id;
  	} queries[CONFIG_DNS_NUM_CONCUR_QUERIES];
  
  	/** Is this context in use */
  	bool is_used;
  };
  
  /**
   * @brief Init DNS resolving context.
   *
   * @details This function sets the DNS server address and initializes the
   * DNS context that is used by the actual resolver.
   * Note that the recommended way to resolve DNS names is to use
   * the dns_get_addr_info() API. In that case user does not need to
   * call dns_resolve_init() as the DNS servers are already setup by the system.
   *
   * @param ctx DNS context. If the context variable is allocated from
   * the stack, then the variable needs to be valid for the whole duration of
   * the resolving. Caller does not need to fill the variable beforehand or
   * edit the context afterwards.
   * @param dns_servers DNS server addresses. The array is null terminated.
   * The port number can be given in the string.
   * Syntax for the server addresses with or without port numbers:
   *    IPv4        : 10.0.9.1
   *    IPv4 + port : 10.0.9.1:5353
   *    IPv6        : 2001:db8::22:42
   *    IPv6 + port : [2001:db8::22:42]:5353
   *
   * @return 0 if ok, <0 if error.
   */
  int dns_resolve_init(struct dns_resolve_context *ctx,
  		     const char *dns_servers[]);
  
  /**
   * @brief Close DNS resolving context.
   *
   * @details This releases DNS resolving context and marks the context unusable.
   * Caller must call the dns_resolve_init() again to make context usable.
   *
   * @param ctx DNS context
   *
   * @return 0 if ok, <0 if error.
   */
  int dns_resolve_close(struct dns_resolve_context *ctx);
  
  /**
   * @brief Cancel a pending DNS query.
   *
   * @details This releases DNS resources used by a pending query.
   *
   * @param ctx DNS context
   * @param dns_id DNS id of the pending query
   *
   * @return 0 if ok, <0 if error.
   */
  int dns_resolve_cancel(struct dns_resolve_context *ctx,
  		       u16_t dns_id);
  
  /**
   * @brief Resolve DNS name.
   *
   * @details This function can be used to resolve e.g., IPv4 or IPv6 address.
   * Note that this is asynchronous call, the function will return immediately
   * and system will call the callback after resolving has finished or timeout
   * has occurred.
   * We might send the query to multiple servers (if there are more than one
   * server configured), but we only use the result of the first received
   * response.
   *
   * @param ctx DNS context
   * @param query What the caller wants to resolve.
   * @param type What kind of data the caller wants to get.
   * @param dns_id DNS id is returned to the caller. This is needed if one
   * wishes to cancel the query. This can be set to NULL if there is no need
   * to cancel the query.
   * @param cb Callback to call after the resolving has finished or timeout
   * has happened.
   * @param user_data The user data.
   * @param timeout The timeout value for the query. Possible values:
   * K_FOREVER: the query is tried forever, user needs to cancel it manually
   *            if it takes too long time to finish
   * >0: start the query and let the system timeout it after specified ms
   *
   * @return 0 if resolving was started ok, < 0 otherwise
   */
  int dns_resolve_name(struct dns_resolve_context *ctx,
  		     const char *query,
  		     enum dns_query_type type,
  		     u16_t *dns_id,
  		     dns_resolve_cb_t cb,
  		     void *user_data,
  		     s32_t timeout);
  
  /**
   * @brief Get default DNS context.
   *
   * @details The system level DNS context uses DNS servers that are
   * defined in project config file. If no DNS servers are defined by the
   * user, then resolving DNS names using default DNS context will do nothing.
   * The configuration options are described in subsys/net/lib/dns/Kconfig file.
   *
   * @return Default DNS context.
   */
  struct dns_resolve_context *dns_resolve_get_default(void);
  
  /**
   * @brief Get IP address info from DNS.
   *
   * @details This function can be used to resolve e.g., IPv4 or IPv6 address.
   * Note that this is asynchronous call, the function will return immediately
   * and system will call the callback after resolving has finished or timeout
   * has occurred.
   * We might send the query to multiple servers (if there are more than one
   * server configured), but we only use the result of the first received
   * response.
   * This variant uses system wide DNS servers.
   *
   * @param query What the caller wants to resolve.
   * @param type What kind of data the caller wants to get.
   * @param dns_id DNS id is returned to the caller. This is needed if one
   * wishes to cancel the query. This can be set to NULL if there is no need
   * to cancel the query.
   * @param cb Callback to call after the resolving has finished or timeout
   * has happened.
   * @param user_data The user data.
   * @param timeout The timeout value for the connection. Possible values:
   * K_FOREVER: the query is tried forever, user needs to cancel it manually
   *            if it takes too long time to finish
   * >0: start the query and let the system timeout it after specified ms
   *
   * @return 0 if resolving was started ok, < 0 otherwise
   */
  static inline int dns_get_addr_info(const char *query,
  				    enum dns_query_type type,
  				    u16_t *dns_id,
  				    dns_resolve_cb_t cb,
  				    void *user_data,
  				    s32_t timeout)
  {
  	return dns_resolve_name(dns_resolve_get_default(),
  				query,
  				type,
  				dns_id,
  				cb,
  				user_data,
  				timeout);
  }
  
  /**
   * @brief Cancel a pending DNS query.
   *
   * @details This releases DNS resources used by a pending query.
   *
   * @param dns_id DNS id of the pending query
   *
   * @return 0 if ok, <0 if error.
   */
  static inline int dns_cancel_addr_info(u16_t dns_id)
  {
  	return dns_resolve_cancel(dns_resolve_get_default(), dns_id);
  }
  
  /**
   * @}
   */
  
  #if defined(CONFIG_DNS_RESOLVER)
  /**
   * @brief Initialize DNS subsystem.
   */
  void dns_init_resolver(void);
  
  #else
  #define dns_init_resolver(...)
  #endif /* CONFIG_DNS_RESOLVER */
  
  #ifdef __cplusplus
  }
  #endif
  
  #endif /* _DNS_RESOLVE_H */
  