PageRenderTime 43ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/src/protocol/ContactRequestClient.cpp

https://gitlab.com/gaurav1981/ricochet
C++ | 276 lines | 190 code | 45 blank | 41 comment | 24 complexity | 16c55d82d958928962698075e79ec9d6 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 "ContactRequestClient.h"
  33. #include "core/ContactUser.h"
  34. #include "core/UserIdentity.h"
  35. #include "IncomingSocket.h"
  36. #include "CommandDataParser.h"
  37. #include "ProtocolConstants.h"
  38. #include "tor/HiddenService.h"
  39. #include "tor/TorSocket.h"
  40. #include "utils/CryptoKey.h"
  41. #include <QNetworkProxy>
  42. #include <QtEndian>
  43. #include <QTimer>
  44. #include <QDebug>
  45. ContactRequestClient::ContactRequestClient(ContactUser *u)
  46. : QObject(u), user(u), socket(0), m_response(NoResponse), state(NotConnected)
  47. {
  48. }
  49. void ContactRequestClient::setMessage(const QString &message)
  50. {
  51. m_message = message;
  52. }
  53. void ContactRequestClient::setMyNickname(const QString &nick)
  54. {
  55. m_mynick = nick;
  56. }
  57. void ContactRequestClient::close()
  58. {
  59. if (socket)
  60. {
  61. socket->disconnect(this);
  62. socket->abort();
  63. socket->deleteLater();
  64. socket = 0;
  65. }
  66. state = NotConnected;
  67. }
  68. void ContactRequestClient::sendRequest()
  69. {
  70. close();
  71. socket = new Tor::TorSocket(this);
  72. connect(socket, SIGNAL(connected()), this, SLOT(socketConnected()));
  73. connect(socket, SIGNAL(readyRead()), this, SLOT(socketReadable()));
  74. socket->setMaxAttemptInterval(600);
  75. state = WaitConnect;
  76. socket->connectToHost(user->hostname(), user->port());
  77. }
  78. void ContactRequestClient::socketConnected()
  79. {
  80. socket->write(IncomingSocket::introData(Protocol::PurposeContactReq));
  81. state = WaitConnect;
  82. qDebug() << "Contact request for" << user->uniqueID << "connected";
  83. }
  84. void ContactRequestClient::socketReadable()
  85. {
  86. switch (state)
  87. {
  88. case WaitConnect:
  89. {
  90. uchar version;
  91. if (socket->read(reinterpret_cast<char*>(&version), 1) < 1)
  92. return;
  93. if (version != Protocol::ProtocolVersion)
  94. {
  95. emit rejected(Protocol::RequestVersionError);
  96. close();
  97. return;
  98. }
  99. state = WaitCookie;
  100. /* Deliberately omitted break; cookie may arrive instantly */
  101. }
  102. case WaitCookie:
  103. if (socket->bytesAvailable() < Protocol::RequestCookieSize)
  104. return;
  105. if (!buildRequestData(socket->read(Protocol::RequestCookieSize)))
  106. {
  107. socket->close();
  108. return;
  109. }
  110. state = WaitAck;
  111. break;
  112. case WaitAck:
  113. case WaitResponse:
  114. if (!handleResponse() && socket)
  115. {
  116. socket->close();
  117. return;
  118. }
  119. break;
  120. default:
  121. break;
  122. }
  123. }
  124. bool ContactRequestClient::buildRequestData(QByteArray cookie)
  125. {
  126. /* [2*length][16*hostname][16*serverCookie][16*connSecret][data:pubkey][str:nick][str:message][data:signature] */
  127. QByteArray requestData;
  128. CommandDataParser request(&requestData);
  129. /* Hostname */
  130. QString hostname = user->hostname();
  131. hostname.truncate(hostname.lastIndexOf(QLatin1Char('.')));
  132. if (hostname.size() != 16)
  133. {
  134. qWarning() << "Cannot send contact request: unable to determine the remote service hostname";
  135. return false;
  136. }
  137. /* Connection secret */
  138. QByteArray connSecret = user->settings()->read<Base64Encode>("localSecret");
  139. if (connSecret.size() != 16)
  140. {
  141. qWarning() << "Cannot send contact request: invalid local secret";
  142. return false;
  143. }
  144. /* Public service key */
  145. Tor::HiddenService *service = user->identity->hiddenService();
  146. CryptoKey serviceKey;
  147. if (!service || !(serviceKey = service->cryptoKey()).isLoaded())
  148. {
  149. qWarning() << "Cannot send contact request: failed to load service key";
  150. return false;
  151. }
  152. QByteArray publicKeyData = serviceKey.encodedPublicKey();
  153. if (publicKeyData.isNull())
  154. {
  155. qWarning() << "Cannot send contact request: failed to encode service key";
  156. return false;
  157. }
  158. /* Build request */
  159. request << (quint16)0; /* placeholder for length */
  160. request.writeFixedData(hostname.toLatin1());
  161. request.writeFixedData(cookie);
  162. request.writeFixedData(connSecret);
  163. request.writeVariableData(publicKeyData);
  164. request << myNickname() << message();
  165. if (request.hasError())
  166. {
  167. qWarning() << "Cannot send contact request: command building failed";
  168. return false;
  169. }
  170. /* Sign request, excluding the length field */
  171. QByteArray signature = serviceKey.signData(requestData.mid(2));
  172. if (signature.isNull())
  173. {
  174. qWarning() << "Cannot send contact request: failed to sign request";
  175. return false;
  176. }
  177. request.writeVariableData(signature);
  178. if (request.hasError())
  179. {
  180. qWarning() << "Cannot send contact request: command building failed";
  181. return false;
  182. }
  183. /* Set length */
  184. qToBigEndian((quint16)requestData.size(), reinterpret_cast<uchar*>(requestData.data()));
  185. /* Send */
  186. qint64 re = socket->write(requestData);
  187. Q_ASSERT(re == requestData.size());
  188. Q_UNUSED(re);
  189. qDebug() << "Contact request for" << user->uniqueID << "sent request data";
  190. return true;
  191. }
  192. bool ContactRequestClient::handleResponse()
  193. {
  194. uchar response;
  195. if (socket->read(reinterpret_cast<char*>(&response), 1) < 1)
  196. return true;
  197. /* TODO much more state handling and cleanup */
  198. switch (response)
  199. {
  200. case 0x00: /* Acknowledge */
  201. qDebug() << "Contact request for" << user->uniqueID << "acknowledged; waiting for response";
  202. state = WaitResponse;
  203. m_response = Acknowledged;
  204. emit acknowledged();
  205. break;
  206. case 0x01: /* Accept */
  207. qDebug() << "Contact request for" << user->uniqueID << "accepted! Converting connection to primary";
  208. m_response = Accepted;
  209. emit accepted();
  210. socket->disconnect(this);
  211. user->incomingProtocolSocket(socket);
  212. Q_ASSERT(socket->parent() != this);
  213. socket = 0;
  214. break;
  215. case 0x40:
  216. qDebug() << "Contact request for" << user->uniqueID << "rejected by user";
  217. m_response = Rejected;
  218. break;
  219. default: /* Error */
  220. qDebug() << "Contact request for" << user->uniqueID << "rejected with code" << hex << (int)response;
  221. m_response = Error;
  222. break;
  223. }
  224. emit responseChanged();
  225. if (m_response >= Rejected)
  226. {
  227. emit rejected(response);
  228. return false;
  229. }
  230. return true;
  231. }