PageRenderTime 47ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/src/QXmppSocks.cpp

http://qxmpp.googlecode.com/
C++ | 345 lines | 255 code | 46 blank | 44 comment | 41 complexity | 0d7e730c368596920f117334127f8094 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /*
  2. * Copyright (C) 2008-2011 The QXmpp developers
  3. *
  4. * Author:
  5. * Jeremy LainĂŠ
  6. *
  7. * Source:
  8. * http://code.google.com/p/qxmpp
  9. *
  10. * This file is a part of QXmpp library.
  11. *
  12. * This library is free software; you can redistribute it and/or
  13. * modify it under the terms of the GNU Lesser General Public
  14. * License as published by the Free Software Foundation; either
  15. * version 2.1 of the License, or (at your option) any later version.
  16. *
  17. * This library is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  20. * Lesser General Public License for more details.
  21. *
  22. */
  23. #include <QDataStream>
  24. #include <QEventLoop>
  25. #include <QTcpServer>
  26. #include <QTcpSocket>
  27. #include <QTimer>
  28. #include "QXmppSocks.h"
  29. const static char SocksVersion = 5;
  30. enum AuthenticationMethod {
  31. NoAuthentication = 0,
  32. GSSAPI = 1,
  33. UsernamePassword = 2,
  34. };
  35. enum Command {
  36. ConnectCommand = 1,
  37. BindCommand = 2,
  38. AssociateCommand = 3,
  39. };
  40. enum AddressType {
  41. IPv4Address = 1,
  42. DomainName = 3,
  43. IPv6Address = 4,
  44. };
  45. enum ReplyType {
  46. Succeeded = 0,
  47. SocksFailure = 1,
  48. ConnectionNotAllowed = 2,
  49. NetworkUnreachable = 3,
  50. HostUnreachable = 4,
  51. ConnectionRefused = 5,
  52. TtlExpired = 6,
  53. CommandNotSupported = 7,
  54. AddressTypeNotSupported = 8,
  55. };
  56. enum State {
  57. ConnectState = 0,
  58. CommandState = 1,
  59. ReadyState = 2,
  60. };
  61. static QByteArray encodeHostAndPort(quint8 type, const QByteArray &host, quint16 port)
  62. {
  63. QByteArray buffer;
  64. QDataStream stream(&buffer, QIODevice::WriteOnly);
  65. // set host name
  66. quint8 hostLength = host.size();
  67. stream << type;
  68. stream << hostLength;
  69. stream.writeRawData(host.constData(), hostLength);
  70. // set port
  71. stream << port;
  72. return buffer;
  73. }
  74. static bool parseHostAndPort(const QByteArray buffer, quint8 &type, QByteArray &host, quint16 &port)
  75. {
  76. if (buffer.size() < 4)
  77. return false;
  78. QDataStream stream(buffer);
  79. // get host name
  80. quint8 hostLength;
  81. stream >> type;
  82. stream >> hostLength;
  83. if (buffer.size() < hostLength + 4)
  84. {
  85. qWarning("Invalid host length");
  86. return false;
  87. }
  88. host.resize(hostLength);
  89. stream.readRawData(host.data(), hostLength);
  90. // get port
  91. stream >> port;
  92. return true;
  93. }
  94. QXmppSocksClient::QXmppSocksClient(const QHostAddress &proxyAddress, quint16 proxyPort, QObject *parent)
  95. : QTcpSocket(parent),
  96. m_proxyAddress(proxyAddress),
  97. m_proxyPort(proxyPort),
  98. m_step(ConnectState)
  99. {
  100. connect(this, SIGNAL(connected()), this, SLOT(slotConnected()));
  101. connect(this, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
  102. }
  103. void QXmppSocksClient::connectToHost(const QString &hostName, quint16 hostPort)
  104. {
  105. m_hostName = hostName;
  106. m_hostPort = hostPort;
  107. QTcpSocket::connectToHost(m_proxyAddress, m_proxyPort);
  108. }
  109. void QXmppSocksClient::slotConnected()
  110. {
  111. m_step = ConnectState;
  112. // disconnect from signal
  113. disconnect(this, SIGNAL(connected()), this, SLOT(slotConnected()));
  114. // send connect to server
  115. QByteArray buffer;
  116. buffer.resize(3);
  117. buffer[0] = SocksVersion;
  118. buffer[1] = 0x01; // number of methods
  119. buffer[2] = NoAuthentication;
  120. write(buffer);
  121. }
  122. void QXmppSocksClient::slotReadyRead()
  123. {
  124. if (m_step == ConnectState)
  125. {
  126. m_step++;
  127. // receive connect to server response
  128. QByteArray buffer = readAll();
  129. if (buffer.size() != 2 || buffer.at(0) != SocksVersion || buffer.at(1) != NoAuthentication)
  130. {
  131. qWarning("QXmppSocksClient received an invalid response during handshake");
  132. close();
  133. return;
  134. }
  135. // send CONNECT command
  136. buffer.resize(3);
  137. buffer[0] = SocksVersion;
  138. buffer[1] = ConnectCommand;
  139. buffer[2] = 0x00; // reserved
  140. buffer.append(encodeHostAndPort(
  141. DomainName,
  142. m_hostName.toAscii(),
  143. m_hostPort));
  144. write(buffer);
  145. } else if (m_step == CommandState) {
  146. m_step++;
  147. // disconnect from signal
  148. disconnect(this, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
  149. // receive CONNECT response
  150. QByteArray buffer = readAll();
  151. if (buffer.size() < 6 ||
  152. buffer.at(0) != SocksVersion ||
  153. buffer.at(1) != Succeeded ||
  154. buffer.at(2) != 0)
  155. {
  156. qWarning("QXmppSocksClient received an invalid response to CONNECT command");
  157. close();
  158. return;
  159. }
  160. // parse host
  161. quint8 hostType;
  162. QByteArray hostName;
  163. quint16 hostPort;
  164. if (!parseHostAndPort(buffer.mid(3), hostType, hostName, hostPort))
  165. {
  166. qWarning("QXmppSocksClient could not parse type/host/port");
  167. close();
  168. return;
  169. }
  170. // FIXME : what do we do with the resulting name / port?
  171. // notify of connection
  172. emit ready();
  173. }
  174. }
  175. bool QXmppSocksClient::waitForReady(int msecs)
  176. {
  177. QEventLoop loop;
  178. connect(this, SIGNAL(disconnected()), &loop, SLOT(quit()));
  179. connect(this, SIGNAL(ready()), &loop, SLOT(quit()));
  180. QTimer::singleShot(msecs, &loop, SLOT(quit()));
  181. loop.exec();
  182. if (m_step == ReadyState && isValid())
  183. return true;
  184. else
  185. return false;
  186. }
  187. QXmppSocksServer::QXmppSocksServer(QObject *parent)
  188. : QObject(parent)
  189. {
  190. m_server = new QTcpServer(this);
  191. connect(m_server, SIGNAL(newConnection()), this, SLOT(slotNewConnection()));
  192. }
  193. void QXmppSocksServer::close()
  194. {
  195. m_server->close();
  196. }
  197. bool QXmppSocksServer::listen(const QHostAddress &address, quint16 port)
  198. {
  199. return m_server->listen(address, port);
  200. }
  201. bool QXmppSocksServer::isListening() const
  202. {
  203. return m_server->isListening();
  204. }
  205. QHostAddress QXmppSocksServer::serverAddress() const
  206. {
  207. return m_server->serverAddress();
  208. }
  209. quint16 QXmppSocksServer::serverPort() const
  210. {
  211. return m_server->serverPort();
  212. }
  213. void QXmppSocksServer::slotNewConnection()
  214. {
  215. QTcpSocket *socket = m_server->nextPendingConnection();
  216. if (!socket)
  217. return;
  218. // register socket
  219. m_states.insert(socket, ConnectState);
  220. connect(socket, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
  221. }
  222. void QXmppSocksServer::slotReadyRead()
  223. {
  224. QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender());
  225. if (!socket || !m_states.contains(socket))
  226. return;
  227. if (m_states.value(socket) == ConnectState)
  228. {
  229. m_states.insert(socket, CommandState);
  230. // receive connect to server request
  231. QByteArray buffer = socket->readAll();
  232. if (buffer.size() < 3 ||
  233. buffer.at(0) != SocksVersion ||
  234. buffer.at(1) + 2 != buffer.size())
  235. {
  236. qWarning("QXmppSocksServer received invalid handshake");
  237. socket->close();
  238. return;
  239. }
  240. // check authentication method
  241. bool foundMethod = false;
  242. for (int i = 2; i < buffer.size(); i++)
  243. {
  244. if (buffer.at(i) == NoAuthentication)
  245. {
  246. foundMethod = true;
  247. break;
  248. }
  249. }
  250. if (!foundMethod)
  251. {
  252. qWarning("QXmppSocksServer received bad authentication method");
  253. socket->close();
  254. return;
  255. }
  256. // send connect to server response
  257. buffer.resize(2);
  258. buffer[0] = SocksVersion;
  259. buffer[1] = NoAuthentication;
  260. socket->write(buffer);
  261. } else if (m_states.value(socket) == CommandState) {
  262. m_states.insert(socket, ReadyState);
  263. // disconnect from signals
  264. disconnect(socket, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
  265. // receive command
  266. QByteArray buffer = socket->readAll();
  267. if (buffer.size() < 4 ||
  268. buffer.at(0) != SocksVersion ||
  269. buffer.at(1) != ConnectCommand ||
  270. buffer.at(2) != 0x00)
  271. {
  272. qWarning("QXmppSocksServer received an invalid command");
  273. socket->close();
  274. return;
  275. }
  276. // parse host
  277. quint8 hostType;
  278. QByteArray hostName;
  279. quint16 hostPort;
  280. if (!parseHostAndPort(buffer.mid(3), hostType, hostName, hostPort))
  281. {
  282. qWarning("QXmppSocksServer could not parse type/host/port");
  283. socket->close();
  284. return;
  285. }
  286. // notify of connection
  287. emit newConnection(socket, hostName, hostPort);
  288. // send response
  289. buffer.resize(3);
  290. buffer[0] = SocksVersion;
  291. buffer[1] = Succeeded;
  292. buffer[2] = 0x00;
  293. buffer.append(encodeHostAndPort(
  294. DomainName,
  295. hostName,
  296. hostPort));
  297. socket->write(buffer);
  298. }
  299. }