/**
 * lwip MDNS resolver file.
 *
 * Created on: Jul 29, 2010
 * Author: Daniel Toma
 *

 * ported from uIP resolv.c Copyright (c) 2002-2003, Adam Dunkels.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote
 *    products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**

 * This file implements a MDNS host name and PUCK service registration.

 *-----------------------------------------------------------------------------
 * Includes
 *----------------------------------------------------------------------------*/
#include "lwip/opt.h"
#if LWIP_MDNS /* don't build if not configured for use in lwipopts.h */
#include "lwip/mdns.h"
#include "lwip/puck_def.h"
#include "lwip/udp.h"
#include "lwip/mem.h"
#include "lwip/igmp.h"
#include "osapi.h"
#include "os_type.h"
#include "user_interface.h"

#ifdef MEMLEAK_DEBUG
static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__;
#endif

/** DNS server IP address */
#ifndef DNS_MULTICAST_ADDRESS
#define DNS_MULTICAST_ADDRESS        ipaddr_addr("224.0.0.251") /* resolver1.opendns.com */
#endif

/** DNS server IP address */
#ifndef MDNS_LOCAL
#define MDNS_LOCAL                "local" /* resolver1.opendns.com */
#endif

/** DNS server port address */
#ifndef DNS_MDNS_PORT
#define DNS_MDNS_PORT           5353
#endif

/** DNS maximum number of retries when asking for a name, before "timeout". */
#ifndef DNS_MAX_RETRIES
#define DNS_MAX_RETRIES           4
#endif

/** DNS resource record max. TTL (one week as default) */
#ifndef DNS_MAX_TTL
#define DNS_MAX_TTL               604800
#endif

/* DNS protocol flags */
#define DNS_FLAG1_RESPONSE        0x84
#define DNS_FLAG1_OPCODE_STATUS   0x10
#define DNS_FLAG1_OPCODE_INVERSE  0x08
#define DNS_FLAG1_OPCODE_STANDARD 0x00
#define DNS_FLAG1_AUTHORATIVE     0x04
#define DNS_FLAG1_TRUNC           0x02
#define DNS_FLAG1_RD              0x01
#define DNS_FLAG2_RA              0x80
#define DNS_FLAG2_ERR_MASK        0x0f
#define DNS_FLAG2_ERR_NONE        0x00
#define DNS_FLAG2_ERR_NAME        0x03

/* DNS protocol states */
#define DNS_STATE_UNUSED          0
#define DNS_STATE_NEW             1
#define DNS_STATE_ASKING          2
#define DNS_STATE_DONE            3

/* MDNS registration type */
#define MDNS_HOSTNAME_REG         0
#define MDNS_SERVICE_REG          1

/* MDNS registration type */
#define MDNS_REG_ANSWER           1
#define MDNS_SD_ANSWER            2
#define MDNS_SERVICE_REG_ANSWER   3

/* MDNS registration time */
#define MDNS_HOST_TIME            120
#define MDNS_SERVICE_TIME         3600

/** MDNS name length with "." at the beginning and end of name*/
#ifndef MDNS_LENGTH_ADD
#define MDNS_LENGTH_ADD           2
#endif

#ifdef MDNS_MAX_NAME_LENGTH
#undef MDNS_MAX_NAME_LENGTH
#endif
#define MDNS_MAX_NAME_LENGTH      (256)

PACK_STRUCT_BEGIN
/** DNS message header */
struct mdns_hdr {
	PACK_STRUCT_FIELD(u16_t id);
	PACK_STRUCT_FIELD(u8_t flags1);
	PACK_STRUCT_FIELD(u8_t flags2);
	PACK_STRUCT_FIELD(u16_t numquestions);
	PACK_STRUCT_FIELD(u16_t numanswers);
	PACK_STRUCT_FIELD(u16_t numauthrr);
	PACK_STRUCT_FIELD(u16_t numextrarr);
}PACK_STRUCT_STRUCT;
PACK_STRUCT_END

#define SIZEOF_DNS_HDR 12

PACK_STRUCT_BEGIN
/** MDNS query message structure */
struct mdns_query {
	/* MDNS query record starts with either a domain name or a pointer
	 to a name already present somewhere in the packet. */PACK_STRUCT_FIELD(u16_t type);
	PACK_STRUCT_FIELD(u16_t class);
}PACK_STRUCT_STRUCT;
PACK_STRUCT_END

#define SIZEOF_DNS_QUERY 4

PACK_STRUCT_BEGIN
/** MDNS answer message structure */
struct mdns_answer {
	/* MDNS answer record starts with either a domain name or a pointer
	 to a name already present somewhere in the packet. */PACK_STRUCT_FIELD(u16_t type);
	PACK_STRUCT_FIELD(u16_t class);
	PACK_STRUCT_FIELD(u32_t ttl);
	PACK_STRUCT_FIELD(u16_t len);
}PACK_STRUCT_STRUCT;
PACK_STRUCT_END
#define SIZEOF_DNS_ANSWER 10

