Wrapper of NetworkSocketAPI for BSD sockets on POSIX systems

Dependents:   BSDInterfaceTests HelloBSDInterface

BSDInterface.cpp

Committer:
geky
Date:
2016-04-01
Revision:
6:363dd62309cf
Parent:
5:1af10f9d9404

File content as of revision 6:363dd62309cf:

/* BSD implementation of NetworkInterfaceAPI
 * Copyright (c) 2015 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
#include "BSDInterface.h"

#include <sys/ioctl.h>
#include <netdb.h>
#include <net/if.h> 
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>


// BSDInterface implementation
static bool ifctl(struct ifreq *ifr, unsigned req) {
    struct ifconf ifc;
    char buffer[1024];

    int sock = ::socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
        return 0;
    }

    ifc.ifc_buf = buffer;
    ifc.ifc_len = sizeof buffer;
    if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) {
        ::close(sock);
        return false;
    }

    for (int i = 0; i < ifc.ifc_len; i++) {
        strcpy(ifr->ifr_name, ifc.ifc_req[i].ifr_name);

        if (ioctl(sock, SIOCGIFFLAGS, ifr) == 0
            && !(ifr->ifr_flags & IFF_LOOPBACK)
            && ioctl(sock, req, ifr) == 0) {
            ::close(sock);
            return true;
        }
    }

    ::close(sock);
    return false;
}

const char *BSDInterface::getIPAddress()
{
    static char ip_address[NS_IP_SIZE] = "\0";
    if (ip_address[0]) {
        return ip_address;
    }

    struct ifreq ifr;
    if (!ifctl(&ifr, SIOCGIFADDR)) {
        return 0;
    }

    struct in_addr addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr;
    strcpy(ip_address, inet_ntoa(addr));
    return ip_address;
}

const char *BSDInterface::getMACAddress() 
{
    static char mac_address[NS_MAC_SIZE] = "\0";
    if (mac_address[0]) {
        return mac_address;
    }

    struct ifreq ifr;
    if (!ifctl(&ifr, SIOCGIFHWADDR)) {
        return 0;
    }

    sprintf(mac_address, "%02x:%02x:%02x:%02x:%02x:%02x",
            (unsigned char)ifr.ifr_hwaddr.sa_data[0],
            (unsigned char)ifr.ifr_hwaddr.sa_data[1],
            (unsigned char)ifr.ifr_hwaddr.sa_data[2],
            (unsigned char)ifr.ifr_hwaddr.sa_data[3],
            (unsigned char)ifr.ifr_hwaddr.sa_data[4],
            (unsigned char)ifr.ifr_hwaddr.sa_data[5]);
    return mac_address;
}

int32_t BSDInterface::getHostByName(const char *name, char *ip)
{
    struct hostent *host = gethostbyname(name);
    if (!host || host->h_addrtype != AF_INET) {
        return NS_ERROR_DNS_FAILURE;
    }

    strcpy(ip, inet_ntoa(*(struct in_addr *)host->h_addr));
    return 0;
}

SocketInterface *BSDInterface::createSocket(ns_protocol_t proto)
{
    int type = (proto == NS_UDP) ? SOCK_DGRAM : SOCK_STREAM;
    int fd = ::socket(AF_INET, type, 0);
    if (fd < 0) {
        return 0;
    }

    return new BSDSocket(fd);
}

void BSDInterface::destroySocket(SocketInterface *socket)
{
    ::close(((BSDSocket*)socket)->fd);
    delete socket;
}


// BSDSocket implementation
int32_t BSDInterface::BSDSocket::open(const char *ip, uint16_t port)
{
    struct sockaddr_in host;    
    memset(&host, 0, sizeof host);
    host.sin_family = AF_INET;
    host.sin_port = htons(port);
    inet_pton(AF_INET, ip, &host.sin_addr);

    if (::connect(fd, (struct sockaddr *)&host, sizeof host) < 0) {
        return NS_ERROR_NO_CONNECTION;
    }

    return 0;
}

int32_t BSDInterface::BSDSocket::close()
{
    return 0;
}

int32_t BSDInterface::BSDSocket::send(const void *data, uint32_t size)
{
    if (::send(fd, data, size, 0) < 0) {
        return NS_ERROR_DEVICE_ERROR;
    }

    return size;
}

int32_t BSDInterface::BSDSocket::recv(void *data, uint32_t size)
{
    int ret = ::recv(fd, data, size, MSG_DONTWAIT);

    if (ret > 0) {
        return ret;
    } else if (errno == EAGAIN) {
        return NS_ERROR_WOULD_BLOCK;
    } else {
        return NS_ERROR_DEVICE_ERROR;
    }
}