PageRenderTime 38ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/socket/tcp-passive.c

https://gitlab.com/libnice/libnice
C | 341 lines | 240 code | 58 blank | 43 comment | 35 complexity | cb0dc3a957c3a446a3432a6a4e850d57 MD5 | raw file
  1. /*
  2. * This file is part of the Nice GLib ICE library.
  3. *
  4. * (C) 2008-2012 Collabora Ltd.
  5. * Contact: Youness Alaoui
  6. * (C) 2008-2009 Nokia Corporation. All rights reserved.
  7. *
  8. * The contents of this file are subject to the Mozilla Public License Version
  9. * 1.1 (the "License"); you may not use this file except in compliance with
  10. * the License. You may obtain a copy of the License at
  11. * http://www.mozilla.org/MPL/
  12. *
  13. * Software distributed under the License is distributed on an "AS IS" basis,
  14. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  15. * for the specific language governing rights and limitations under the
  16. * License.
  17. *
  18. * The Original Code is the Nice GLib ICE library.
  19. *
  20. * The Initial Developers of the Original Code are Collabora Ltd and Nokia
  21. * Corporation. All Rights Reserved.
  22. *
  23. * Contributors:
  24. * Youness Alaoui, Collabora Ltd.
  25. * George Kiagiadakis, Collabora Ltd.
  26. *
  27. * Alternatively, the contents of this file may be used under the terms of the
  28. * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
  29. * case the provisions of LGPL are applicable instead of those above. If you
  30. * wish to allow use of your version of this file only under the terms of the
  31. * LGPL and not to allow others to use your version of this file under the
  32. * MPL, indicate your decision by deleting the provisions above and replace
  33. * them with the notice and other provisions required by the LGPL. If you do
  34. * not delete the provisions above, a recipient may use your version of this
  35. * file under either the MPL or the LGPL.
  36. */
  37. #ifdef HAVE_CONFIG_H
  38. # include "config.h"
  39. #endif
  40. #include "tcp-passive.h"
  41. #include "agent-priv.h"
  42. #include <string.h>
  43. #include <errno.h>
  44. #include <fcntl.h>
  45. #ifndef G_OS_WIN32
  46. #include <unistd.h>
  47. #endif
  48. /* FIXME: This should be defined in gio/gnetworking.h, which we should include;
  49. * but we cannot do that without refactoring.
  50. * (See: https://phabricator.freedesktop.org/D230). */
  51. #undef TCP_NODELAY
  52. #define TCP_NODELAY 1
  53. typedef struct {
  54. GMainContext *context;
  55. GHashTable *connections;
  56. NiceSocketWritableCb writable_cb;
  57. gpointer writable_data;
  58. } TcpPassivePriv;
  59. static void socket_close (NiceSocket *sock);
  60. static gint socket_recv_messages (NiceSocket *sock,
  61. NiceInputMessage *recv_messages, guint n_recv_messages);
  62. static gint socket_send_messages (NiceSocket *sock, const NiceAddress *to,
  63. const NiceOutputMessage *messages, guint n_messages);
  64. static gint socket_send_messages_reliable (NiceSocket *sock,
  65. const NiceAddress *to, const NiceOutputMessage *messages, guint n_messages);
  66. static gboolean socket_is_reliable (NiceSocket *sock);
  67. static gboolean socket_can_send (NiceSocket *sock, NiceAddress *addr);
  68. static void socket_set_writable_callback (NiceSocket *sock,
  69. NiceSocketWritableCb callback, gpointer user_data);
  70. static guint nice_address_hash (const NiceAddress * key);
  71. NiceSocket *
  72. nice_tcp_passive_socket_new (GMainContext *ctx, NiceAddress *addr)
  73. {
  74. union {
  75. struct sockaddr_storage storage;
  76. struct sockaddr addr;
  77. } name;
  78. NiceSocket *sock;
  79. TcpPassivePriv *priv;
  80. GSocket *gsock = NULL;
  81. gboolean gret = FALSE;
  82. GSocketAddress *gaddr;
  83. if (addr != NULL) {
  84. nice_address_copy_to_sockaddr(addr, &name.addr);
  85. } else {
  86. memset (&name, 0, sizeof (name));
  87. name.storage.ss_family = AF_UNSPEC;
  88. }
  89. if (name.storage.ss_family == AF_UNSPEC || name.storage.ss_family == AF_INET) {
  90. gsock = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM,
  91. G_SOCKET_PROTOCOL_TCP, NULL);
  92. name.storage.ss_family = AF_INET;
  93. #ifdef HAVE_SA_LEN
  94. name.storage.ss_len = sizeof (struct sockaddr_in);
  95. #endif
  96. } else if (name.storage.ss_family == AF_INET6) {
  97. gsock = g_socket_new (G_SOCKET_FAMILY_IPV6, G_SOCKET_TYPE_STREAM,
  98. G_SOCKET_PROTOCOL_TCP, NULL);
  99. name.storage.ss_family = AF_INET6;
  100. #ifdef HAVE_SA_LEN
  101. name.storage.ss_len = sizeof (struct sockaddr_in6);
  102. #endif
  103. }
  104. if (gsock == NULL) {
  105. return NULL;
  106. }
  107. gaddr = g_socket_address_new_from_native (&name.addr, sizeof (name));
  108. if (gaddr == NULL) {
  109. g_object_unref (gsock);
  110. return NULL;
  111. }
  112. /* GSocket: All socket file descriptors are set to be close-on-exec. */
  113. g_socket_set_blocking (gsock, false);
  114. gret = g_socket_bind (gsock, gaddr, FALSE, NULL) &&
  115. g_socket_listen (gsock, NULL);
  116. g_object_unref (gaddr);
  117. if (gret == FALSE) {
  118. g_socket_close (gsock, NULL);
  119. g_object_unref (gsock);
  120. return NULL;
  121. }
  122. gaddr = g_socket_get_local_address (gsock, NULL);
  123. if (gaddr == NULL ||
  124. !g_socket_address_to_native (gaddr, &name.addr, sizeof (name), NULL)) {
  125. g_socket_close (gsock, NULL);
  126. g_object_unref (gsock);
  127. return NULL;
  128. }
  129. g_object_unref (gaddr);
  130. if (ctx == NULL) {
  131. ctx = g_main_context_default ();
  132. }
  133. sock = g_slice_new0 (NiceSocket);
  134. nice_address_set_from_sockaddr (&sock->addr, &name.addr);
  135. sock->priv = priv = g_slice_new0 (TcpPassivePriv);
  136. priv->context = g_main_context_ref (ctx);
  137. priv->connections = g_hash_table_new_full ((GHashFunc) nice_address_hash,
  138. (GEqualFunc) nice_address_equal, (
  139. GDestroyNotify) nice_address_free, NULL);
  140. priv->writable_cb = NULL;
  141. priv->writable_data = NULL;
  142. sock->type = NICE_SOCKET_TYPE_TCP_PASSIVE;
  143. sock->fileno = gsock;
  144. sock->send_messages = socket_send_messages;
  145. sock->send_messages_reliable = socket_send_messages_reliable;
  146. sock->recv_messages = socket_recv_messages;
  147. sock->is_reliable = socket_is_reliable;
  148. sock->can_send = socket_can_send;
  149. sock->set_writable_callback = socket_set_writable_callback;
  150. sock->close = socket_close;
  151. return sock;
  152. }
  153. static void
  154. socket_close (NiceSocket *sock)
  155. {
  156. TcpPassivePriv *priv = sock->priv;
  157. if (sock->fileno != NULL) {
  158. g_socket_close (sock->fileno, NULL);
  159. g_object_unref (sock->fileno);
  160. sock->fileno = NULL;
  161. }
  162. if (priv->context)
  163. g_main_context_unref (priv->context);
  164. g_hash_table_unref (priv->connections);
  165. g_slice_free (TcpPassivePriv, sock->priv);
  166. }
  167. static gint socket_recv_messages (NiceSocket *sock,
  168. NiceInputMessage *recv_messages, guint n_recv_messages)
  169. {
  170. return -1;
  171. }
  172. static gint socket_send_messages (NiceSocket *sock, const NiceAddress *to,
  173. const NiceOutputMessage *messages, guint n_messages)
  174. {
  175. TcpPassivePriv *priv = sock->priv;
  176. if (to) {
  177. NiceSocket *peer_socket = g_hash_table_lookup (priv->connections, to);
  178. if (peer_socket)
  179. return nice_socket_send_messages (peer_socket, to, messages, n_messages);
  180. }
  181. return -1;
  182. }
  183. static gint socket_send_messages_reliable (NiceSocket *sock,
  184. const NiceAddress *to, const NiceOutputMessage *messages, guint n_messages)
  185. {
  186. TcpPassivePriv *priv = sock->priv;
  187. if (to) {
  188. NiceSocket *peer_socket = g_hash_table_lookup (priv->connections, to);
  189. if (peer_socket)
  190. return nice_socket_send_messages_reliable (peer_socket, to, messages,
  191. n_messages);
  192. }
  193. return -1;
  194. }
  195. static gboolean
  196. socket_is_reliable (NiceSocket *sock)
  197. {
  198. return TRUE;
  199. }
  200. static gboolean
  201. socket_can_send (NiceSocket *sock, NiceAddress *addr)
  202. {
  203. TcpPassivePriv *priv = sock->priv;
  204. NiceSocket *peer_socket = NULL;
  205. /* FIXME: Danger if child socket was closed */
  206. if (addr)
  207. peer_socket = g_hash_table_lookup (priv->connections, addr);
  208. if (peer_socket)
  209. return nice_socket_can_send (peer_socket, addr);
  210. return FALSE;
  211. }
  212. static void
  213. _child_writable_cb (NiceSocket *child, gpointer data)
  214. {
  215. NiceSocket *sock = data;
  216. TcpPassivePriv *priv = sock->priv;
  217. if (priv->writable_cb)
  218. priv->writable_cb (sock, priv->writable_data);
  219. }
  220. static void
  221. socket_set_writable_callback (NiceSocket *sock,
  222. NiceSocketWritableCb callback, gpointer user_data)
  223. {
  224. TcpPassivePriv *priv = sock->priv;
  225. priv->writable_cb = callback;
  226. priv->writable_data = user_data;
  227. }
  228. NiceSocket *
  229. nice_tcp_passive_socket_accept (NiceSocket *sock)
  230. {
  231. union {
  232. struct sockaddr_storage storage;
  233. struct sockaddr addr;
  234. } name;
  235. TcpPassivePriv *priv = sock->priv;
  236. GSocket *gsock = NULL;
  237. GSocketAddress *gaddr;
  238. NiceAddress remote_addr;
  239. NiceSocket *new_socket = NULL;
  240. gsock = g_socket_accept (sock->fileno, NULL, NULL);
  241. if (gsock == NULL) {
  242. return NULL;
  243. }
  244. /* GSocket: All socket file descriptors are set to be close-on-exec. */
  245. g_socket_set_blocking (gsock, false);
  246. /* setting TCP_NODELAY to TRUE in order to avoid packet batching */
  247. g_socket_set_option (gsock, IPPROTO_TCP, TCP_NODELAY, TRUE, NULL);
  248. gaddr = g_socket_get_remote_address (gsock, NULL);
  249. if (gaddr == NULL ||
  250. !g_socket_address_to_native (gaddr, &name.addr, sizeof (name), NULL)) {
  251. g_socket_close (gsock, NULL);
  252. g_object_unref (gsock);
  253. return NULL;
  254. }
  255. g_object_unref (gaddr);
  256. nice_address_set_from_sockaddr (&remote_addr, &name.addr);
  257. new_socket = nice_tcp_bsd_socket_new_from_gsock (priv->context, gsock,
  258. &sock->addr, &remote_addr, TRUE);
  259. g_object_unref (gsock);
  260. if (new_socket) {
  261. NiceAddress *key = nice_address_dup (&remote_addr);
  262. nice_tcp_bsd_socket_set_passive_parent (new_socket, sock);
  263. nice_socket_set_writable_callback (new_socket, _child_writable_cb, sock);
  264. g_hash_table_insert (priv->connections, key, new_socket);
  265. }
  266. return new_socket;
  267. }
  268. static guint nice_address_hash (const NiceAddress * key)
  269. {
  270. gchar ip[INET6_ADDRSTRLEN];
  271. gchar *str;
  272. guint hash;
  273. nice_address_to_string (key, ip);
  274. str = g_strdup_printf ("%s:%u", ip, nice_address_get_port (key));
  275. hash = g_str_hash (str);
  276. g_free (str);
  277. return hash;
  278. }
  279. void nice_tcp_passive_socket_remove_connection (NiceSocket *sock, const NiceAddress *to)
  280. {
  281. TcpPassivePriv *priv = sock->priv;
  282. g_hash_table_remove (priv->connections, to);
  283. }