PACK_STRUCT_BEGIN
/** MDNS answer message structure */
struct mdns_auth {
	PACK_STRUCT_FIELD(u32_t src);
}PACK_STRUCT_STRUCT;
PACK_STRUCT_END

#define SIZEOF_MDNS_AUTH 4
PACK_STRUCT_BEGIN
/** MDNS service registration message structure */
struct mdns_service {
	PACK_STRUCT_FIELD(u16_t prior);
	PACK_STRUCT_FIELD(u16_t weight);
	PACK_STRUCT_FIELD(u16_t port);
}PACK_STRUCT_STRUCT;
PACK_STRUCT_END

#define SIZEOF_MDNS_SERVICE 6

uint16 PUCK_PORT ;
os_timer_t mdns_timer;
/* forward declarations */
static void mdns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p,
		struct ip_addr *addr, u16_t port);

/*-----------------------------------------------------------------------------
 * Globales
 *----------------------------------------------------------------------------*/

/* MDNS variables */
static char host_name[MDNS_NAME_LENGTH];
static char service_name[MDNS_NAME_LENGTH];
static char server_name[MDNS_NAME_LENGTH];
//static char puck_datasheet[PUCK_DATASHEET_SIZE];
static struct udp_pcb *mdns_pcb = NULL;
static struct mdns_info * ms_info = NULL;
static struct ip_addr multicast_addr;
static struct ip_addr host_addr;
static uint8 register_flag = 0;
static uint8 mdns_flag = 0;
//#if (DNS_USES_STATIC_BUF == 1)
static u8_t mdns_payload[DNS_MSG_SIZE];
//#endif /* (MDNS_USES_STATIC_BUF == 1) */
/*
 *  Function to set the UDP pcb used to send the mDNS packages
 */
void ICACHE_FLASH_ATTR
getPcb(struct udp_pcb *pcb) {
	mdns_pcb = pcb;
}

#if DNS_DOES_NAME_CHECK
/**
 * Compare the "dotted" name "query" with the encoded name "response"
 * to make sure an answer from the DNS server matches the current mdns_table
 * entry (otherwise, answers might arrive late for hostname not on the list
 * any more).
 *
 * @param query hostname (not encoded) from the mdns_table
 * @param response encoded hostname in the DNS response
 * @return 0: names equal; 1: names differ
 */
static u8_t ICACHE_FLASH_ATTR
mdns_compare_name(unsigned char *query, unsigned char *response) {
	unsigned char n;

	do {
		n = *response++;
		/** @see RFC 1035 - 4.1.4. Message compression */
		if ((n & 0xc0) == 0xc0) {
			/* Compressed name */
			break;
		} else {
			/* Not compressed name */
			while (n > 0) {
				if ((*query) != (*response)) {
					return 1;
				}
				++response;
				++query;
				--n;
			};
			++query;
		}
	} while (*response != 0);

	return 0;
}
#endif /* DNS_DOES_NAME_CHECK */
/**
 * Send a mDNS answer packet.
 *
 * @param type of answer hostname and service registration or service
 * @param name to query
 * @param id transaction ID in the DNS query packet
 * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise
 */
