203 lines
5.8 KiB
C++
203 lines
5.8 KiB
C++
#include "GwUdpWriter.h"
|
|
#include <ESPmDNS.h>
|
|
#include <errno.h>
|
|
#include "GwBuffer.h"
|
|
#include "GwSocketConnection.h"
|
|
#include "GwSocketHelper.h"
|
|
#include "GWWifi.h"
|
|
|
|
GwUdpWriter::WriterSocket::WriterSocket(GwLog *l,int p,const String &src,const String &dst, SourceMode sm) :
|
|
sourceMode(sm), source(src), destination(dst), port(p),logger(l)
|
|
{
|
|
if (inet_pton(AF_INET, dst.c_str(), &dstA.sin_addr) != 1)
|
|
{
|
|
LOG_ERROR("UDPW: invalid destination ip address %s", dst.c_str());
|
|
return;
|
|
}
|
|
if (sourceMode != SourceMode::S_UNBOUND)
|
|
{
|
|
if (inet_pton(AF_INET, src.c_str(), &srcA) != 1)
|
|
{
|
|
LOG_ERROR("UDPW: invalid source ip address %s", src.c_str());
|
|
return;
|
|
}
|
|
}
|
|
dstA.sin_family=AF_INET;
|
|
dstA.sin_port=htons(port);
|
|
fd=socket(AF_INET,SOCK_DGRAM,IPPROTO_IP);
|
|
if (fd < 0){
|
|
LOG_ERROR("UDPW: unable to create udp socket: %d",errno);
|
|
return;
|
|
}
|
|
int enable = 1;
|
|
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
|
|
setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &enable, sizeof(int));
|
|
switch (sourceMode)
|
|
{
|
|
case SourceMode::S_SRC:
|
|
{
|
|
sockaddr_in bindA;
|
|
bindA.sin_family = AF_INET;
|
|
bindA.sin_port = htons(0); // let system select
|
|
bindA.sin_addr = srcA;
|
|
if (bind(fd, (struct sockaddr *)&bindA, sizeof(bindA)) != 0)
|
|
{
|
|
LOG_ERROR("UDPW: bind failed for address %s: %d", source.c_str(), errno);
|
|
::close(fd);
|
|
fd = -1;
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
case SourceMode::S_MC:
|
|
{
|
|
if (setsockopt(fd,IPPROTO_IP,IP_MULTICAST_IF,&srcA,sizeof(srcA)) != 0){
|
|
LOG_ERROR("UDPW: unable to set MC source %s: %d",source.c_str(),errno);
|
|
::close(fd);
|
|
fd=-1;
|
|
return;
|
|
}
|
|
int loop=0;
|
|
setsockopt(fd,IPPROTO_IP,IP_MULTICAST_LOOP,&loop,sizeof(loop));
|
|
}
|
|
break;
|
|
default:
|
|
//not bound
|
|
break;
|
|
}
|
|
}
|
|
bool GwUdpWriter::WriterSocket::changed(const String &newSrc, const String &newDst){
|
|
if (newDst != destination) return true;
|
|
if (sourceMode == SourceMode::S_UNBOUND) return false;
|
|
return newSrc != source;
|
|
}
|
|
size_t GwUdpWriter::WriterSocket::send(const char *buf,size_t len){
|
|
if (fd < 0) return 0;
|
|
ssize_t err = sendto(fd,buf,len,0,(struct sockaddr *)&dstA, sizeof(dstA));
|
|
if (err < 0){
|
|
LOG_DEBUG(GwLog::DEBUG,"UDPW %s error sending: %d",destination.c_str(), errno);
|
|
return 0;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
GwUdpWriter::GwUdpWriter(const GwConfigHandler *config, GwLog *logger, int minId)
|
|
{
|
|
this->config = config;
|
|
this->logger = logger;
|
|
this->minId = minId;
|
|
port=config->getInt(GwConfigDefinitions::udpwPort);
|
|
|
|
}
|
|
void GwUdpWriter::checkStaSocket(){
|
|
String src;
|
|
String bc;
|
|
if (type == T_BCAP || type == T_MCAP || type == T_NORM || type == T_UNKNOWN ) return;
|
|
bool connected=false;
|
|
if (WiFi.isConnected()){
|
|
src=WiFi.localIP().toString();
|
|
bc=WiFi.broadcastIP().toString();
|
|
connected=true;
|
|
}
|
|
else{
|
|
if (staSocket == nullptr) return;
|
|
}
|
|
String dst;
|
|
WriterSocket::SourceMode sm=WriterSocket::SourceMode::S_SRC;
|
|
switch (type){
|
|
case T_BCALL:
|
|
case T_BCSTA:
|
|
sm=WriterSocket::SourceMode::S_SRC;
|
|
dst=bc;
|
|
break;
|
|
case T_MCALL:
|
|
case T_MCSTA:
|
|
dst=config->getString(GwConfigDefinitions::udpwMC);
|
|
sm=WriterSocket::SourceMode::S_MC;
|
|
break;
|
|
|
|
}
|
|
if (staSocket != nullptr)
|
|
{
|
|
if (!connected || staSocket->changed(src, dst))
|
|
{
|
|
staSocket->close();
|
|
delete staSocket;
|
|
staSocket = nullptr;
|
|
LOG_INFO("changing/stopping UDPW(sta) socket");
|
|
}
|
|
}
|
|
if (staSocket == nullptr && connected)
|
|
{
|
|
LOG_INFO("creating new UDP(sta) socket src=%s, dst=%s", src.c_str(), dst.c_str());
|
|
staSocket = new WriterSocket(logger, port, src, dst, WriterSocket::SourceMode::S_SRC);
|
|
}
|
|
}
|
|
|
|
void GwUdpWriter::begin()
|
|
{
|
|
if (type != T_UNKNOWN) return; //already started
|
|
type=(UType)(config->getInt(GwConfigDefinitions::udpwType));
|
|
LOG_INFO("UDPW begin, mode=%d",(int)type);
|
|
String src=WiFi.softAPIP().toString();
|
|
String dst;
|
|
WriterSocket::SourceMode sm=WriterSocket::SourceMode::S_UNBOUND;
|
|
bool createApSocket=false;
|
|
switch(type){
|
|
case T_BCALL:
|
|
case T_BCAP:
|
|
createApSocket=true;
|
|
dst=WiFi.softAPBroadcastIP().toString();
|
|
sm=WriterSocket::SourceMode::S_SRC;
|
|
break;
|
|
case T_MCALL:
|
|
case T_MCAP:
|
|
createApSocket=true;
|
|
dst=config->getString(GwConfigDefinitions::udpwMC);
|
|
sm=WriterSocket::SourceMode::S_SRC;
|
|
break;
|
|
case T_NORM:
|
|
createApSocket=true;
|
|
dst=config->getString(GwConfigDefinitions::udpwAddress);
|
|
sm=WriterSocket::SourceMode::S_UNBOUND;
|
|
}
|
|
if (createApSocket){
|
|
LOG_INFO("creating new UDPW(ap) socket src=%s, dst=%s", src.c_str(), dst.c_str());
|
|
apSocket=new WriterSocket(logger,port,src,dst,sm);
|
|
}
|
|
checkStaSocket();
|
|
}
|
|
|
|
void GwUdpWriter::loop(bool handleRead, bool handleWrite)
|
|
{
|
|
if (handleWrite){
|
|
checkStaSocket();
|
|
}
|
|
|
|
}
|
|
|
|
void GwUdpWriter::readMessages(GwMessageFetcher *writer)
|
|
{
|
|
|
|
}
|
|
size_t GwUdpWriter::sendToClients(const char *buf, int source,bool partial)
|
|
{
|
|
if (source == minId) return 0;
|
|
size_t len=strlen(buf);
|
|
bool hasSent=false;
|
|
size_t res=0;
|
|
if (apSocket != nullptr){
|
|
res=apSocket->send(buf,len);
|
|
if (res > 0) hasSent=true;
|
|
}
|
|
if (staSocket != nullptr){
|
|
res=staSocket->send(buf,len);
|
|
if (res > 0) hasSent=true;
|
|
}
|
|
return hasSent?len:0;
|
|
}
|
|
|
|
|
|
GwUdpWriter::~GwUdpWriter()
|
|
{
|
|
} |