void pico_mock_destroy(struct pico_device *dev)
{
    struct mock_device search = {
        .dev = dev
    };
    struct mock_device*mock = pico_tree_findKey(&mock_device_tree, &search);
    struct mock_frame*nxt;

    if(!mock)
        return;

    nxt = mock->in_head;
    while(nxt != NULL) {
        mock->in_head = mock->in_head->next;
        PICO_FREE(nxt);
        nxt = mock->in_head;
    }
    nxt = mock->out_head;
    while(nxt != NULL) {
        mock->out_head = mock->out_head->next;
        PICO_FREE(nxt);
        nxt = mock->out_head;
    }
    pico_tree_delete(&mock_device_tree, mock);
}
Exemple #2
0
static struct pico_dns_ns *pico_dns_client_add_ns(struct pico_ip4 *ns_addr)
{
    struct pico_dns_ns *dns = NULL, *found = NULL, test = {{0}};

    dns = pico_zalloc(sizeof(struct pico_dns_ns));
    if (!dns) {
        pico_err = PICO_ERR_ENOMEM;
        return NULL;
    }

    dns->ns = *ns_addr;

    found = pico_tree_insert(&NSTable, dns);
    if (found) { /* nameserver already present */
        pico_free(dns);
        return found;
    }

    /* default NS found, remove it */
    pico_string_to_ipv4(PICO_DNS_NS_GOOGLE, &test.ns.addr);
    found = pico_tree_findKey(&NSTable, &test);
    if (found && (found->ns.addr != ns_addr->addr))
        pico_dns_client_del_ns(&found->ns);

    return dns;
}
static struct pico_dns_key *pico_dns_client_idcheck(uint16_t id)
{
  struct pico_dns_key test;

  test.id = id;
  return pico_tree_findKey(&DNSTable,&test);
}
static int pico_mock_poll(struct pico_device *dev, int loop_score)
{
    struct mock_device search = {
        .dev = dev
    };
    struct mock_device*mock = pico_tree_findKey(&mock_device_tree, &search);
    struct mock_frame*nxt;

    if(!mock)
        return 0;

    if (loop_score <= 0)
        return 0;

    while(mock->in_head != NULL && loop_score > 0)
    {
        pico_stack_recv(dev, mock->in_head->buffer, (uint32_t)mock->in_head->len);
        loop_score--;

        PICO_FREE(mock->in_head->buffer);

        if(mock->in_tail == mock->in_head) {
            PICO_FREE(mock->in_head);
            mock->in_tail = mock->in_head = NULL;
            return loop_score;
        }

        nxt = mock->in_head->next;
        PICO_FREE(mock->in_head);
        mock->in_head = nxt;
    }
    return loop_score;
}
static struct pico_dhcp_server_negotiation *pico_dhcp_server_add_negotiation(struct pico_device *dev, struct pico_dhcp_hdr *hdr)
{
    struct pico_dhcp_server_negotiation *dhcpn = NULL;
    struct pico_dhcp_server_setting test = {
        0
    };

    if (pico_dhcp_server_find_negotiation(hdr->xid))
        return NULL;

    dhcpn = PICO_ZALLOC(sizeof(struct pico_dhcp_server_negotiation));
    if (!dhcpn) {
        pico_err = PICO_ERR_ENOMEM;
        return NULL;
    }

    dhcpn->xid = hdr->xid;
    dhcpn->state = PICO_DHCP_STATE_DISCOVER;
    dhcpn->bcast = ((short_be(hdr->flags) & PICO_DHCP_FLAG_BROADCAST) != 0) ? (1) : (0);
    memcpy(dhcpn->hwaddr.addr, hdr->hwaddr, PICO_SIZE_ETH);

    test.dev = dev;
    dhcpn->dhcps = pico_tree_findKey(&DHCPSettings, &test);
    if (!dhcpn->dhcps) {
        dhcps_dbg("DHCP server WARNING: received DHCP message on unconfigured link %s\n", dev->name);
        PICO_FREE(dhcpn);
        return NULL;
    }