static err_t ICACHE_FLASH_ATTR
mdns_answer(u16_t type, const char* name, u8_t id) {
	err_t err;
	struct mdns_hdr *hdr;
	struct mdns_answer ans;
	struct mdns_auth auth;
	struct mdns_service serv;
	struct pbuf *p ,*p_sta;
	char *query, *nptr;
	const char *pHostname;
	struct netif * sta_netif = NULL;
	struct netif * ap_netif = NULL;
	static char tmpBuf[PUCK_DATASHEET_SIZE + PUCK_SERVICE_LENGTH];
	u8_t n;
	u16_t length = 0;
	/* if here, we have either a new query or a retry on a previous query to process */
	p = pbuf_alloc(PBUF_TRANSPORT,
			SIZEOF_DNS_HDR + MDNS_MAX_NAME_LENGTH * 2 + SIZEOF_DNS_QUERY, PBUF_RAM);
	if (p != NULL) {
		LWIP_ASSERT("pbuf must be in one piece", p->next == NULL);
		/* fill dns header */
		hdr = (struct mdns_hdr*) p->payload;
		os_memset(hdr, 0, SIZEOF_DNS_HDR);
		hdr->id = htons(id);
		hdr->flags1 = DNS_FLAG1_RESPONSE;

		if (type == MDNS_SD_ANSWER) {
			pHostname = DNS_SD_SERVICE;
			hdr->numanswers = htons(1);
		} else if (type == MDNS_SERVICE_REG_ANSWER) {
			pHostname = PUCK_SERVICE;
			hdr->numanswers = htons(type);
		} else {
			pHostname = name;
			hdr->numanswers = htons(type);
		}
		query = (char*) hdr + SIZEOF_DNS_HDR;
		--pHostname;
		/* convert hostname into suitable query format. */
		do {
			++pHostname;
			nptr = query;
			++query;
			for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
				*query = *pHostname;
				++query;
				++n;
			}
			*nptr = n;
		} while (*pHostname != 0);
		*query++ = '\0';
		/* fill dns query */

		if (type == MDNS_REG_ANSWER) {

			ans.type = htons(DNS_RRTYPE_A);
			ans.class = htons(DNS_RRCLASS_IN);
			ans.ttl = htonl(MDNS_SERVICE_TIME);
			ans.len = htons(DNS_IP_ADDR_LEN);
			length = DNS_IP_ADDR_LEN;

			MEMCPY( query, &ans, SIZEOF_DNS_ANSWER);

			/* resize the query */
			query = query + SIZEOF_DNS_ANSWER;

			/* set the local IP address */
			auth.src = host_addr.addr;
			MEMCPY( query, &auth, SIZEOF_MDNS_AUTH);
		}
		if (type == MDNS_SD_ANSWER) {

			ans.type = htons(DNS_RRTYPE_PTR);
			ans.class = htons(DNS_RRCLASS_IN);
			ans.ttl = htonl(300);
			ans.len = htons(os_strlen(PUCK_SERVICE) + 1 +1 );
			length = 0;

			MEMCPY( query, &ans, SIZEOF_DNS_ANSWER);

			/* resize the query */
			query = query + SIZEOF_DNS_ANSWER;
			pHostname = PUCK_SERVICE;
			--pHostname;

			/* convert hostname into suitable query format. */
			do {
				++pHostname;
				nptr = query;
				++query;
				for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
					*query = *pHostname;
					++query;
					++n;
				}
				*nptr = n;
			} while (*pHostname != 0);
			*query++ = '\0';
		}

		if (type == MDNS_SERVICE_REG_ANSWER) {

			ans.type = htons(DNS_RRTYPE_PTR);
			ans.class = htons(DNS_RRCLASS_IN);
			ans.ttl = htonl(MDNS_SERVICE_TIME);
			os_strcpy(tmpBuf, name);
			os_strcat(tmpBuf, ".");
			os_strcat(tmpBuf, PUCK_SERVICE);

			length = os_strlen(tmpBuf) + MDNS_LENGTH_ADD;
			ans.len = htons(length);
			length = 0;

			MEMCPY( query, &ans, SIZEOF_DNS_ANSWER);

			/* resize the query */
			query = query + SIZEOF_DNS_ANSWER;

			pHostname = tmpBuf;
			--pHostname;

			/* convert hostname into suitable query format. */
			do {
				++pHostname;
				nptr = query;
				++query;
				for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
					*query = *pHostname;
					++query;
					++n;
				}
				*nptr = n;
			} while (*pHostname != 0);
			*query++ = '\0';

			/* Service query*/
			pHostname = name;
			--pHostname;

			/* convert hostname into suitable query format. */
			do {
				++pHostname;
				nptr = query;
				++query;
				for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
					*query = *pHostname;
					++query;
					++n;
				}
				*nptr = n;
			} while (*pHostname != 0);

			/* Add to the service name the service local
			 * pointing to the beginning of the mDNS message*/
			*query++ = DNS_OFFSET_FLAG;
			*query++ = DNS_DEFAULT_OFFSET;

			/* fill the query */

			ans.type = htons(DNS_RRTYPE_SRV);
			ans.class = htons(DNS_RRCLASS_FLUSH_IN);
			ans.ttl = htonl(MDNS_SERVICE_TIME);
			os_strcpy(tmpBuf, host_name);
			os_strcat(tmpBuf, ".");
			os_strcat(tmpBuf, MDNS_LOCAL);
			length = os_strlen(tmpBuf) + MDNS_LENGTH_ADD;
			ans.len = htons(SIZEOF_MDNS_SERVICE + length);
			length = 0;
			MEMCPY( query, &ans, SIZEOF_DNS_ANSWER);

			/* resize the query */
			query = query + SIZEOF_DNS_ANSWER;
			/* fill the service properties */

			serv.prior = htons(0);
			serv.weight = htons(0);
			serv.port = htons(PUCK_PORT);
			MEMCPY( query, &serv, SIZEOF_MDNS_SERVICE);
			/* resize the query */
			query = query + SIZEOF_MDNS_SERVICE;

			pHostname = tmpBuf;
			--pHostname;

			/* convert hostname into suitable query format. */
			do {
				++pHostname;
				nptr = query;
				++query;
				for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
					*query = *pHostname;
					++query;
					++n;
				}
				*nptr = n;
			} while (*pHostname != 0);
			*query++ = '\0';

			/* TXT answer */
			pHostname = name;
			--pHostname;

			/* convert hostname into suitable query format. */
			do {
				++pHostname;
				nptr = query;
				++query;
				for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
					*query = *pHostname;
					++query;
					++n;
				}
				*nptr = n;
			} while (*pHostname != 0);

			/* Add to the service name the service local
			 * pointing to the beginning of the mDNS message*/
			*query++ = DNS_OFFSET_FLAG;
			*query++ = DNS_DEFAULT_OFFSET;

			/* fill the answer */
			ans.type = htons(DNS_RRTYPE_TXT);
			ans.class = htons(DNS_RRCLASS_IN);
			ans.ttl = htonl(MDNS_SERVICE_TIME);
			length = sizeof(SERVICE_DESCRIPTION);
			ans.len = htons(length);
			length = 0;
			MEMCPY( query, &ans, SIZEOF_DNS_ANSWER);

			/* resize the query */
			query = query + SIZEOF_DNS_ANSWER;

			pHostname = SERVICE_DESCRIPTION;
			--pHostname;

			/* convert hostname into suitable query format. */
			do {
				++pHostname;
				nptr = query;
				++query;
				for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
					*query = *pHostname;
					++query;
					++n;
				}
				*nptr = n;
			} while (*pHostname != 0);
			*query++ = '\0';
		}
		/* resize pbuf to the exact dns query */
		pbuf_realloc(p, (query + length) - ((char*) (p->payload)));

		/* send dns packet */
		/*add by tzx for AP + STA MDNS begin------*/
		sta_netif = (struct netif *)eagle_lwip_getif(0x00);
		ap_netif =  (struct netif *)eagle_lwip_getif(0x01);
		if(wifi_get_opmode() == 0x03 && wifi_get_broadcast_if() == 0x03 &&\
				sta_netif != NULL && ap_netif != NULL) {
			if(netif_is_up(sta_netif) && netif_is_up(ap_netif)) {

				p_sta = pbuf_alloc(PBUF_TRANSPORT,
							SIZEOF_DNS_HDR + MDNS_MAX_NAME_LENGTH * 2 + SIZEOF_DNS_QUERY, PBUF_RAM);
			  if (pbuf_copy (p_sta,p) != ERR_OK) {
				  os_printf("mdns_answer copying to new pbuf failed\n");
				  return -1;
			  }
			  netif_set_default(sta_netif);
			  err = udp_sendto(mdns_pcb, p_sta, &multicast_addr, DNS_MDNS_PORT);
			  pbuf_free(p_sta);
			  netif_set_default(ap_netif);
			}
		}
		/*add by tzx for AP + STA MDNS end------*/
		err = udp_sendto(mdns_pcb, p, &multicast_addr, DNS_MDNS_PORT);
		/* free pbuf */
		pbuf_free(p);
	} else {
		err = ERR_MEM;
	}

	return err;
}

