Implementation of the WifiPlusClick hardware module.

Dependents:   WifiPlusKlickExample

WifiPlusClick Libary

Overview

http://www.mikroe.com/img/development-tools/accessory-boards/click/wifi-plus/wifi_plus_click_main.png

This library implements the functionality exposed by a WifiPlusClick module from MikroElektronika (http://www.mikroe.com/click/wifi-plus/).

The WifiPlusClick module is an easy to handle module which provides access to up to 8 simultaneous socket objects - which is an an important aspect when you want to implement your own web server.

When I first started with the more commonly used Wifly module, I found out that the Wifly module does not reliably serve webpages which include other resources like images, JavaScript files or CSS files. The root cause seems to be the limitation that Wifly is only able to handle a single socket at this time. So I searched for an alternative and found this (actually cheaper) alternative :

WifiPlusClick HW Module

This module comes with its own limitations. The WifiPlusClick Module interface does not allow to use broadcasting or multicasting on UDP sockets. There are some additional limitations, but I think these are not so important. The following functionality is provided by the module and my library implementation :

  1. Wifi functionality
    1. Connections using AD-HOC or INFRASTRUCTURE mode
    2. List all available Wifi beacons
    3. WEP and WPA/WPA2 security modes including binary and ASCII keys
    4. reading binary WPA key after successfull Connection to speed up connection time
  2. Socket functionality
    1. UDP sockets
    2. TCP sockets

Limitations

I found the following limitations:

  1. UDP sockets cannot use multicasting or broadcasting
  2. set_option functionality is not provided by the HW
  3. 8 sockets can be configured with 1024 bytes of buffer each or 1 socket with 8192 bytes of buffer.

Sample application

Here is my sample application which you can use as a starting point.

Import programWifiPlusKlickExample

Example application of the WifiPlusClick library for use of WifiPlusClick HW Module from Mikroe.com

NOTE

The implementation of the Sockets in this library is still not completely tested. I only tested the TCP part of the sockets. Please let me know what your experiences are when using the library. I will be working on a multithreaded version of this library...

Helper/DnsQuery.cpp

Committer:
leihen
Date:
2013-07-29
Revision:
0:2a179bd4cc02

File content as of revision 0:2a179bd4cc02:

/* Copyright (c) 2013 Henry Leinen (henry[dot]leinen [at] online [dot] de)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
 * and associated documentation files (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge, publish, distribute,
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include "mbed.h"
#include "DnsQuery.h"

#define DEBUG
#include "debug.h"



DnsQuery::DnsQuery(Wifi* wifi, IPADDRESS_t *dnsip)
    : _wifi(wifi)
{
    _dnsip.sin_addr.o1 = dnsip->sin_addr.o1;
    _dnsip.sin_addr.o2 = dnsip->sin_addr.o2;
    _dnsip.sin_addr.o3 = dnsip->sin_addr.o3;
    _dnsip.sin_addr.o4 = dnsip->sin_addr.o4;
    INFO("Setting   DNS = %d.%d.%d.%d !", dnsip->sin_addr.o1, dnsip->sin_addr.o2, dnsip->sin_addr.o3, dnsip->sin_addr.o4);
    INFO("Accepting DNS = %d.%d.%d.%d !", _dnsip.sin_addr.o1, _dnsip.sin_addr.o2, _dnsip.sin_addr.o3, _dnsip.sin_addr.o4);
}

bool DnsQuery::gethostbyname(const char* hostname, IPADDRESS_t &ipaddress)
{
    int len = 0;
    if (hostname == NULL)
        return false;
    len = strlen(hostname);
    if ((len >128) || (len == 0))
        return false;
    memset (&ipaddress, 0, sizeof(ipaddress));
    
    int packetlen = /* size of HEADER structure */ 12 + /* size of QUESTION Structure */5 + len + 1;
    char *packet = new char[packetlen]; /* this is the UDP packet to send to the DNS */
    if (packet == NULL)
        return false;
    
    //  Fill the header
    memset(packet, 0, packetlen);
    packet[1] = 1;      //  ID = 1
    packet[5] = 1;      // QDCOUNT = 1 (contains one question)

    int c = 13;         //  point to NAME element in question section or request
    int cnt = 12;       //  points to the counter of
    packet[cnt] = 0;
    for (int i = 0 ; i < len ; i++) {
        if (hostname[i] != '.') {
            //  Copy the character and increment the character counter
            packet[cnt]++;
            packet[c++] = hostname[i];
        } else {
            //  Finished with this part, so go to the next
            cnt = c++;
            packet[cnt] = 0;
        }
    }
    
    //  Terminate this domain name with a zero entry
    packet[c++] = 0;
    
    //  Set QTYPE
    packet[c++] = 0;
    packet[c++] = 1;
    //  Set QCLASS
    packet[c++] = 0;
    packet[c++] = 1;
    
    //  REMOVE ME !!!!
    std::printf("--> DUMPING UDP PACKET : ");
    int port;
    for( int i = 0 ; i < c ; i++) {
        std::printf("%02x ", packet[i]);
    }
    std::printf("\n\n\n");
    //  Ready to send to DNS
    SOCKET_HANDLE_t sock = _wifi->SocketCreate(UDP);
    if (sock == InvalidSocketHandle) {
        delete packet;
        return false;
    }
 
    INFO("Using %d.%d.%d.%d as DNS !", _dnsip.sin_addr.o1, _dnsip.sin_addr.o2, _dnsip.sin_addr.o3, _dnsip.sin_addr.o4);
    if (!_wifi->SocketSendTo(sock, &_dnsip, 53, packet, packetlen)) {
        delete packet;
        _wifi->SocketClose(sock);
        return false;
    }
    
    delete packet;
    
    packet = new char [1024];
    if (packet == NULL) {
        _wifi->SocketClose(sock);
        return false;
    }
    
    //  Receive the answer from DNS
    Timer t;
    int recvd = 0;
    t.start();
    
    while (t.read_ms() < 10000) {
        if ((recvd = _wifi->SocketRecvFrom (sock, NULL, &port, packet, 1024)) > 0 ) {
            //  process input
            std::printf("Received %d bytes from DNS !\n", recvd);
            if (!resolve(packet, ipaddress)) {
                break;
            }
                        
            //  cleanup and return
            delete packet;
            _wifi->SocketClose(sock);
            return true;
        } else {
            ERR("SocketRecvFrom returned %d !", recvd);
        }
    }    

    delete packet;
    
    _wifi->SocketClose(sock);
        
    return false;
}