    dhcp_negotiation_set_ciaddr(dhcpn);
    pico_tree_insert(&DHCPNegotiations, dhcpn);
    return dhcpn;
}
static struct pico_dhcp_client_cookie *pico_dhcp_client_add_cookie(uint32_t xid, struct pico_device *dev, void (*cb)(void *dhcpc, int code), uint32_t *uid)
{
    struct pico_dhcp_client_cookie *dhcpc = NULL, *found = NULL, test = {
        0
    };

    test.xid = xid;
    found = pico_tree_findKey(&DHCPCookies, &test);
    if (found) {
        pico_err = PICO_ERR_EAGAIN;
        return NULL;
    }

    dhcpc = pico_zalloc(sizeof(struct pico_dhcp_client_cookie));
    if (!dhcpc) {
        pico_err = PICO_ERR_ENOMEM;
        return NULL;
    }

    dhcpc->state = DHCP_CLIENT_STATE_INIT;
    dhcpc->status = DHCP_CLIENT_STATUS_INIT;
    dhcpc->xid = xid;
    dhcpc->uid = uid;
    *(dhcpc->uid) = 0;
    dhcpc->cb = cb;
    dhcpc->dev = dev;

    pico_tree_insert(&DHCPCookies, dhcpc);
    return dhcpc;
}
Exemple #7
0
static void pico_dns_client_retransmission(pico_time now, void *arg)
{
    struct pico_dns_query *q = NULL;
    struct pico_dns_query dummy;
    IGNORE_PARAMETER(now);

    if(!arg)
        return;

    /* search for the dns query and free used space */
    dummy.id = *(uint16_t *)arg;
    q = (struct pico_dns_query *)pico_tree_findKey(&DNSTable, &dummy);
    PICO_FREE(arg);

    /* dns query successful? */
    if (!q) {
        return;
    }

    if (q->retrans++ <= PICO_DNS_CLIENT_MAX_RETRANS) {
        q->q_ns = pico_dns_client_next_ns(&q->q_ns.ns);
        pico_dns_client_send(q);
    } else {
        pico_err = PICO_ERR_EIO;
        q->callback(NULL, q->arg);
        pico_dns_client_del_query(q->id);
    }
}
Exemple #8
0
static struct pico_dhcp_server_setting *pico_dhcp_server_add_setting(struct pico_dhcp_server_setting *setting)
{
  uint16_t port = PICO_DHCPD_PORT;
  struct pico_dhcp_server_setting *dhcps = NULL, *found = NULL, test = {0};
  struct pico_ipv4_link *link = NULL;
  
  link = pico_ipv4_link_get(&setting->server_ip);
  if (!link) {
    pico_err = PICO_ERR_EINVAL;
    return NULL;
  }
  test.dev = setting->dev;
  found = pico_tree_findKey(&DHCPSettings, &test);
  if (found) {
    pico_err = PICO_ERR_EINVAL;
    return NULL;
  }
  dhcps = pico_zalloc(sizeof(struct pico_dhcp_server_setting));
  if (!dhcps) {
    pico_err = PICO_ERR_ENOMEM;
    return NULL;
  }

  dhcps->lease_time = setting->lease_time;
  dhcps->pool_start = setting->pool_start;
  dhcps->pool_next = setting->pool_next;
  dhcps->pool_end = setting->pool_end;
  dhcps->dev = link->dev;
  dhcps->server_ip = link->address;
  dhcps->netmask = link->netmask;