/**
 * Send a mDNS service answer packet.
 *
 * @param name service name to query
 * @param id transaction ID in the DNS query packet
 * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise
 */
static err_t ICACHE_FLASH_ATTR
mdns_send_service(struct mdns_info *info, u8_t id) {
	err_t err;
	struct mdns_hdr *hdr;
	struct mdns_answer ans;
	struct mdns_service serv;
	struct mdns_auth auth;
	struct pbuf *p ,*p_sta;
	char *query, *nptr;
	const char *pHostname;
	char *device_info;
	const char *name = info->host_name;
	u8_t n;
	u8_t i = 0;
	u16_t length = 0;
	u8_t addr1 = 12, addr2 = 12;
	struct netif * sta_netif = NULL;
	struct netif * ap_netif = NULL;
	static char tmpBuf[PUCK_DATASHEET_SIZE + PUCK_SERVICE_LENGTH];
	/* if here, we have either a new query or a retry on a previous query to process */
	p = pbuf_alloc(PBUF_TRANSPORT,
			SIZEOF_DNS_HDR + MDNS_MAX_NAME_LENGTH * 2 + SIZEOF_DNS_QUERY, PBUF_RAM);
	if (p != NULL) {
		LWIP_ASSERT("pbuf must be in one piece", p->next == NULL);
		/* fill dns header */
		hdr = (struct mdns_hdr*) p->payload;
		os_memset(hdr, 0, SIZEOF_DNS_HDR);
		hdr->id = htons(id);
		hdr->flags1 = DNS_FLAG1_RESPONSE;
		hdr->numanswers = htons(4);
		query = (char*) hdr + SIZEOF_DNS_HDR;
		os_strcpy(tmpBuf, PUCK_SERVICE);

		pHostname = tmpBuf;
		--pHostname;

		/* convert hostname into suitable query format. */
		do {
			++pHostname;
			nptr = query;
			++query;
			++addr1;
			++addr2;
			for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
				*query = *pHostname;
				++query;
				++addr1;
				++addr2;
				++n;
			}
			*nptr = n;
		} while (*pHostname != 0);
		*query++ = '\0';
		length = sizeof(MDNS_LOCAL);
		addr1 -= length;
		length = os_strlen(PUCK_SERVICE) + 1;
		addr2 -= length;

		ans.type = htons(DNS_RRTYPE_PTR);
		ans.class = htons(DNS_RRCLASS_IN);
		ans.ttl = htonl(300);
		os_strcpy(tmpBuf, name);
		length = os_strlen(tmpBuf) + MDNS_LENGTH_ADD + 1;
		ans.len = htons(length);
		length = 0;

		MEMCPY( query, &ans, SIZEOF_DNS_ANSWER);
		/* resize the query */
		query = query + SIZEOF_DNS_ANSWER;

		pHostname = tmpBuf;
		--pHostname;
		/* convert hostname into suitable query format. */
		do {
			++pHostname;
			nptr = query;
			++query;
			for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
				*query = *pHostname;
				++query;
				++n;
			}
			*nptr = n;
		} while (*pHostname != 0);
		*query++ = DNS_OFFSET_FLAG;
		*query++ = DNS_DEFAULT_OFFSET;
		pHostname = name;
		--pHostname;
		/* convert hostname into suitable query format. */
		do {
			++pHostname;
			nptr = query;
			++query;
			for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
				*query = *pHostname;
				++query;
				++n;
			}
			*nptr = n;
		} while (*pHostname != 0);
		//*query++ = '\0';
		*query++ = DNS_OFFSET_FLAG;
		*query++ = DNS_DEFAULT_OFFSET;

		/* fill the answer */
		ans.type = htons(DNS_RRTYPE_TXT);
		ans.class = htons(DNS_RRCLASS_FLUSH_IN);
		ans.ttl = htonl(300);
