Permalink
Browse files

Add full IPv6 support to RAOP and fix Bonjour discovery of RAOP service.

Do not advertise RAOP via Bonjour, if listening on the local interfaces failed
  • Loading branch information...
jyavenard committed May 1, 2012
1 parent c45628d commit 3265013b4ef634a60144392bac2d8f73f95f28f6
@@ -1,6 +1,5 @@
#include <QTimer>
#include <QTcpSocket>
#include <QUdpSocket>
#include <QtEndian>
#include <QTextStream>
@@ -153,24 +152,16 @@ bool MythRAOPConnection::Init(void)
}
// try a few ports in case the first is in use
int baseport = m_dataPort;
while (m_dataPort < baseport + RAOP_PORT_RANGE)
{
if (m_dataSocket->bind(m_dataPort))
{
LOG(VB_GENERAL, LOG_INFO, LOC +
QString("Bound to port %1 for incoming data").arg(m_dataPort));
break;
}
m_dataPort++;
}
if (m_dataPort >= baseport + RAOP_PORT_RANGE)
m_dataPort = m_dataSocket->tryBindingPort(m_dataPort, RAOP_PORT_RANGE);
if (m_dataPort < 0)
{
LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to bind to a port for data.");
return false;
}
LOG(VB_GENERAL, LOG_INFO, LOC +
QString("Bound to port %1 for incoming data").arg(m_dataPort));
// load the private key
if (!LoadKey())
return false;
@@ -193,24 +184,6 @@ bool MythRAOPConnection::Init(void)
* Socket incoming data signal handler
* use for audio, control and timing socket
*/
void MythRAOPConnection::udpDataReady(void)
{
QUdpSocket *socket = dynamic_cast<QUdpSocket*>(sender());
while (socket->state() == QAbstractSocket::BoundState &&
socket->hasPendingDatagrams())
{
QByteArray buffer;
buffer.resize(socket->pendingDatagramSize());
QHostAddress sender;
quint16 senderPort;
socket->readDatagram(buffer.data(), buffer.size(),
&sender, &senderPort);
udpDataReady(buffer, sender, senderPort);
}
}
void MythRAOPConnection::udpDataReady(QByteArray buf, QHostAddress peer,
quint16 port)
{
@@ -920,11 +893,19 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header,
else if (m_socket->localAddress().protocol() ==
QAbstractSocket::IPv6Protocol)
{
// NB IPv6 untested
Q_IPV6ADDR ip = m_socket->localAddress().toIPv6Address();
//ip = qToBigEndian(ip);
memcpy(from + i, &ip, 16);
i += 16;
if(memcmp(&ip,
"\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x00\x00\xff\xff",
12) == 0)
{
memcpy(from + i, &ip[12], 4);
i += 4;
}
else
{
memcpy(from + i, &ip, 16);
i += 16;
}
}
memcpy(from + i, m_hardwareId.constData(), RAOP_HARDWARE_ID_SIZE);
i += RAOP_HARDWARE_ID_SIZE;
@@ -1058,9 +1039,10 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header,
delete m_clientControlSocket;
}
m_clientControlSocket = new QUdpSocket(this);
int controlbind_port = findNextBindingPort(m_clientControlSocket,
control_port);
m_clientControlSocket = new ServerPool(this);
int controlbind_port =
m_clientControlSocket->tryBindingPort(control_port,
RAOP_PORT_RANGE);
if (controlbind_port < 0)
{
LOG(VB_GENERAL, LOG_ERR, LOC +
@@ -1074,8 +1056,10 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header,
.arg(control_port).arg(controlbind_port));
}
m_clientControlPort = control_port;
connect(m_clientControlSocket, SIGNAL(readyRead()),
this, SLOT(udpDataReady()));
connect(m_clientControlSocket,
SIGNAL(newDatagram(QByteArray, QHostAddress, quint16)),
this,
SLOT(udpDataReady(QByteArray, QHostAddress, quint16)));
if (m_clientTimingSocket)
{
@@ -1084,9 +1068,10 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header,
delete m_clientTimingSocket;
}
m_clientTimingSocket = new QUdpSocket(this);
int timingbind_port = findNextBindingPort(m_clientTimingSocket,
timing_port);
m_clientTimingSocket = new ServerPool(this);
int timingbind_port =
m_clientTimingSocket->tryBindingPort(timing_port,
RAOP_PORT_RANGE);
if (timingbind_port < 0)
{
LOG(VB_GENERAL, LOG_ERR, LOC +
@@ -1100,8 +1085,10 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header,
.arg(timing_port).arg(timingbind_port));
}
m_clientTimingPort = timing_port;
connect(m_clientTimingSocket, SIGNAL(readyRead()),
this, SLOT(udpDataReady()));
connect(m_clientTimingSocket,
SIGNAL(newDatagram(QByteArray, QHostAddress, quint16)),
this,
SLOT(udpDataReady(QByteArray, QHostAddress, quint16)));
if (OpenAudioDevice())
CreateDecoder();
@@ -1421,7 +1408,7 @@ bool MythRAOPConnection::CreateDecoder(void)
m_codec = avcodec_find_decoder(CODEC_ID_ALAC);
if (!m_codec)
{
LOG(VB_GENERAL, LOG_ERR, LOC
LOG(VB_GENERAL, LOG_ERR, LOC
+ "Failed to create ALAC decoder- going silent...");
return false;
}
@@ -1560,23 +1547,3 @@ int64_t MythRAOPConnection::AudioCardLatency(void)
uint64_t audiots = m_audio->GetAudiotime();
return (int64_t)timestamp - (int64_t)audiots;
}
int MythRAOPConnection::findNextBindingPort(QUdpSocket *socket, int baseport)
{
// try a few ports in case the first is in use
int port = baseport;
while (port < baseport + RAOP_PORT_RANGE)
{
if (socket->bind(port))
{
break;
}
port++;
}
if (port >= baseport + RAOP_PORT_RANGE)
{
return -1;
}
return port;
}
@@ -59,7 +59,6 @@ class MythRAOPConnection : public QObject
public slots:
void readClient(void);
void udpDataReady(QByteArray buf, QHostAddress peer, quint16 port);
void udpDataReady(void);
void timeout(void);
void audioRetry(void);
@@ -73,7 +72,6 @@ class MythRAOPConnection : public QObject
void ExpireResendRequests(uint64_t timestamp);
uint32_t decodeAudioPacket(uint8_t type, const QByteArray *buf,
QList<AudioData> *dest);
void ProcessAudio();
int ExpireAudio(uint64_t timestamp);
void ResetAudio(void);
void ProcessRequest(const QStringList &header,
@@ -101,7 +99,6 @@ class MythRAOPConnection : public QObject
// utility functions
int64_t AudioCardLatency(void);
int findNextBindingPort(QUdpSocket *socket, int port);
QStringList splitLines(const QByteArray &lines);
QString stringFromSeconds(int seconds);
uint64_t framesToMs(uint64_t frames);
@@ -118,9 +115,9 @@ class MythRAOPConnection : public QObject
QHostAddress m_peerAddress;
ServerPool *m_dataSocket;
int m_dataPort;
QUdpSocket *m_clientControlSocket;
ServerPool *m_clientControlSocket;
int m_clientControlPort;
QUdpSocket *m_clientTimingSocket;
ServerPool *m_clientTimingSocket;
int m_clientTimingPort;
// incoming audio
@@ -174,6 +171,9 @@ class MythRAOPConnection : public QObject
uint32_t m_progressEnd;
QByteArray m_artwork;
QByteArray m_dmap;
private slots:
void ProcessAudio(void);
};
#endif // MYTHRAOPCONNECTION_H
@@ -127,59 +127,56 @@ void MythRAOPDevice::Start(void)
connect(this, SIGNAL(newConnection(QTcpSocket *)),
this, SLOT(newConnection(QTcpSocket *)));
// start listening for connections (try a few ports in case the default is in use)
int baseport = m_setupPort;
while (m_setupPort < baseport + RAOP_PORT_RANGE)
m_setupPort = tryListeningPort(m_setupPort, RAOP_PORT_RANGE);
// start listening for connections (try a few ports in case the default is in use)
if (m_setupPort < 0)
{
if (listen(QNetworkInterface::allAddresses(), m_setupPort, false))
{
LOG(VB_GENERAL, LOG_INFO, LOC +
QString("Listening for connections on port %1").arg(m_setupPort));
break;
}
m_setupPort++;
LOG(VB_GENERAL, LOG_ERR, LOC +
"Failed to find a port for incoming connections.");
}
if (m_setupPort >= baseport + RAOP_PORT_RANGE)
LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to find a port for incoming connections.");
// announce service
m_bonjour = new BonjourRegister(this);
// give each frontend a unique name
int multiple = m_setupPort - baseport;
if (multiple > 0)
m_name += QString::number(multiple);
QByteArray name = m_hardwareId.toHex();
name.append("@");
name.append(m_name);
name.append(" on ");
name.append(gCoreContext->GetHostName());
QByteArray type = "_raop._tcp";
QByteArray txt;
txt.append(6); txt.append("tp=UDP");
txt.append(8); txt.append("sm=false");
txt.append(8); txt.append("sv=false");
txt.append(4); txt.append("ek=1"); //
txt.append(6); txt.append("et=0,1"); // encryption type: no, RSA
txt.append(6); txt.append("cn=0,1"); // audio codec: pcm, alac
txt.append(4); txt.append("ch=2"); // audio channels
txt.append(5); txt.append("ss=16"); // sample size
txt.append(8); txt.append("sr=44100"); // sample rate
txt.append(8); txt.append("pw=false"); // no password
txt.append(4); txt.append("vn=3");
txt.append(9); txt.append("txtvers=1"); // TXT record version 1
txt.append(8); txt.append("md=0,1,2"); // metadata-type: text, artwork, progress
txt.append(9); txt.append("vs=130.14");
txt.append(7); txt.append("da=true");
LOG(VB_GENERAL, LOG_INFO, QString("Registering service %1.%2 port %3 TXT %4")
.arg(QString(name)).arg(QString(type)).arg(m_setupPort).arg(QString(txt)));
if (!m_bonjour->Register(m_setupPort, type, name, txt))
else
{
LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to register service.");
return;
LOG(VB_GENERAL, LOG_INFO, LOC +
QString("Listening for connections on port %1").arg(m_setupPort));
// announce service
m_bonjour = new BonjourRegister(this);
// give each frontend a unique name
int multiple = m_setupPort - baseport;
if (multiple > 0)
m_name += QString::number(multiple);
QByteArray name = m_hardwareId.toHex();
name.append("@");
name.append(m_name);
name.append(" on ");
name.append(gCoreContext->GetHostName());
QByteArray type = "_raop._tcp";
QByteArray txt;
txt.append(6); txt.append("tp=UDP");
txt.append(8); txt.append("sm=false");
txt.append(8); txt.append("sv=false");
txt.append(4); txt.append("ek=1"); //
txt.append(6); txt.append("et=0,1"); // encryption type: no, RSA
txt.append(6); txt.append("cn=0,1"); // audio codec: pcm, alac
txt.append(4); txt.append("ch=2"); // audio channels
txt.append(5); txt.append("ss=16"); // sample size
txt.append(8); txt.append("sr=44100"); // sample rate
txt.append(8); txt.append("pw=false"); // no password
txt.append(4); txt.append("vn=3");
txt.append(9); txt.append("txtvers=1"); // TXT record version 1
txt.append(8); txt.append("md=0,1,2"); // metadata-type: text, artwork, progress
txt.append(9); txt.append("vs=130.14");
txt.append(7); txt.append("da=true");
LOG(VB_GENERAL, LOG_INFO, QString("Registering service %1.%2 port %3 TXT %4")
.arg(QString(name)).arg(QString(type)).arg(m_setupPort).arg(QString(txt)));
if (!m_bonjour->Register(m_setupPort, type, name, txt))
{
LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to register service.");
return;
}
}
m_valid = true;

0 comments on commit 3265013

Please sign in to comment.