  /* default values if not provided */
  if (!dhcps->pool_start)
    dhcps->pool_start = (dhcps->server_ip.addr & dhcps->netmask.addr) | DHCP_SERVER_POOL_START;
  if (!dhcps->pool_end)
    dhcps->pool_end = (dhcps->server_ip.addr & dhcps->netmask.addr) | DHCP_SERVER_POOL_END;
  if (!dhcps->lease_time)
    dhcps->lease_time = DHCP_SERVER_LEASE_TIME;
  dhcps->pool_next = dhcps->pool_start;

  dhcps->s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcpd_wakeup);
  if (!dhcps->s) {
    dhcps_dbg("DHCP server ERROR: failure opening socket (%s)\n", strerror(pico_err));
    pico_free(dhcps);
    return NULL;
  }
  if (pico_socket_bind(dhcps->s, &dhcps->server_ip, &port) < 0) {
    dhcps_dbg("DHCP server ERROR: failure binding socket (%s)\n", strerror(pico_err));
    pico_free(dhcps);
    return NULL;
  }
  
  pico_tree_insert(&DHCPSettings, dhcps);
  return dhcps;
}
Exemple #9
0
static struct pico_dhcp_server_negotiation *pico_dhcp_server_find_negotiation(uint32_t xid)
{
  struct pico_dhcp_server_negotiation test = {0}, *found = NULL;

  test.xid = xid;
  found = pico_tree_findKey(&DHCPNegotiations, &test);
  if (found)
    return found;
  else
    return NULL;
}
Exemple #10
0
static int ipfilter_apply_filter(struct pico_frame *f, struct filter_node *pkt)
{
    struct filter_node *filter_frame = NULL;
    filter_frame = pico_tree_findKey(&filter_tree, pkt);
    if(filter_frame)
    {
        filter_frame->function_ptr(filter_frame, f);
        return 1;
    }

    return 0;
}
Exemple #11
0
static struct pico_dhcp_client_cookie *pico_dhcp_client_find_cookie(uint32_t xid)
{
    struct pico_dhcp_client_cookie test = {
        0
    }, *found = NULL;

    test.xid = xid;
    found = pico_tree_findKey(&DHCPCookies, &test);
    if (found)
        return found;
    else
        return NULL;
}
Exemple #12
0
static struct pico_dns_query *pico_dns_client_find_query(uint16_t id)
{
    struct pico_dns_query test = {
        0
    }, *found = NULL;

    test.id = id;
    found = pico_tree_findKey(&DNSTable, &test);
    if (found)
        return found;
    else
        return NULL;
}
int pico_dhcp_server_destroy(struct pico_device *dev)
{
    struct pico_dhcp_server_setting *found, test = { 0 };
    test.dev = dev;
    found = pico_tree_findKey(&DHCPSettings, &test);
    if (!found) {
        pico_err = PICO_ERR_ENOENT;
        return -1;
    }

    pico_tree_delete(&DHCPSettings, found);
    PICO_FREE(found);
    return 0;
}
Exemple #14
0
static int pico_dns_client_del_query(uint16_t id)
{
    struct pico_dns_query test = {
        0
    }, *found = NULL;

    test.id = id;
    found = pico_tree_findKey(&DNSTable, &test);
    if (!found)
        return -1;

    pico_free(found->query);
    pico_socket_close(found->s);
    pico_tree_delete(&DNSTable, found);
    pico_free(found);
    return 0;
}
Exemple #15
0
static int pico_dhcp_client_del_cookie(uint32_t xid)
{
    struct pico_dhcp_client_cookie test = {
        0
    }, *found = NULL;

    test.xid = xid;
    found = pico_tree_findKey(&DHCPCookies, &test);
    if (!found)
        return -1;

    pico_socket_close(found->s);
    pico_ipv4_link_del(found->dev, found->address);
    pico_tree_delete(&DHCPCookies, found);
    pico_free(found);
    return 0;
}
Exemple #16
0
static int pico_dns_client_del_ns(struct pico_ip4 *ns_addr)
{
    struct pico_dns_ns test = {{0}}, *found = NULL;

    test.ns = *ns_addr;
    found = pico_tree_findKey(&NSTable, &test);
    if (!found)
        return -1;

    pico_tree_delete(&NSTable, found);
    pico_free(found);

    /* no NS left, add default NS */
    if (pico_tree_empty(&NSTable))
        pico_dns_client_init();

    return 0;
}
Exemple #17
0
static void next_ping(uint32_t now, void *arg)
{
  struct pico_icmp4_ping_cookie *newcookie, *cookie = (struct pico_icmp4_ping_cookie *)arg;
  IGNORE_PARAMETER(now);

	if(pico_tree_findKey(&Pings,cookie)){
    if (cookie->seq < cookie->count) {
      newcookie = pico_zalloc(sizeof(struct pico_icmp4_ping_cookie));
      if (!newcookie)
        return;
      memcpy(newcookie, cookie, sizeof(struct pico_icmp4_ping_cookie));
      newcookie->seq++;

	    pico_tree_insert(&Pings,newcookie);
      send_ping(newcookie);
    }
  }
}
static struct pico_dhcp_server_setting *pico_dhcp_server_add_setting(struct pico_dhcp_server_setting *setting)
{
    struct pico_dhcp_server_setting *dhcps = NULL, *found = NULL, test = {
        0
    };
    struct pico_ipv4_link *link = NULL;