//		length = os_strlen(TXT_DATA) + MDNS_LENGTH_ADD + 1;
		device_info = (char *)os_zalloc(50);
		ets_sprintf(device_info,"vendor = %s","Espressif");
		for(i = 0; i < 10 &&(info->txt_data[i] != NULL);i++) {
			length += os_strlen(info->txt_data[i]);
			length++;
		}
		length += os_strlen(device_info)+ 1 ;
		ans.len = htons(length);
		length = 0;
		MEMCPY( query, &ans, SIZEOF_DNS_ANSWER);
		query = query + SIZEOF_DNS_ANSWER;
		pHostname = device_info;
		--pHostname;
		/* convert hostname into suitable query format. */
		do {
			++pHostname;
			nptr = query;
			++query;
			for (n = 0;  *pHostname != 0; ++pHostname) {
				*query = *pHostname;
				++query;
				++n;
			}
			*nptr = n;
		} while (*pHostname != 0);
		i = 0;
		while(info->txt_data[i] != NULL && i < 10) {
			pHostname = info->txt_data[i];
			--pHostname;
			/* convert hostname into suitable query format. */
			do {
				++pHostname;
				nptr = query;
				++query;
				for (n = 0;  *pHostname != 0; ++pHostname) {
					*query = *pHostname;
					++query;
					++n;
				}
				*nptr = n;
			} while (*pHostname != 0);
			i++;
		}
