PageRenderTime 53ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/src/protocol/IncomingSocket.cpp

https://gitlab.com/gaurav1981/ricochet
C++ | 294 lines | 195 code | 50 blank | 49 comment | 39 complexity | 94f26d3ddc366bcc4fdda91af86789a5 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. /* Ricochet - https://ricochet.im/
  2. * Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions are
  6. * met:
  7. *
  8. * * Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. *
  11. * * Redistributions in binary form must reproduce the above
  12. * copyright notice, this list of conditions and the following disclaimer
  13. * in the documentation and/or other materials provided with the
  14. * distribution.
  15. *
  16. * * Neither the names of the copyright owners nor the names of its
  17. * contributors may be used to endorse or promote products derived from
  18. * this software without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. */
  32. #include "IncomingSocket.h"
  33. #include "core/UserIdentity.h"
  34. #include "core/ContactsManager.h"
  35. #include "ContactRequestServer.h"
  36. #include <QTcpServer>
  37. #include <QTcpSocket>
  38. #include <QElapsedTimer>
  39. #include <QtDebug>
  40. IncomingSocket::IncomingSocket(UserIdentity *id, QObject *parent)
  41. : QObject(parent), identity(id), server(new QTcpServer(this))
  42. {
  43. connect(server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
  44. }
  45. bool IncomingSocket::listen(const QHostAddress &address, quint16 port)
  46. {
  47. if (server->isListening())
  48. server->close();
  49. return server->listen(address, port);
  50. }
  51. QString IncomingSocket::errorString() const
  52. {
  53. return server->errorString();
  54. }
  55. QHostAddress IncomingSocket::serverAddress() const
  56. {
  57. return server->serverAddress();
  58. }
  59. quint16 IncomingSocket::serverPort() const
  60. {
  61. return server->serverPort();
  62. }
  63. void IncomingSocket::incomingConnection()
  64. {
  65. while (server->hasPendingConnections())
  66. {
  67. QTcpSocket *conn = server->nextPendingConnection();
  68. conn->setParent(this);
  69. connect(conn, SIGNAL(readyRead()), this, SLOT(readSocket()));
  70. connect(conn, SIGNAL(disconnected()), this, SLOT(removeSocket()));
  71. QElapsedTimer time;
  72. time.start();
  73. conn->setProperty("startTime", time.msecsSinceReference());
  74. pendingSockets.append(conn);
  75. if (!expireTimer.isActive())
  76. expireTimer.start(11000, this);
  77. }
  78. }
  79. void IncomingSocket::removeSocket(QTcpSocket *socket)
  80. {
  81. if (!socket)
  82. {
  83. socket = qobject_cast<QTcpSocket*>(sender());
  84. if (!socket)
  85. return;
  86. }
  87. qDebug() << "Disconnecting pending socket";
  88. pendingSockets.removeOne(socket);
  89. socket->disconnect(this);
  90. socket->close();
  91. socket->deleteLater();
  92. }
  93. void IncomingSocket::timerEvent(QTimerEvent *)
  94. {
  95. QElapsedTimer now;
  96. now.start();
  97. for (int i = 0; i < pendingSockets.size(); ++i)
  98. {
  99. qint64 started = pendingSockets[i]->property("startTime").toLongLong();
  100. if (now.msecsSinceReference() - started >= 10000)
  101. {
  102. /* time is up. */
  103. removeSocket(pendingSockets[i]);
  104. --i;
  105. }
  106. }
  107. if (pendingSockets.isEmpty())
  108. expireTimer.stop();
  109. }
  110. void IncomingSocket::readSocket()
  111. {
  112. QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender());
  113. if (!socket)
  114. return;
  115. QVariant versionProp = socket->property("protocolVersion");
  116. if (versionProp.isNull())
  117. {
  118. if (!handleVersion(socket))
  119. return;
  120. versionProp = socket->property("protocolVersion");
  121. if (versionProp.isNull())
  122. {
  123. Q_ASSERT_X(false, "IncomingConnection::handleVersion", "version property not properly set");
  124. return;
  125. }
  126. }
  127. handleIntro(socket, (uchar)versionProp.toUInt());
  128. }
  129. bool IncomingSocket::handleVersion(QTcpSocket *socket)
  130. {
  131. /* 0x49 0x4D [numversions] (numversions * version) */
  132. qint64 available = socket->bytesAvailable();
  133. if (available < 3)
  134. return false;
  135. /* Peek for the identifier and version count */
  136. uchar intro[3];
  137. qint64 re = socket->peek(reinterpret_cast<char*>(intro), 3);
  138. if (re < 3)
  139. return false;
  140. if (intro[0] != 0x49 || intro[1] != 0x4D || intro[2] == 0)
  141. {
  142. qDebug() << "Connection rejected: incorrect introduction sequence";
  143. removeSocket(socket);
  144. return false;
  145. }
  146. /* Stop and wait if the full list of supported versions is not here */
  147. if (available < (intro[2] + 3))
  148. return false;
  149. QByteArray versions = socket->read(intro[2] + 3);
  150. Q_ASSERT(versions.size() == intro[2] + 3);
  151. /* Only one version is supported right now (protocolVersion). 0xff is the reserved failure code. */
  152. uchar version = 0xff;
  153. for (int i = 3; i < versions.size(); ++i)
  154. {
  155. if ((uchar)versions[i] == Protocol::ProtocolVersion)
  156. {
  157. version = Protocol::ProtocolVersion;
  158. break;
  159. }
  160. }
  161. /* Send the version response */
  162. socket->write(reinterpret_cast<char*>(&version), 1);
  163. if (version == 0xff)
  164. {
  165. qDebug() << "Connection rejected: no mutually supported protocol version";
  166. removeSocket(socket);
  167. return false;
  168. }
  169. /* Set the version property (used for the rest of the intro) */
  170. socket->setProperty("protocolVersion", QVariant((unsigned)version));
  171. return true;
  172. }
  173. void IncomingSocket::handleIntro(QTcpSocket *socket, uchar version)
  174. {
  175. Q_ASSERT(version == Protocol::ProtocolVersion);
  176. Q_UNUSED(version);
  177. /* Peek at the purpose; can't be a read as this may be called repeatedly until it's ready */
  178. uchar purpose;
  179. if (socket->peek(reinterpret_cast<char*>(&purpose), 1) < 1)
  180. return;
  181. if (purpose == Protocol::PurposePrimary)
  182. {
  183. /* Wait until the auth data is available */
  184. quint64 available = socket->bytesAvailable();
  185. if (available < 16)
  186. return;
  187. QByteArray secret = socket->read(17);
  188. /* Remove purpose */
  189. secret.remove(0, 1);
  190. Q_ASSERT(secret.size() == 16);
  191. ContactUser *user = identity->contacts.lookupSecret(secret);
  192. /* Response; 0x00 is success, while all others are error. 0x01 is generic error. */
  193. char response = 0x01;
  194. if (!user)
  195. {
  196. qDebug() << "Connection authentication failed: no match for secret";
  197. response = 0x02;
  198. socket->write(&response, 1);
  199. removeSocket(socket);
  200. return;
  201. }
  202. qDebug() << "Connection authentication successful for contact" << user->uniqueID
  203. << "purpose" << hex << purpose;
  204. /* 0x00 is success */
  205. response = 0x00;
  206. socket->write(&response, 1);
  207. pendingSockets.removeOne(socket);
  208. socket->disconnect(this);
  209. /* The protocolmanager also takes ownership */
  210. user->incomingProtocolSocket(socket);
  211. Q_ASSERT(socket->parent() != this);
  212. }
  213. else if (purpose == Protocol::PurposeContactReq)
  214. {
  215. /* Incoming contact request connection */
  216. /* Read purpose */
  217. int ok = socket->read(1).size();
  218. Q_ASSERT(ok);
  219. Q_UNUSED(ok);
  220. /* Pass to ContactRequestServer */
  221. pendingSockets.removeOne(socket);
  222. socket->disconnect(this);
  223. new ContactRequestServer(identity, socket);
  224. Q_ASSERT(socket->parent() != this);
  225. }
  226. else
  227. {
  228. /* Purpose unknown and not supported; just close the connection, because somebody
  229. * isn't obeying the rules of protocol versioning. */
  230. qDebug() << "Connection rejected: Unrecognized version 0 purpose" << hex << purpose;
  231. removeSocket(socket);
  232. }
  233. }
  234. QByteArray IncomingSocket::introData(Protocol::Purpose purpose)
  235. {
  236. QByteArray re;
  237. re.resize(5);
  238. re[0] = 0x49;
  239. re[1] = 0x4D;
  240. re[2] = 0x01; /* number of versions */
  241. re[3] = Protocol::ProtocolVersion; /* version */
  242. re[4] = (char)purpose;
  243. return re;
  244. }