QXmppSocks.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. /*
  2. * Copyright (C) 2008-2012 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 <QTcpServer>
  25. #include <QTcpSocket>
  26. #include <QTimer>
  27. #include "QXmppSocks.h"
  28. const static char SocksVersion = 5;
  29. enum AuthenticationMethod {
  30. NoAuthentication = 0,
  31. GSSAPI = 1,
  32. UsernamePassword = 2,
  33. };
  34. enum Command {
  35. ConnectCommand = 1,
  36. BindCommand = 2,
  37. AssociateCommand = 3,
  38. };
  39. enum AddressType {
  40. IPv4Address = 1,
  41. DomainName = 3,
  42. IPv6Address = 4,
  43. };
  44. enum ReplyType {
  45. Succeeded = 0,
  46. SocksFailure = 1,
  47. ConnectionNotAllowed = 2,
  48. NetworkUnreachable = 3,
  49. HostUnreachable = 4,
  50. ConnectionRefused = 5,
  51. TtlExpired = 6,
  52. CommandNotSupported = 7,
  53. AddressTypeNotSupported = 8,
  54. };
  55. enum State {
  56. ConnectState = 0,
  57. CommandState = 1,
  58. ReadyState = 2,
  59. };
  60. static QByteArray encodeHostAndPort(quint8 type, const QByteArray &host, quint16 port)
  61. {
  62. QByteArray buffer;
  63. QDataStream stream(&buffer, QIODevice::WriteOnly);
  64. // set host name
  65. quint8 hostLength = host.size();
  66. stream << type;
  67. stream << hostLength;
  68. stream.writeRawData(host.constData(), hostLength);
  69. // set port
  70. stream << port;
  71. return buffer;
  72. }
  73. static bool parseHostAndPort(const QByteArray buffer, quint8 &type, QByteArray &host, quint16 &port)
  74. {
  75. if (buffer.size() < 4)
  76. return false;
  77. QDataStream stream(buffer);
  78. // get host name
  79. quint8 hostLength;
  80. stream >> type;
  81. stream >> hostLength;
  82. if (buffer.size() < hostLength + 4)
  83. {
  84. qWarning("Invalid host length");
  85. return false;
  86. }
  87. host.resize(hostLength);
  88. stream.readRawData(host.data(), hostLength);
  89. // get port
  90. stream >> port;
  91. return true;
  92. }
  93. QXmppSocksClient::QXmppSocksClient(const QString &proxyHost, quint16 proxyPort, QObject *parent)
  94. : QTcpSocket(parent),
  95. m_proxyHost(proxyHost),
  96. m_proxyPort(proxyPort),
  97. m_step(ConnectState)
  98. {
  99. connect(this, SIGNAL(connected()), this, SLOT(slotConnected()));
  100. connect(this, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
  101. }
  102. void QXmppSocksClient::connectToHost(const QString &hostName, quint16 hostPort)
  103. {
  104. m_hostName = hostName;
  105. m_hostPort = hostPort;
  106. QTcpSocket::connectToHost(m_proxyHost, m_proxyPort);
  107. }
  108. void QXmppSocksClient::slotConnected()
  109. {
  110. m_step = ConnectState;
  111. // disconnect from signal
  112. disconnect(this, SIGNAL(connected()), this, SLOT(slotConnected()));
  113. // send connect to server
  114. QByteArray buffer;
  115. buffer.resize(3);
  116. buffer[0] = SocksVersion;
  117. buffer[1] = 0x01; // number of methods
  118. buffer[2] = NoAuthentication;
  119. write(buffer);
  120. }
  121. void QXmppSocksClient::slotReadyRead()
  122. {
  123. if (m_step == ConnectState)
  124. {
  125. m_step++;
  126. // receive connect to server response
  127. QByteArray buffer = readAll();
  128. if (buffer.size() != 2 || buffer.at(0) != SocksVersion || buffer.at(1) != NoAuthentication)
  129. {
  130. qWarning("QXmppSocksClient received an invalid response during handshake");
  131. close();
  132. return;
  133. }
  134. // send CONNECT command
  135. buffer.resize(3);
  136. buffer[0] = SocksVersion;
  137. buffer[1] = ConnectCommand;
  138. buffer[2] = 0x00; // reserved
  139. buffer.append(encodeHostAndPort(
  140. DomainName,
  141. m_hostName.toAscii(),
  142. m_hostPort));
  143. write(buffer);
  144. } else if (m_step == CommandState) {
  145. m_step++;
  146. // disconnect from signal
  147. disconnect(this, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
  148. // receive CONNECT response
  149. QByteArray buffer = readAll();
  150. if (buffer.size() < 6 ||
  151. buffer.at(0) != SocksVersion ||
  152. buffer.at(1) != Succeeded ||
  153. buffer.at(2) != 0)
  154. {
  155. qWarning("QXmppSocksClient received an invalid response to CONNECT command");
  156. close();
  157. return;
  158. }
  159. // parse host
  160. quint8 hostType;
  161. QByteArray hostName;
  162. quint16 hostPort;
  163. if (!parseHostAndPort(buffer.mid(3), hostType, hostName, hostPort))
  164. {
  165. qWarning("QXmppSocksClient could not parse type/host/port");
  166. close();
  167. return;
  168. }
  169. // FIXME : what do we do with the resulting name / port?
  170. // notify of connection
  171. emit ready();
  172. }
  173. }
  174. QXmppSocksServer::QXmppSocksServer(QObject *parent)
  175. : QObject(parent)
  176. {
  177. m_server = new QTcpServer(this);
  178. connect(m_server, SIGNAL(newConnection()), this, SLOT(slotNewConnection()));
  179. m_server_v6 = new QTcpServer(this);
  180. connect(m_server_v6, SIGNAL(newConnection()), this, SLOT(slotNewConnection()));
  181. }
  182. void QXmppSocksServer::close()
  183. {
  184. m_server->close();
  185. m_server_v6->close();
  186. }
  187. bool QXmppSocksServer::listen(quint16 port)
  188. {
  189. if (!m_server->listen(QHostAddress::Any, port))
  190. return false;
  191. m_server_v6->listen(QHostAddress::AnyIPv6, m_server->serverPort());
  192. return true;
  193. }
  194. quint16 QXmppSocksServer::serverPort() const
  195. {
  196. return m_server->serverPort();
  197. }
  198. void QXmppSocksServer::slotNewConnection()
  199. {
  200. QTcpServer *server = qobject_cast<QTcpServer*>(sender());
  201. if (!server)
  202. return;
  203. QTcpSocket *socket = server->nextPendingConnection();
  204. if (!socket)
  205. return;
  206. // register socket
  207. m_states.insert(socket, ConnectState);
  208. connect(socket, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
  209. }
  210. void QXmppSocksServer::slotReadyRead()
  211. {
  212. QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender());
  213. if (!socket || !m_states.contains(socket))
  214. return;
  215. if (m_states.value(socket) == ConnectState)
  216. {
  217. m_states.insert(socket, CommandState);
  218. // receive connect to server request
  219. QByteArray buffer = socket->readAll();
  220. if (buffer.size() < 3 ||
  221. buffer.at(0) != SocksVersion ||
  222. buffer.at(1) + 2 != buffer.size())
  223. {
  224. qWarning("QXmppSocksServer received invalid handshake");
  225. socket->close();
  226. return;
  227. }
  228. // check authentication method
  229. bool foundMethod = false;
  230. for (int i = 2; i < buffer.size(); i++)
  231. {
  232. if (buffer.at(i) == NoAuthentication)
  233. {
  234. foundMethod = true;
  235. break;
  236. }
  237. }
  238. if (!foundMethod)
  239. {
  240. qWarning("QXmppSocksServer received bad authentication method");
  241. socket->close();
  242. return;
  243. }
  244. // send connect to server response
  245. buffer.resize(2);
  246. buffer[0] = SocksVersion;
  247. buffer[1] = NoAuthentication;
  248. socket->write(buffer);
  249. } else if (m_states.value(socket) == CommandState) {
  250. m_states.insert(socket, ReadyState);
  251. // disconnect from signals
  252. disconnect(socket, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
  253. // receive command
  254. QByteArray buffer = socket->readAll();
  255. if (buffer.size() < 4 ||
  256. buffer.at(0) != SocksVersion ||
  257. buffer.at(1) != ConnectCommand ||
  258. buffer.at(2) != 0x00)
  259. {
  260. qWarning("QXmppSocksServer received an invalid command");
  261. socket->close();
  262. return;
  263. }
  264. // parse host
  265. quint8 hostType;
  266. QByteArray hostName;
  267. quint16 hostPort;
  268. if (!parseHostAndPort(buffer.mid(3), hostType, hostName, hostPort))
  269. {
  270. qWarning("QXmppSocksServer could not parse type/host/port");
  271. socket->close();
  272. return;
  273. }
  274. // notify of connection
  275. emit newConnection(socket, hostName, hostPort);
  276. // send response
  277. buffer.resize(3);
  278. buffer[0] = SocksVersion;
  279. buffer[1] = Succeeded;
  280. buffer[2] = 0x00;
  281. buffer.append(encodeHostAndPort(
  282. DomainName,
  283. hostName,
  284. hostPort));
  285. socket->write(buffer);
  286. }
  287. }