//		*query++ = '\0';
		os_free(device_info);
		os_strcpy(tmpBuf, name);
		pHostname = tmpBuf;
		--pHostname;
		do {
			++pHostname;
			nptr = query;
			++query;
			for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
				*query = *pHostname;
				++query;
				++n;
			}
			*nptr = n;
		} while (*pHostname != 0);

		*query++ = DNS_OFFSET_FLAG;
		*query++ = DNS_DEFAULT_OFFSET;

		ans.type = htons(DNS_RRTYPE_SRV);
		ans.class = htons(DNS_RRCLASS_FLUSH_IN);
		ans.ttl = htonl(300);
		os_strcpy(tmpBuf,service_name);
		os_strcat(tmpBuf, ".");
		os_strcat(tmpBuf, MDNS_LOCAL);
		length = os_strlen(tmpBuf) + MDNS_LENGTH_ADD;
		ans.len = htons(SIZEOF_MDNS_SERVICE + length);
		length = 0;
		MEMCPY( query, &ans, SIZEOF_DNS_ANSWER);

		/* resize the query */
		query = query + SIZEOF_DNS_ANSWER;

		serv.prior = htons(0);
		serv.weight = htons(0);
		serv.port = htons(PUCK_PORT);
		MEMCPY( query, &serv, SIZEOF_MDNS_SERVICE);
		/* resize the query */
		query = query + SIZEOF_MDNS_SERVICE;

		pHostname = tmpBuf;
		--pHostname;
		do {
			++pHostname;
			nptr = query;
			++query;
			for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
				*query = *pHostname;
				++query;
				++n;
			}
			*nptr = n;
		} while (*pHostname != 0);
		*query++ = '\0';
		/* set the name of the authority field.
		 * The same name as the Query using the offset address*/
		os_strcpy(tmpBuf,service_name);
		os_strcat(tmpBuf, ".");
		os_strcat(tmpBuf, MDNS_LOCAL);
		pHostname = tmpBuf;
		--pHostname;
		do {
			++pHostname;
			nptr = query;
			++query;
			for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
				*query = *pHostname;
				++query;
				++n;
			}
			*nptr = n;
		} while (*pHostname != 0);
		*query++ = '\0';
		/* set the name of the authority field.
		 * The same name as the Query using the offset address*/
		//*query++ = DNS_OFFSET_FLAG;
		//*query++ = DNS_DEFAULT_OFFSET;
		ans.type = htons(DNS_RRTYPE_A);
		ans.class = htons(DNS_RRCLASS_FLUSH_IN);
		ans.ttl = htonl(300);
		ans.len = htons(DNS_IP_ADDR_LEN);

		MEMCPY( query, &ans, SIZEOF_DNS_ANSWER);

		/* resize the query */
		query = query + SIZEOF_DNS_ANSWER;

		/* fill the payload of the mDNS message */
		/* set the local IP address */
		auth.src = host_addr.addr; //ipAddr;
		MEMCPY( query, &auth, SIZEOF_MDNS_AUTH);
		/* resize the query */
		query = query + SIZEOF_MDNS_AUTH;

		/* set the name of the authority field.
		 * The same name as the Query using the offset address*/

		/* resize pbuf to the exact dns query */
		pbuf_realloc(p, (query) - ((char*) (p->payload)));
		/* send dns packet */
		sta_netif = (struct netif *)eagle_lwip_getif(0x00);
		ap_netif =  (struct netif *)eagle_lwip_getif(0x01);
		if(wifi_get_opmode() == 0x03 && wifi_get_broadcast_if() == 0x03 &&\
				sta_netif != NULL && ap_netif != NULL) {
			if(netif_is_up(sta_netif) && netif_is_up(ap_netif)) {

				p_sta = pbuf_alloc(PBUF_TRANSPORT,
							SIZEOF_DNS_HDR + MDNS_MAX_NAME_LENGTH * 2 + SIZEOF_DNS_QUERY, PBUF_RAM);
			  if (pbuf_copy (p_sta,p) != ERR_OK) {
				  os_printf("mdns_send_service copying to new pbuf failed\n");
				  return -1;
			  }
			  netif_set_default(sta_netif);
			  err = udp_sendto(mdns_pcb, p_sta, &multicast_addr, DNS_MDNS_PORT);
			  pbuf_free(p_sta);
			  netif_set_default(ap_netif);
			}
		}
		err = udp_sendto(mdns_pcb, p, &multicast_addr, DNS_MDNS_PORT);

		/* free pbuf */
		pbuf_free(p);
	} else {
		os_printf("ERR_MEM \n");
		err = ERR_MEM;
	}

	return err;
}

/**
 * Receive input function for DNS response packets arriving for the dns UDP pcb.
 *
 * @params see udp.h
 */
