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...
1 parent c45628d commit 3265013b4ef634a60144392bac2d8f73f95f28f6 @jyavenard jyavenard committed May 1, 2012
@@ -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.