    link = pico_ipv4_link_get(&setting->server_ip);
    if (!link) {
        pico_err = PICO_ERR_EINVAL;
        return NULL;
    }

    test.dev = setting->dev;
    found = pico_tree_findKey(&DHCPSettings, &test);
    if (found) {
        pico_err = PICO_ERR_EINVAL;
        return NULL;
    }

    dhcps = PICO_ZALLOC(sizeof(struct pico_dhcp_server_setting));
    if (!dhcps) {
        pico_err = PICO_ERR_ENOMEM;
        return NULL;
    }

    dhcps->lease_time = setting->lease_time;
    dhcps->pool_start = setting->pool_start;
    dhcps->pool_next = setting->pool_next;
    dhcps->pool_end = setting->pool_end;
    dhcps->dev = link->dev;
    dhcps->server_ip = link->address;
    dhcps->netmask = link->netmask;

    /* default values if not provided */
    dhcps_set_default_lease_time_if_not_provided(dhcps);
    dhcps_set_default_pool_end_if_not_provided(dhcps);
    dhcps_set_default_pool_start_if_not_provided(dhcps);

    dhcps->pool_next = dhcps->pool_start;

    return dhcps_try_open_socket(dhcps);

}
Exemple #19
0
static void ping_timeout(uint32_t now, void *arg)
{
  struct pico_icmp4_ping_cookie *cookie = (struct pico_icmp4_ping_cookie *)arg;
  IGNORE_PARAMETER(now);

  if(pico_tree_findKey(&Pings,cookie)){
    if (cookie->err == PICO_PING_ERR_PENDING) {
      struct pico_icmp4_stats stats;
      stats.dst = cookie->dst;
      stats.seq = cookie->seq;
      stats.time = 0;
      stats.size = cookie->size;
      stats.err = PICO_PING_ERR_TIMEOUT;
      dbg(" ---- Ping timeout!!!\n");
      cookie->cb(&stats);
    }

    pico_tree_delete(&Pings,cookie);
    pico_free(cookie);
  }
}
Exemple #20
0
static struct pico_dhcp_server_negotiation *pico_dhcp_server_add_negotiation(struct pico_device *dev, struct pico_dhcp_hdr *hdr)
{
  struct pico_dhcp_server_negotiation *dhcpn = NULL;
  struct pico_dhcp_server_setting test = {0};
  struct pico_ip4 *ciaddr = NULL;

  if (pico_dhcp_server_find_negotiation(hdr->xid))
    return NULL;

  dhcpn = pico_zalloc(sizeof(struct pico_dhcp_server_negotiation));
  if (!dhcpn) {
    pico_err = PICO_ERR_ENOMEM;
    return NULL;
  }