static void ICACHE_FLASH_ATTR
mdns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr,
		u16_t port) {
	u8_t i;
	struct mdns_hdr *hdr;
	u8_t nquestions;
	LWIP_UNUSED_ARG(arg);
	LWIP_UNUSED_ARG(pcb);
	LWIP_UNUSED_ARG(addr);
	LWIP_UNUSED_ARG(port);
	struct mdns_info *info = (struct mdns_info *)arg;
	/* is the dns message too big ? */
	if (p->tot_len > DNS_MSG_SIZE) {
		LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too big\n"));
		/* free pbuf and return */
		goto memerr1;
	}

	/* is the dns message big enough ? */
	if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY + SIZEOF_DNS_ANSWER)) {
		LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n"));
		/* free pbuf and return */
		goto memerr1;
	}
	/* copy dns payload inside static buffer for processing */
	if (pbuf_copy_partial(p, mdns_payload, p->tot_len, 0) == p->tot_len) {
		/* The ID in the DNS header should be our entry into the name table. */
		hdr = (struct mdns_hdr*) mdns_payload;

		i = htons(hdr->id);
		if (i < DNS_TABLE_SIZE) {

			nquestions = htons(hdr->numquestions);
			//nanswers   = htons(hdr->numanswers);
			/* if we have a question send an answer if necessary */
			if (nquestions > 0) {
				/* MDNS_DS_DOES_NAME_CHECK */
				/* Check if the name in the "question" part match with the name of the MDNS DS service. */
				if (mdns_compare_name((unsigned char *) DNS_SD_SERVICE,
						(unsigned char *) mdns_payload + SIZEOF_DNS_HDR) == 0) {
					/* respond with the puck service*/
					mdns_answer(MDNS_SD_ANSWER, PUCK_SERVICE, 0);
				} else if (mdns_compare_name((unsigned char *) PUCK_SERVICE,
						(unsigned char *) mdns_payload + SIZEOF_DNS_HDR) == 0) {
					/* respond with the puck service*/
					mdns_send_service(info, 0);
				} else
					goto memerr2;
			}
		}
	}
	goto memerr2;
	memerr2:
	os_memset(mdns_payload , 0 ,DNS_MSG_SIZE);
	memerr1:
	/* free pbuf */
	pbuf_free(p);
	return;
}

/**
 * close the UDP pcb .
 */
void ICACHE_FLASH_ATTR
mdns_close(void)
{
	uint8 text_index = 0;
	if (mdns_pcb != NULL && ms_info != NULL) {
		udp_remove(mdns_pcb);
		for(text_index = 0;text_index < 10;text_index++) {
				if(ms_info->txt_data[text_index] != NULL) {
					os_free(ms_info->txt_data[text_index]);
					ms_info->txt_data[text_index] = NULL;
				}
		}
		if (ms_info->host_name != NULL) {
			os_free(ms_info->host_name);
			ms_info->host_name = NULL;
		}
		if (ms_info->server_name != NULL) {
			os_free(ms_info->server_name);
			ms_info->server_name = NULL;
		}
		os_free(ms_info);
		mdns_pcb = NULL;
		ms_info = NULL;

	}
}

void ICACHE_FLASH_ATTR
mdns_set_name(const char *name)
{
	//strcpy(host_name, name);
	os_strcpy(service_name, name);
}

void ICACHE_FLASH_ATTR
mdns_enable(void)
{
	if(mdns_flag == 0) {
		udp_recv(mdns_pcb, mdns_recv, NULL);
	}
}

void ICACHE_FLASH_ATTR
mdns_disable(void)
{
	if (mdns_flag == 1) {
		udp_recv(mdns_pcb, NULL, NULL);
	}
}

/**
 * close the UDP pcb .
 */
char* ICACHE_FLASH_ATTR
mdns_get_hostname(void) {
	//strcpy(host_name, name);
	char *name = host_name;
	if (host_name[0] != 0 ) {
	return name;
	} else {
		return ("Espressif");
	}
}

void ICACHE_FLASH_ATTR
mdns_set_hostname(char *name) {
	if (name == NULL) {
		os_strncpy(host_name, "Espressif", os_strlen("Espressif")+3);
		return;
	}
	if (os_strlen(name) + 3 <= MDNS_NAME_LENGTH ){
		os_strncpy(host_name, name, os_strlen(name) );
//		os_memset(host_name + os_strlen(host_name) ,0x00,3);
	} else {
		os_strncpy(host_name, name, MDNS_NAME_LENGTH);
	}
}

void ICACHE_FLASH_ATTR
mdns_set_servername(const char *name) {
	if (name == NULL) {
		PUCK_SERVICE = "_Espressif._tcp._local";
	}else {
		os_sprintf(server_name ,"_%s._tcp.local",name);
		PUCK_SERVICE = server_name;
	}
}

char* ICACHE_FLASH_ATTR
mdns_get_servername(void) {
	char *name = PUCK_SERVICE;
	if (name == NULL) {
		PUCK_SERVICE = "_Espressif._tcp._local";
	}
	return name;
}

void ICACHE_FLASH_ATTR
mdns_server_unregister(void) {
	struct ip_addr ap_host_addr;
	struct ip_info ipconfig;
	if(register_flag == 1){
		if (igmp_leavegroup(&host_addr, &multicast_addr) != ERR_OK) {
			os_printf("sta udp_leave_multigrup failed!\n");
			return;
		};
		if(wifi_get_opmode() == 0x03 || wifi_get_opmode() == 0x02) {
		   wifi_get_ip_info(SOFTAP_IF, &ipconfig);
		   ap_host_addr.addr = ipconfig.ip.addr;
		   if (igmp_leavegroup(&ap_host_addr, &multicast_addr) != ERR_OK) {
				os_printf("ap udp_join_multigrup failed!\n");
				return;
			};
		}
		register_flag = 0;
	}
}

