blob: fafa2be2f506b749ba02937c8640d04ec475c9a2 [file] [log] [blame]
/*
* Copyright (c) 2017, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cerrno>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <fstream>
#include "server.h"
#include "cmdiface.h"
#include "debug.h"
#include "pmc_file.h"
#include "file_reader.h"
#include "udp_server.h"
const std::string UdpServer::local_host = "127.0.0.1";
const std::string UdpServer::host_details_file_name = "/etc/wigig_remoteserver_details";
const std::string UdpServer::cmd_get_host_identity = "GetHostIdentity";
const std::string UdpServer::cmd_delimiter = ";";
std::string UdpServer::host_broadcast_ip = "";
std::string UdpServer::host_ip = "";
std::string UdpServer::host_alias = "";
int UdpServer::sock_in = 0;
int UdpServer::sock_out = 0;
int UdpServer::port_in = 0;
int UdpServer::port_out = 0;
int UdpServer::SendAll(std::string message)
{
int result = -1;
struct sockaddr_in dstAddress;
dstAddress.sin_family = AF_INET;
inet_pton(AF_INET , host_broadcast_ip.c_str(), &dstAddress.sin_addr.s_addr);
dstAddress.sin_port = htons(UdpServer::port_out);
int messageSize = message.length() * sizeof(char);
result = sendto(UdpServer::sock_out, message.c_str(), messageSize, 0, (sockaddr*)&dstAddress, sizeof(dstAddress));
LOG_DEBUG << "INFO : sendto with sock_out=" << UdpServer::sock_out << ", message=" << message << " messageSize=" << messageSize << " returned with " << result << std::endl;
if (result < 0)
{
LOG_DEBUG << "ERROR : Cannot send udp broadcast message, error " << errno << ": " << strerror(errno) << std::endl;
return -4;
}
return 0;
}
int UdpServer::HandleRequest(char* buf)
{
if(NULL == buf)
{
return 0;
}
LOG_DEBUG << "INFO : Client Command: " << PlainStr(buf) << std::endl;
std::string cmd(buf);
std::string answer;
if (UdpServer::local_host == host_ip)
{
LOG_DEBUG << "WARNING : There is no support in UDP srevices when using local host" << std::endl;
return 0;
}
if (UdpServer::cmd_get_host_identity == cmd)
{
answer = GetHostDetails();
}
else
{
LOG_DEBUG << "ERROR : Unkonwn UDP command" << std::endl;
}
// send back response in broadcast
// For the time being, all requests will have broadcast response
// All responses will use the same outgoing communication port
LOG_DEBUG << "INFO : Answer is: " << PlainStr(answer.c_str()) << std::endl;
SendAll(answer);
return 0;
}
/*
Close the server to allow un-bind te socket - allowing future connections without delay
*/
int UdpServer::stop()
{
LOG_DEBUG << "INFO : Stopping the UDP server" << std::endl;
shutdown(UdpServer::sock_in, SHUT_RDWR);
return 0;
}
/*
Initialize UPD server on the given port. The function returns in case of error,
otherwise it doesn't return
*/
int UdpServer::start(int localUdpPortIn, int remoteUdpPortIn)
{
LOG_DEBUG << "INFO : Starting UDP server on port " << localUdpPortIn << std::endl;
LOG_DEBUG << "INFO : Sending broadcast answers to port " << remoteUdpPortIn << std::endl;
// open the UDP server on a new thread
UdpServer::port_in = localUdpPortIn;
UdpServer::port_out = remoteUdpPortIn;
pthread_t thread;
pthread_create(&thread, 0, start_udp_server, NULL);
pthread_detach(thread);
return 0;
}
void* UdpServer::start_udp_server(void* unused)
{
(void)unused;
if (LoadHostDetails() < 0)
{
return NULL;
}
// Create outgoing response socket
UdpServer::sock_out = socket(AF_INET, SOCK_DGRAM, 0);
if (UdpServer::sock_out < 0)
{
LOG_DEBUG << "ERROR : Cannot open udp broadcast outgoing socket" << std::endl;
return NULL;
}
int enabled = 1;
int result;
result = setsockopt(UdpServer::sock_out, SOL_SOCKET, SO_BROADCAST, (char*)&enabled, sizeof(enabled));
if (result < 0)
{
LOG_DEBUG << "ERROR : Cannot set broadcast option for udp socket, error " << errno << ": " << strerror(errno) << std::endl;
shutdown(UdpServer::sock_out, SHUT_RDWR);
return NULL;
}
// Create incoming request socket
struct sockaddr_in address;
UdpServer::sock_in = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(UdpServer::sock_in < 0)
{
LOG_DEBUG << "ERROR : Cannot create a socket on port " << UdpServer::port_in << std::endl;
return NULL;
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(UdpServer::port_in);
// Set the "Re-Use" socket option - allows reconnections after wilserver exit
int reuse = 1;
setsockopt(UdpServer::sock_in, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int));
if(bind(UdpServer::sock_in, (struct sockaddr *)&address, sizeof(struct sockaddr_in)) < 0)
{
LOG_DEBUG << "ERROR : Cannot bind socket to port " << UdpServer::port_in << ", error " << errno << ": " << strerror(errno) << std::endl;
return NULL;
}
char* buf = new char[MAX_INPUT_BUF];
if (!buf)
{
LOG_ERROR << "Cannot allocate receive buffer for UDP messages" << std::endl;
return NULL;
}
do
{
if (recvfrom(UdpServer::sock_in, buf, MAX_INPUT_BUF, 0, NULL, 0) < 0)
{
LOG_DEBUG << "ERROR : Cannot listen on port " << UdpServer::port_in << std::endl;
return NULL;
}
HandleRequest(buf);
} while (true);
LOG_DEBUG << "INFO : UDP server shutdown" << std::endl;
return NULL; // Wont get here, just to avoid the warning
}
bool FindEthernetInterface(struct ifreq& ifr, int& fd)
{
fd = socket(AF_INET, SOCK_DGRAM, 0);
if(fd < 0)
{
LOG_DEBUG << "ERROR : Cannot get host and broadcast IPs" << std::endl;
return -1;
}
for (int i = 0; i < 100; i++)
{
snprintf(ifr.ifr_name, IFNAMSIZ-1, "eth%d", i);
if (ioctl(fd, SIOCGIFADDR, &ifr) >= 0)
{
return true;
}
}
return false;
}
int UdpServer::LoadHostAndBroadcastIps()
{
int fd;
struct ifreq ifr;
ifr.ifr_addr.sa_family = AF_INET; // IP4V
// Get IP address according to OS
if (FindEthernetInterface(ifr, fd))
{
LOG_INFO << "Linux OS" << std::endl;
}
else
{
snprintf(ifr.ifr_name, IFNAMSIZ, "br-lan");
if (ioctl(fd, SIOCGIFADDR, &ifr) >= 0)
{
LOG_INFO << "OpenWRT OS" << std::endl;
}
else
{
// Probably Android OS
LOG_INFO << "Android OS (no external IP Adress)" << std::endl;
host_ip = UdpServer::local_host;
host_broadcast_ip = UdpServer::local_host;
return 0;
}
}
host_ip = inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr);
if (ioctl(fd, SIOCGIFBRDADDR, &ifr) < 0)
{
LOG_DEBUG << "ERROR : Cannot get broadcast IP" << std::endl;
return -3;
}
host_broadcast_ip = inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr);
LOG_DEBUG << "INFO : " << "host_ip=" << host_ip << std::endl;
LOG_DEBUG << "INFO : " << "host_broadcast_ip=" << host_broadcast_ip << std::endl;
close(fd);
return 0;
}
void UdpServer::LoadHostAlias()
{
std::ifstream infile;
infile.open(host_details_file_name.c_str());
std::getline(infile, host_alias);
infile.close();
}
int UdpServer::LoadHostDetails()
{
if (LoadHostAndBroadcastIps() < 0)
{
return -1;
}
LoadHostAlias();
return 0;
}