  dhcpn->xid = hdr->xid;
  dhcpn->state = PICO_DHCP_STATE_DISCOVER;
  memcpy(dhcpn->hwaddr.addr, hdr->hwaddr, PICO_SIZE_ETH);

  test.dev = dev;
  dhcpn->dhcps = pico_tree_findKey(&DHCPSettings, &test);
  if (!dhcpn->dhcps) {
    dhcps_dbg("DHCP server WARNING: received DHCP message on unconfigured link %s\n", dev->name);
    pico_free(dhcpn);
    return NULL;
  }

  ciaddr = pico_arp_reverse_lookup(&dhcpn->hwaddr);
  if (!ciaddr) {
    dhcpn->ciaddr.addr = dhcpn->dhcps->pool_next;
    dhcpn->dhcps->pool_next = long_be(long_be(dhcpn->dhcps->pool_next) + 1);
    pico_arp_create_entry(dhcpn->hwaddr.addr, dhcpn->ciaddr, dhcpn->dhcps->dev);
  } else {
    dhcpn->ciaddr = *ciaddr;
  }

  pico_tree_insert(&DHCPNegotiations, dhcpn);
  return dhcpn; 
}
Exemple #21
0
static void ping_recv_reply(struct pico_frame *f)
{
  struct pico_icmp4_ping_cookie test, *cookie;
  struct pico_icmp4_hdr *hdr = (struct pico_icmp4_hdr *) f->transport_hdr;
  test.id  = short_be(hdr->hun.ih_idseq.idseq_id );
  test.seq = short_be(hdr->hun.ih_idseq.idseq_seq);

  cookie = pico_tree_findKey(&Pings, &test);
  if (cookie) {
    struct pico_icmp4_stats stats;
    cookie->err = PICO_PING_ERR_REPLIED;
    stats.dst = cookie->dst;
    stats.seq = cookie->seq;
    stats.size = cookie->size;
    stats.time = pico_tick - cookie->timestamp;
    stats.err = cookie->err;
    stats.ttl = ((struct pico_ipv4_hdr *)f->net_hdr)->ttl;
		if(cookie->cb != NULL)
    	cookie->cb(&stats);
  } else {
    dbg("Reply for seq=%d, not found.\n", test.seq);
  }
}
Exemple #22
0
static int pico_mock_send(struct pico_device *dev, void *buf, int len)
{
    struct mock_device search = {
        .dev = dev
    };
    struct mock_device*mock = pico_tree_findKey(&mock_device_tree, &search);
    struct mock_frame*frame;

    if(!mock)
        return 0;

    if (len > MOCK_MTU)
        return 0;

    frame = PICO_ZALLOC(sizeof(struct mock_frame));
    if(!frame) {
        return 0;
    }

    if(mock->out_head == NULL)
        mock->out_head = frame;
    else
        mock->out_tail->next = frame;

    mock->out_tail = frame;

    mock->out_tail->buffer = PICO_ZALLOC((uint32_t)len);
    if(!mock->out_tail->buffer)
        return 0;

    memcpy(mock->out_tail->buffer, buf, (uint32_t)len);
    mock->out_tail->len = len;

    return len;

}
static void pico_dns_client_callback(uint16_t ev, struct pico_socket *s)
{
  char *q_qname, *q_suf, *a_hdr, *a_qname, *a_suf, *a_rdata;
  struct dns_message_hdr *hdr;
  struct dns_query_suffix query_suf;
  struct dns_answer_suffix answer_suf;
  struct pico_dns_key test, *key;
  char *answer;
  char dns_answer[PICO_DNS_MAX_RESPONSE_LEN] = {0};
  uint8_t valid_suffix = 0;
  uint16_t compression = 0;
  int i = 0, r = 0;

  if (ev & PICO_SOCK_EV_RD) {
    r = pico_socket_read(s, dns_answer, PICO_DNS_MAX_RESPONSE_LEN);
    pico_socket_close(s);
    if (r == PICO_DNS_MAX_RESPONSE_LEN || r < (int)sizeof(struct dns_message_hdr)) {
      dns_dbg("DNS ERROR: received incorrect number(%d) of bytes\n", r);
      return;
    }

    /* Check header validity */
    a_hdr = dns_answer;
    hdr = (struct dns_message_hdr *) a_hdr;
    pico_dns_client_hdr_ntoh(hdr);
    if (GET_FLAG_QR(hdr) != PICO_DNS_QR_RESPONSE || GET_FLAG_OPCODE(hdr) != PICO_DNS_OPCODE_QUERY 
        || GET_FLAG_TC(hdr) == PICO_DNS_TC_IS_TRUNCATED || GET_FLAG_RCODE(hdr) != PICO_DNS_RCODE_NO_ERROR) {
      dns_dbg("DNS ERROR: OPCODE %d | TC %d | RCODE %d\n", GET_FLAG_OPCODE(hdr), GET_FLAG_TC(hdr), GET_FLAG_RCODE(hdr));
      return;
    }

    if (hdr->ancount < 1 || r < (int)(sizeof(struct dns_message_hdr) + hdr->qdcount * sizeof(struct dns_query_suffix)
            + hdr->ancount * sizeof(struct dns_answer_suffix))) {
      dns_dbg("DNS ERROR: ancount < 1 OR received number(%d) of bytes too low\n", r);
      return;
    }

    /* Find DNS key */
    test.id = hdr->id;

    key = pico_tree_findKey(&DNSTable,&test);
    if (!key) {
      dns_dbg("DNS WARNING: key with id %u not found\n", hdr->id);
      return;
    }
    key->retrans = 0;

    /* Check query suffix validity */
    q_qname = a_hdr + sizeof(struct dns_message_hdr);
    q_suf = pico_dns_client_seek(q_qname);
    query_suf = *(struct dns_query_suffix *) q_suf;
    if (short_be(query_suf.qtype) != key->qtype || short_be(query_suf.qclass) != key->qclass) {
      dns_dbg("DNS ERROR: received qtype (%u) or qclass (%u) incorrect\n", short_be(query_suf.qtype), short_be(query_suf.qclass));
      return;
    }

    /* Seek answer suffix */
    a_qname = q_suf + sizeof(struct dns_query_suffix);
    a_suf = a_qname;
    while(i++ < hdr->ancount) {
      uint16_t comp_h = short_from(a_suf);
      compression = short_be(comp_h);
      switch (compression >> 14)
      {
        case PICO_DNS_POINTER:
          while (compression >> 14 == PICO_DNS_POINTER) {
            dns_dbg("DNS: pointer\n");
            a_suf += sizeof(uint16_t);
            comp_h = short_from(a_suf);
            compression = short_be(comp_h);
          }
          break;

        case PICO_DNS_LABEL:
          dns_dbg("DNS: label\n");
          a_suf = pico_dns_client_seek(a_qname);
          break;

        default:
          dns_dbg("DNS ERROR: incorrect compression (%u) value\n", compression);
          return;
      }

      /* Check answer suffix validity */
      answer_suf = *(struct dns_answer_suffix *)a_suf;
      if (short_be(answer_suf.qtype) != key->qtype || short_be(answer_suf.qclass) != key->qclass) {
        dns_dbg("DNS WARNING: received qtype (%u) or qclass (%u) incorrect\n", short_be(answer_suf.qtype), short_be(answer_suf.qclass));
        a_suf = a_suf + sizeof(struct dns_answer_suffix) + short_be(answer_suf.rdlength);
        continue;
      }

      if (short_be(answer_suf.ttl) > PICO_DNS_MAX_TTL) {
        dns_dbg("DNS WARNING: received TTL (%u) > MAX (%u)\n", short_be(answer_suf.ttl), PICO_DNS_MAX_TTL);
        a_suf = a_suf + sizeof(struct dns_answer_suffix) + short_be(answer_suf.rdlength);
        continue;
      }

      valid_suffix = 1;
      break;
    }

    if (!valid_suffix) {
       dns_dbg("DNS ERROR: invalid dns answer suffix\n");
       return;
    }

    a_rdata = a_suf + sizeof(struct dns_answer_suffix);
    if (key->qtype == PICO_DNS_TYPE_A) {
      uint32_t ip_h = long_from(a_rdata);
      dns_dbg("DNS: length %u | ip %08X\n", short_be(answer_suf.rdlength), long_be(ip_h));
      answer = pico_zalloc(16);
      pico_ipv4_to_string(answer, ip_h);
      key->callback(answer, key->arg);
    } else if (key->qtype == PICO_DNS_TYPE_PTR) {
      pico_dns_client_reverse_label((char *) a_rdata);
      dns_dbg("DNS: length %u | name %s\n", short_be(answer_suf.rdlength), (char *)a_rdata + 1);
      answer = pico_zalloc(answer_suf.rdlength - 1);
      memcpy(answer, (char *)a_rdata + 1, short_be(answer_suf.rdlength) - 1);
      key->callback(answer, key->arg);
    } else {
      dns_dbg("DNS ERROR: incorrect qtype (%u)\n", key->qtype);
      return;
    }
  }
int pico_dns_client_nameserver(struct pico_ip4 *ns, uint8_t flag)
{
  struct pico_dns_ns test, *key = NULL;

  if (!ns) {
    pico_err = PICO_ERR_EINVAL;
    return -1;
  }

  switch (flag)
  {
    case PICO_DNS_NS_ADD:
      key = pico_zalloc(sizeof(struct pico_dns_ns));
      if (!key) {
        pico_err = PICO_ERR_ENOMEM;
        return -1;
      }
      key->ns = *ns;

      if(pico_tree_insert(&NSTable,key)){
        dns_dbg("DNS WARNING: nameserver %08X already added\n",ns->addr);
        pico_err = PICO_ERR_EINVAL;
        pico_free(key);
        return -1; /* Element key already exists */
      }
      dns_dbg("DNS: nameserver %08X added\n", ns->addr);
      /* If default NS found, remove it */
      pico_string_to_ipv4(PICO_DNS_NS_GOOGLE, &test.ns.addr);
      if (ns->addr != test.ns.addr) {

          key = pico_tree_findKey(&NSTable,&test);
        if (key) {
            if(pico_tree_delete(&NSTable,key)) {
            dns_dbg("DNS: default nameserver %08X removed\n", test.ns.addr);
            pico_free(key);
          } else {
            pico_err = PICO_ERR_EAGAIN;
            return -1;
          }
        }
      }
      break;

    case PICO_DNS_NS_DEL:
      test.ns = *ns;

      key = pico_tree_findKey(&NSTable,&test);
      if (!key) {
        dns_dbg("DNS WARNING: nameserver %08X not found\n", ns->addr);
        pico_err = PICO_ERR_EINVAL;
        return -1;
      }
      /* RB_REMOVE returns pointer to removed element, NULL to indicate error */

            if(pico_tree_delete(&NSTable,key)) {
        dns_dbg("DNS: nameserver %08X removed\n",key->ns.addr);
        pico_free(key);
      } else {
        pico_err = PICO_ERR_EAGAIN;
        return -1;
      }
      /* If no NS left, add default NS */
      if(pico_tree_first(&NSTable) == NULL){
        dns_dbg("DNS: add default nameserver\n");
        return pico_dns_client_init();
      }
      break;

    default:
      pico_err = PICO_ERR_EINVAL;
      return -1;
  }
  return 0;
}