bool DnsQuery::resolve(char* resp, IPADDRESS_t &ipaddress)
{
    int ID = (((int)resp[0]) <<8) + resp[1];
    int QR = resp[2] >>7;
    int Opcode = (resp[2]>>3) & 0x0F;
    int RCODE = (resp[3] & 0x0F);
    int ANCOUNT = (((int)resp[6])<<8)+ resp[7];
    
    INFO("Resolving response : ID = %d, QR = %d, Opcode = %d, RCODE = %d", ID, QR, Opcode, RCODE);
    if ((ID != 1) || (QR != 1) || (Opcode != 0) || (RCODE != 0)) {
        ERR("Received non matching response from DNS !");
        return false;
    }
    
    int c = 12;
    int d;
    //  Skip domain question
    while(  (d=resp[c++]) != 0) {
        c+=d;
    }
    c+= 4; //   skip QTYPE and QCLASS
    
    //  Here comes the resource record
    for (int ans = 0 ; ans < ANCOUNT; ans++) {
        if (parseRR(resp, c, ipaddress)) {
            return true;
        }
    }
    
    return false;
}

bool DnsQuery::parseRR(char *resp, int& c, IPADDRESS_t& adr )
{
    int n = 0;
    
    while( (n=resp[c++]) != 0) {
        if ((n & 0xc0) != 0) {
            //  This is a link
            c++;
            break;
        } else {
            c+= n;  //  skip this segment, not interested in string domain names
        }
    }
    
    int TYPE = (((int)resp[c])<<8) + resp[c+1];
    int CLASS = (((int)resp[c+2])<<8) + resp[c+3];
    int RDLENGTH = (((int)resp[c+8])<<8) + resp[c+9];

    INFO("Record of TYPE=%d and CLASS=%d detected !", TYPE, CLASS);
    c+= 10;
    if ((CLASS == 1) && (TYPE == 1)) {
        adr.sin_addr.o1 = resp[c];
        adr.sin_addr.o2 = resp[c+1];
        adr.sin_addr.o3 = resp[c+2];
        adr.sin_addr.o4 = resp[c+3];
        c+= RDLENGTH;
        return true;
    } else {
    }
    c+= RDLENGTH;
    
    return false;
}