void ICACHE_FLASH_ATTR
mdns_server_register(void) {

	if (register_flag == 1) {
		os_printf("mdns server is already registered !\n");
		return;
	} else if (igmp_joingroup(&host_addr, &multicast_addr) != ERR_OK) {
		os_printf("udp_join_multigrup failed!\n");
		return;
	};
	register_flag = 1;
}

void ICACHE_FLASH_ATTR
mdns_reg(struct mdns_info *info) {

   static uint8 i = 0;
   if (i <= 3) {
	   mdns_send_service(info,0);
	   i++;
   } else {
	   os_timer_disarm(&mdns_timer);
   }
}

/**
 * Initialize the resolver: set up the UDP pcb and configure the default server
 * (NEW IP).
 */
void ICACHE_FLASH_ATTR
mdns_init(struct mdns_info *info) {
	/* initialize default DNS server address */
	multicast_addr.addr = DNS_MULTICAST_ADDRESS;
	struct ip_addr ap_host_addr;
	struct ip_info ipconfig;
	uint8 text_index = 0;
	ms_info = (struct mdns_info *)os_zalloc(sizeof(struct mdns_info));
	if (ms_info != NULL) {
		os_memcpy(ms_info,info,sizeof(struct mdns_info));
		ms_info->host_name = (char *)os_zalloc(os_strlen(info->host_name)+1);
		os_memcpy(ms_info->host_name,info->host_name,os_strlen(info->host_name));
		ms_info->server_name = (char *)os_zalloc(os_strlen(info->server_name)+1);
		os_memcpy(ms_info->server_name,info->server_name,os_strlen(info->server_name));
		for(text_index = 0;text_index < 10;text_index++) {
			if(info->txt_data[text_index] != NULL) {
				ms_info->txt_data[text_index] = (char *)os_zalloc(os_strlen(info->txt_data[text_index])+1);
				os_memcpy(ms_info->txt_data[text_index],info->txt_data[text_index],os_strlen(info->txt_data[text_index]));
			} else {
				break;
			}

		}
	} else {
		os_printf("ms_info alloc failed\n");
		return;
	}
	if (ms_info->ipAddr == 0) {
		os_printf("mdns ip error!\n ");
		return;
	}
	host_addr.addr = ms_info->ipAddr ;
	LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n"));
	//get the datasheet from PUCK
	mdns_set_hostname(ms_info->host_name);
	mdns_set_servername(ms_info->server_name);
	mdns_set_name(ms_info->host_name);

	// get the host name as instrumentName_serialNumber for MDNS
	// set the name of the service, the same as host name
	os_printf("host_name = %s\n", host_name);
	os_printf("server_name = %s\n", PUCK_SERVICE);
	if (ms_info->server_port == 0)
	{
		PUCK_PORT = 80;
	} else {
		PUCK_PORT = ms_info->server_port;
	}

	/* initialize mDNS */
	mdns_pcb = udp_new();

	if (mdns_pcb != NULL) {
		/* join to the multicast address 224.0.0.251 */
		if(wifi_get_opmode() == 0x03 || wifi_get_opmode() == 0x01) {
			if (igmp_joingroup(&host_addr, &multicast_addr) != ERR_OK) {
				os_printf("sta udp_join_multigrup failed!\n");
				return;
			};
		}
		if(wifi_get_opmode() == 0x03 || wifi_get_opmode() == 0x02) {
		   wifi_get_ip_info(SOFTAP_IF, &ipconfig);
		   ap_host_addr.addr = ipconfig.ip.addr;
		   if (igmp_joingroup(&ap_host_addr, &multicast_addr) != ERR_OK) {
				os_printf("ap udp_join_multigrup failed!\n");
				return;
			};
		}
		register_flag = 1;
		/* join to any IP address at the port 5353 */
		if (udp_bind(mdns_pcb, IP_ADDR_ANY, DNS_MDNS_PORT) != ERR_OK) {
			os_printf("udp_bind failed!\n");
			return;
		};

		/*loopback function for the multicast(224.0.0.251) messages received at port 5353*/
//		mdns_enable();
		udp_recv(mdns_pcb, mdns_recv, ms_info);
		mdns_flag = 1;
		/*
		 * Register the name of the instrument
		 */

		os_timer_disarm(&mdns_timer);
		os_timer_setfn(&mdns_timer, (os_timer_func_t *)mdns_reg,ms_info);
		os_timer_arm(&mdns_timer, 1000, 1);
	}
}

#endif /* LWIP_MDNS */
