// cae_server.cpp // // Network server for caed(8). // // (C) Copyright 2019-2023 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include #include #include "cae_server.h" #include "playsession.h" // // Uncomment this to send all protocol messages to syslog (DEBUG priority) // // #define __CAE_SERVER_LOG_PROTOCOL_MESSAGES CaeServerConnection::CaeServerConnection(QTcpSocket *sock) { socket=sock; authenticated=false; accum=""; meter_port=0; for(int i=0;ideleteLater(); } CaeServer::CaeServer(RDConfig *config,QObject *parent) : QObject(parent) { cae_config=config; cae_server=new QTcpServer(this); connect(cae_server,SIGNAL(newConnection()),this,SLOT(newConnectionData())); cae_ready_read_mapper=new QSignalMapper(this); connect(cae_ready_read_mapper,SIGNAL(mapped(int)), this,SLOT(readyReadData(int))); cae_connection_closed_mapper=new QSignalMapper(this); connect(cae_connection_closed_mapper,SIGNAL(mapped(int)), this,SLOT(connectionClosedData(int))); } QList CaeServer::connectionIds() const { QList ret; for(QMap::const_iterator it= cae_connections.begin();it!=cae_connections.end();it++) { ret.push_back(it.key()); } return ret; } QHostAddress CaeServer::peerAddress(int id) const { return cae_connections[id]->socket->peerAddress(); } uint16_t CaeServer::peerPort(int id) const { return cae_connections[id]->socket->peerPort(); } uint16_t CaeServer::meterPort(int id) const { return cae_connections[id]->meter_port; } void CaeServer::setMeterPort(int id,uint16_t port) { cae_connections[id]->meter_port=port; } bool CaeServer::metersEnabled(int id,unsigned card) const { return cae_connections[id]->meters_enabled[card]; } void CaeServer::setMetersEnabled(int id,unsigned card,bool state) { cae_connections[id]->meters_enabled[card]=state; } bool CaeServer::listen(const QHostAddress &addr,uint16_t port) { return cae_server->listen(addr,port); } void CaeServer::sendCommand(const QString &cmd) { for(QMap::const_iterator it= cae_connections.begin();it!=cae_connections.end();it++) { if(it.value()->authenticated) { sendCommand(it.key(),cmd); } } } void CaeServer::sendCommand(uint64_t phandle,const QString &cmd) { sendCommand(PlaySession::socketDescriptor(phandle),cmd); } void CaeServer::sendCommand(int id,const QString &cmd) { #ifdef __CAE_SERVER_LOG_PROTOCOL_MESSAGES RDApplication::syslog(cae_config,LOG_DEBUG, "send[%d]: %s",id,(const char *)cmd.toUtf8()); #endif // __CAE_SERVER_LOG_PROTOCOL_MESSAGES cae_connections.value(id)->socket->write(cmd.toUtf8()); } void CaeServer::newConnectionData() { QTcpSocket *sock=cae_server->nextPendingConnection(); cae_connection_closed_mapper->setMapping(sock,sock->socketDescriptor()); connect(sock,SIGNAL(disconnected()),cae_connection_closed_mapper,SLOT(map())); cae_ready_read_mapper->setMapping(sock,sock->socketDescriptor()); connect(sock,SIGNAL(readyRead()),cae_ready_read_mapper,SLOT(map())); cae_connections[sock->socketDescriptor()]=new CaeServerConnection(sock); RDApplication::syslog(cae_config,LOG_DEBUG, "added connection %d [%s:%u]",sock->socketDescriptor(), sock->peerAddress().toString().toUtf8().constData(), 0xFFFF&sock->peerPort()); } void CaeServer::readyReadData(int id) { QByteArray data=cae_connections.value(id)->socket->readAll(); for(int i=0;iaccum)) { return; } break; case 10: case 13: break; default: cae_connections.value(id)->accum+=c; break; } } } void CaeServer::connectionClosedData(int id) { QString logmsg= QString::asprintf("removed connection %d [%s:%u]", id, peerAddress(id).toString().toUtf8().constData(), 0xFFFF&peerPort(id)); int priority=LOG_DEBUG; if(!cae_connections.value(id)->authenticated) { logmsg= QString::asprintf("removed never authenticated connection %d [%s:%u]", id, peerAddress(id).toString().toUtf8().constData(), 0xFFFF&peerPort(id)); priority=LOG_WARNING; } emit connectionDropped(id); cae_connections.value(id)->socket->disconnect(); delete cae_connections.value(id); cae_connections.remove(id); RDApplication::syslog(cae_config,priority,"%s",logmsg.toUtf8().constData()); } bool CaeServer::ProcessCommand(int id,const QString &cmd) { // rda->syslog(LOG_NOTICE,"processing command: \"%s\"",cmd.toUtf8().constData()); CaeServerConnection *conn=cae_connections.value(id); bool ok=false; QString cmdstr=cmd; QStringList f0=cmd.split(" ",QString::SkipEmptyParts); if(f0.size()==0) { return false; } #ifdef __CAE_SERVER_LOG_PROTOCOL_MESSAGES RDApplication::syslog(cae_config,LOG_DEBUG, "recv[%d]: %s",id,(const char *)cmd.toUtf8()); #endif // __CAE_SERVER_LOG_PROTOCOL_MESSAGES cae_connections.value(id)->accum=""; // // Unpriviledged Commands // if(f0.at(0)=="DC") { cae_connections.value(id)->socket->close(); return true; } if(f0.at(0)=="PW") { if((f0.size()==2)&&(f0.at(1)==cae_config->password())) { conn->authenticated=true; sendCommand(id,"PW +!"); RDApplication::syslog(cae_config,LOG_DEBUG, "PASSED authentication: connection %d [%s:%u]", id, peerAddress(id).toString().toUtf8().constData(), 0xFFFF&peerPort(id)); } else { conn->authenticated=false; sendCommand(id,"PW -!"); RDApplication::syslog(cae_config,LOG_WARNING, "FAILED authentication: connection %d [%s:%u]", id, peerAddress(id).toString().toUtf8().constData(), 0xFFFF&peerPort(id)); connectionClosedData(id); return true; } return false; } // // Priviledged Commands // Authentication required to execute these! // if(!conn->authenticated) { RDApplication::syslog(cae_config,LOG_WARNING, "unauthenticated connection %d [%s:%u] sent command \"%s\"", id, peerAddress(id).toString().toUtf8().constData(), 0xFFFF&peerPort(id), cmdstr.toUtf8().constData()); connectionClosedData(id); return true; } bool was_processed=false; if((f0.at(0)=="LP")&&(f0.size()==5)) { // Load Playback unsigned serial=f0.at(1).toUInt(&ok); if(ok) { unsigned cardnum=f0.at(2).toUInt(&ok); if(ok&&(cardnum2) { // So we don't warn if no cards are specified uint16_t udp_port=0xFFFF&f0.at(1).toUInt(&ok); if(ok) { QList cards; for(int i=2;i