purple-socket.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. /* purple
  2. *
  3. * Purple is the legal property of its developers, whose names are too numerous
  4. * to list here. Please refer to the COPYRIGHT file distributed with this
  5. * source distribution.
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
  20. */
  21. #include "purple-socket.h"
  22. #ifndef _WIN32
  23. #include <errno.h>
  24. #include <unistd.h>
  25. #endif
  26. #include "internal.h"
  27. #include "debug.h"
  28. #include "proxy.h"
  29. #include "sslconn.h"
  30. typedef enum {
  31. PURPLE_SOCKET_STATE_DISCONNECTED = 0,
  32. PURPLE_SOCKET_STATE_CONNECTING,
  33. PURPLE_SOCKET_STATE_CONNECTED,
  34. PURPLE_SOCKET_STATE_ERROR
  35. } PurpleSocketState;
  36. struct _PurpleSocket
  37. {
  38. PurpleConnection *gc;
  39. gchar *host;
  40. int port;
  41. gboolean is_tls;
  42. GHashTable *data;
  43. PurpleSocketState state;
  44. PurpleSslConnection *tls_connection;
  45. PurpleProxyConnectData *raw_connection;
  46. int fd;
  47. guint inpa;
  48. PurpleSocketConnectCb cb;
  49. gpointer cb_data;
  50. };
  51. static GHashTable *handles = NULL;
  52. static void
  53. handle_add(PurpleSocket *ps)
  54. {
  55. PurpleConnection *gc = ps->gc;
  56. GSList *l;
  57. l = g_hash_table_lookup(handles, gc);
  58. l = g_slist_prepend(l, ps);
  59. g_hash_table_insert(handles, gc, l);
  60. }
  61. static void
  62. handle_remove(PurpleSocket *ps)
  63. {
  64. PurpleConnection *gc = ps->gc;
  65. GSList *l;
  66. l = g_hash_table_lookup(handles, gc);
  67. l = g_slist_remove(l, ps);
  68. g_hash_table_insert(handles, gc, l);
  69. }
  70. void
  71. _purple_socket_init(void)
  72. {
  73. handles = g_hash_table_new(g_direct_hash, g_direct_equal);
  74. }
  75. void
  76. _purple_socket_uninit(void)
  77. {
  78. g_hash_table_destroy(handles);
  79. handles = NULL;
  80. }
  81. PurpleSocket *
  82. purple_socket_new(PurpleConnection *gc)
  83. {
  84. PurpleSocket *ps = g_new0(PurpleSocket, 1);
  85. ps->gc = gc;
  86. ps->fd = -1;
  87. ps->port = -1;
  88. ps->data = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
  89. handle_add(ps);
  90. return ps;
  91. }
  92. PurpleConnection *
  93. purple_socket_get_connection(PurpleSocket *ps)
  94. {
  95. g_return_val_if_fail(ps != NULL, NULL);
  96. return ps->gc;
  97. }
  98. static gboolean
  99. purple_socket_check_state(PurpleSocket *ps, PurpleSocketState wanted_state)
  100. {
  101. g_return_val_if_fail(ps != NULL, FALSE);
  102. if (ps->state == wanted_state)
  103. return TRUE;
  104. purple_debug_error("socket", "invalid state: %d (should be: %d)",
  105. ps->state, wanted_state);
  106. ps->state = PURPLE_SOCKET_STATE_ERROR;
  107. return FALSE;
  108. }
  109. void
  110. purple_socket_set_tls(PurpleSocket *ps, gboolean is_tls)
  111. {
  112. g_return_if_fail(ps != NULL);
  113. if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED))
  114. return;
  115. ps->is_tls = is_tls;
  116. }
  117. void
  118. purple_socket_set_host(PurpleSocket *ps, const gchar *host)
  119. {
  120. g_return_if_fail(ps != NULL);
  121. if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED))
  122. return;
  123. g_free(ps->host);
  124. ps->host = g_strdup(host);
  125. }
  126. void
  127. purple_socket_set_port(PurpleSocket *ps, int port)
  128. {
  129. g_return_if_fail(ps != NULL);
  130. g_return_if_fail(port >= 0);
  131. g_return_if_fail(port <= 65535);
  132. if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED))
  133. return;
  134. ps->port = port;
  135. }
  136. static void
  137. _purple_socket_connected_raw(gpointer _ps, gint fd, const gchar *error_message)
  138. {
  139. PurpleSocket *ps = _ps;
  140. ps->raw_connection = NULL;
  141. if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTING)) {
  142. if (fd > 0)
  143. close(fd);
  144. ps->cb(ps, _("Invalid socket state"), ps->cb_data);
  145. return;
  146. }
  147. if (fd <= 0 || error_message != NULL) {
  148. if (error_message == NULL)
  149. error_message = _("Unknown error");
  150. ps->fd = -1;
  151. ps->state = PURPLE_SOCKET_STATE_ERROR;
  152. ps->cb(ps, error_message, ps->cb_data);
  153. return;
  154. }
  155. ps->state = PURPLE_SOCKET_STATE_CONNECTED;
  156. ps->fd = fd;
  157. ps->cb(ps, NULL, ps->cb_data);
  158. }
  159. static void
  160. _purple_socket_connected_tls(gpointer _ps, PurpleSslConnection *tls_connection,
  161. PurpleInputCondition cond)
  162. {
  163. PurpleSocket *ps = _ps;
  164. if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTING)) {
  165. purple_ssl_close(tls_connection);
  166. ps->tls_connection = NULL;
  167. ps->cb(ps, _("Invalid socket state"), ps->cb_data);
  168. return;
  169. }
  170. if (ps->tls_connection->fd <= 0) {
  171. ps->state = PURPLE_SOCKET_STATE_ERROR;
  172. purple_ssl_close(tls_connection);
  173. ps->tls_connection = NULL;
  174. ps->cb(ps, _("Invalid file descriptor"), ps->cb_data);
  175. return;
  176. }
  177. ps->state = PURPLE_SOCKET_STATE_CONNECTED;
  178. ps->fd = ps->tls_connection->fd;
  179. ps->cb(ps, NULL, ps->cb_data);
  180. }
  181. static void
  182. _purple_socket_connected_tls_error(PurpleSslConnection *ssl_connection,
  183. PurpleSslErrorType error, gpointer _ps)
  184. {
  185. PurpleSocket *ps = _ps;
  186. ps->state = PURPLE_SOCKET_STATE_ERROR;
  187. ps->tls_connection = NULL;
  188. ps->cb(ps, purple_ssl_strerror(error), ps->cb_data);
  189. }
  190. gboolean
  191. purple_socket_connect(PurpleSocket *ps, PurpleSocketConnectCb cb,
  192. gpointer user_data)
  193. {
  194. PurpleAccount *account = NULL;
  195. g_return_val_if_fail(ps != NULL, FALSE);
  196. if (ps->gc && purple_connection_is_disconnecting(ps->gc)) {
  197. purple_debug_error("socket", "connection is being destroyed");
  198. ps->state = PURPLE_SOCKET_STATE_ERROR;
  199. return FALSE;
  200. }
  201. if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED))
  202. return FALSE;
  203. ps->state = PURPLE_SOCKET_STATE_CONNECTING;
  204. if (ps->host == NULL || ps->port < 0) {
  205. purple_debug_error("socket", "Host or port is not specified");
  206. ps->state = PURPLE_SOCKET_STATE_ERROR;
  207. return FALSE;
  208. }
  209. if (ps->gc != NULL)
  210. account = purple_connection_get_account(ps->gc);
  211. ps->cb = cb;
  212. ps->cb_data = user_data;
  213. if (ps->is_tls) {
  214. ps->tls_connection = purple_ssl_connect(account, ps->host,
  215. ps->port, _purple_socket_connected_tls,
  216. _purple_socket_connected_tls_error, ps);
  217. } else {
  218. ps->raw_connection = purple_proxy_connect(ps->gc, account,
  219. ps->host, ps->port, _purple_socket_connected_raw, ps);
  220. }
  221. if (ps->tls_connection == NULL &&
  222. ps->raw_connection == NULL)
  223. {
  224. ps->state = PURPLE_SOCKET_STATE_ERROR;
  225. return FALSE;
  226. }
  227. return TRUE;
  228. }
  229. gssize
  230. purple_socket_read(PurpleSocket *ps, guchar *buf, size_t len)
  231. {
  232. g_return_val_if_fail(ps != NULL, -1);
  233. g_return_val_if_fail(buf != NULL, -1);
  234. if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED))
  235. return -1;
  236. if (ps->is_tls)
  237. return purple_ssl_read(ps->tls_connection, buf, len);
  238. else
  239. return read(ps->fd, buf, len);
  240. }
  241. gssize
  242. purple_socket_write(PurpleSocket *ps, const guchar *buf, size_t len)
  243. {
  244. g_return_val_if_fail(ps != NULL, -1);
  245. g_return_val_if_fail(buf != NULL, -1);
  246. if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED))
  247. return -1;
  248. if (ps->is_tls)
  249. return purple_ssl_write(ps->tls_connection, buf, len);
  250. else
  251. return write(ps->fd, buf, len);
  252. }
  253. void
  254. purple_socket_watch(PurpleSocket *ps, PurpleInputCondition cond,
  255. PurpleInputFunction func, gpointer user_data)
  256. {
  257. g_return_if_fail(ps != NULL);
  258. if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED))
  259. return;
  260. if (ps->inpa > 0)
  261. purple_input_remove(ps->inpa);
  262. ps->inpa = 0;
  263. g_return_if_fail(ps->fd > 0);
  264. if (func != NULL)
  265. ps->inpa = purple_input_add(ps->fd, cond, func, user_data);
  266. }
  267. int
  268. purple_socket_get_fd(PurpleSocket *ps)
  269. {
  270. g_return_val_if_fail(ps != NULL, -1);
  271. if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED))
  272. return -1;
  273. g_return_val_if_fail(ps->fd > 0, -1);
  274. return ps->fd;
  275. }
  276. void
  277. purple_socket_set_data(PurpleSocket *ps, const gchar *key, gpointer data)
  278. {
  279. g_return_if_fail(ps != NULL);
  280. g_return_if_fail(key != NULL);
  281. if (data == NULL)
  282. g_hash_table_remove(ps->data, key);
  283. else
  284. g_hash_table_insert(ps->data, g_strdup(key), data);
  285. }
  286. gpointer
  287. purple_socket_get_data(PurpleSocket *ps, const gchar *key)
  288. {
  289. g_return_val_if_fail(ps != NULL, NULL);
  290. g_return_val_if_fail(key != NULL, NULL);
  291. return g_hash_table_lookup(ps->data, key);
  292. }
  293. static void
  294. purple_socket_cancel(PurpleSocket *ps)
  295. {
  296. if (ps->inpa > 0)
  297. purple_input_remove(ps->inpa);
  298. ps->inpa = 0;
  299. if (ps->tls_connection != NULL) {
  300. purple_ssl_close(ps->tls_connection);
  301. ps->fd = -1;
  302. }
  303. ps->tls_connection = NULL;
  304. if (ps->raw_connection != NULL)
  305. purple_proxy_connect_cancel(ps->raw_connection);
  306. ps->raw_connection = NULL;
  307. if (ps->fd > 0)
  308. close(ps->fd);
  309. ps->fd = 0;
  310. }
  311. void
  312. purple_socket_destroy(PurpleSocket *ps)
  313. {
  314. if (ps == NULL)
  315. return;
  316. handle_remove(ps);
  317. purple_socket_cancel(ps);
  318. g_free(ps->host);
  319. g_hash_table_destroy(ps->data);
  320. g_free(ps);
  321. }
  322. void
  323. _purple_socket_cancel_with_connection(PurpleConnection *gc)
  324. {
  325. GSList *it;
  326. it = g_hash_table_lookup(handles, gc);
  327. for (; it; it = g_slist_next(it)) {
  328. PurpleSocket *ps = it->data;
  329. purple_socket_cancel(ps);
  330. }
  331. }