2 * This file is part of the Nice GLib ICE library.
4 * (C) 2008-2009 Collabora Ltd.
5 * Contact: Youness Alaoui
6 * (C) 2008-2009 Nokia Corporation. All rights reserved.
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/
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
18 * The Original Code is the Nice GLib ICE library.
20 * The Initial Developers of the Original Code are Collabora Ltd and Nokia
21 * Corporation. All Rights Reserved.
24 * Youness Alaoui, Collabora Ltd.
26 * Alternatively, the contents of this file may be used under the terms of the
27 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
28 * case the provisions of LGPL are applicable instead of those above. If you
29 * wish to allow use of your version of this file only under the terms of the
30 * LGPL and not to allow others to use your version of this file under the
31 * MPL, indicate your decision by deleting the provisions above and replace
32 * them with the notice and other provisions required by the LGPL. If you do
33 * not delete the provisions above, a recipient may use your version of this
34 * file under either the MPL or the LGPL.
38 * Implementation of TCP relay socket interface using TCP Berkeley sockets. (See
39 * http://en.wikipedia.org/wiki/Berkeley_sockets.)
46 #include "agent-priv.h"
58 NiceAddress server_addr
;
60 GMainContext
*context
;
61 GIOChannel
*io_channel
;
71 #define MAX_QUEUE_LENGTH 20
73 static void socket_close (NiceSocket
*sock
);
74 static gint
socket_recv (NiceSocket
*sock
, NiceAddress
*from
,
75 guint len
, gchar
*buf
);
76 static gboolean
socket_send (NiceSocket
*sock
, const NiceAddress
*to
,
77 guint len
, const gchar
*buf
);
78 static gboolean
socket_is_reliable (NiceSocket
*sock
);
81 static void add_to_be_sent (NiceSocket
*sock
, const gchar
*buf
, guint len
,
83 static void free_to_be_sent (struct to_be_sent
*tbs
);
84 static gboolean
socket_send_more (GIOChannel
*source
, GIOCondition condition
,
88 nice_tcp_bsd_socket_new (NiceAgent
*agent
, GMainContext
*ctx
, NiceAddress
*addr
)
92 struct sockaddr_storage name
;
93 guint name_len
= sizeof (name
);
94 NiceSocket
*sock
= g_slice_new0 (NiceSocket
);
98 /* We can't connect a tcp socket with no destination address */
102 nice_address_copy_to_sockaddr(addr
, (struct sockaddr
*)&name
);
104 if ((sockfd
== -1) &&
105 ((name
.ss_family
== AF_UNSPEC
) ||
106 (name
.ss_family
== AF_INET
))) {
107 sockfd
= socket (PF_INET
, SOCK_STREAM
, 0);
108 name
.ss_family
= AF_INET
;
110 name
.ss_len
= sizeof (struct sockaddr_in
);
115 g_slice_free (NiceSocket
, sock
);
120 fcntl (sockfd
, F_SETFD
, fcntl (sockfd
, F_GETFD
) | FD_CLOEXEC
);
123 fcntl (sockfd
, F_SETFL
, fcntl (sockfd
, F_GETFL
) | O_NONBLOCK
);
126 name_len
= name
.ss_family
== AF_INET
? sizeof (struct sockaddr_in
) :
127 sizeof(struct sockaddr_in6
);
128 ret
= connect (sockfd
, (const struct sockaddr
*)&name
, name_len
);
131 if (ret
< 0 && WSAGetLastError () != WSAEINPROGRESS
) {
132 closesocket (sockfd
);
134 if (ret
< 0 && errno
!= EINPROGRESS
) {
137 g_slice_free (NiceSocket
, sock
);
141 name_len
= name
.ss_family
== AF_INET
? sizeof (struct sockaddr_in
) :
142 sizeof(struct sockaddr_in6
);
143 if (getsockname (sockfd
, (struct sockaddr
*) &name
, &name_len
) < 0) {
144 g_slice_free (NiceSocket
, sock
);
153 nice_address_set_from_sockaddr (&sock
->addr
, (struct sockaddr
*)&name
);
155 sock
->priv
= priv
= g_slice_new0 (TcpPriv
);
159 priv
->server_addr
= *addr
;
161 sock
->fileno
= sockfd
;
162 sock
->send
= socket_send
;
163 sock
->recv
= socket_recv
;
164 sock
->is_reliable
= socket_is_reliable
;
165 sock
->close
= socket_close
;
172 socket_close (NiceSocket
*sock
)
174 TcpPriv
*priv
= sock
->priv
;
177 closesocket(sock
->fileno
);
179 close (sock
->fileno
);
181 if (priv
->io_source
) {
182 g_source_destroy (priv
->io_source
);
183 g_source_unref (priv
->io_source
);
185 if (priv
->io_channel
)
186 g_io_channel_unref (priv
->io_channel
);
187 g_queue_foreach (&priv
->send_queue
, (GFunc
) free_to_be_sent
, NULL
);
188 g_queue_clear (&priv
->send_queue
);
190 g_slice_free(TcpPriv
, sock
->priv
);
194 socket_recv (NiceSocket
*sock
, NiceAddress
*from
, guint len
, gchar
*buf
)
196 TcpPriv
*priv
= sock
->priv
;
199 ret
= recv (sock
->fileno
, buf
, len
, 0);
201 /* recv returns 0 when the peer performed a shutdown.. we must return -1 here
202 * so that the agent destroys the g_source */
208 if (WSAGetLastError () == WSAEWOULDBLOCK
)
218 *from
= priv
->server_addr
;
222 /* Data sent to this function must be a single entity because buffers can be
223 * dropped if the bandwidth isn't fast enough. So do not send a message in
224 * multiple chunks. */
226 socket_send (NiceSocket
*sock
, const NiceAddress
*to
,
227 guint len
, const gchar
*buf
)
229 TcpPriv
*priv
= sock
->priv
;
232 /* First try to send the data, don't send it later if it can be sent now
233 this way we avoid allocating memory on every send */
234 if (g_queue_is_empty (&priv
->send_queue
)) {
235 ret
= send (sock
->fileno
, buf
, len
, 0);
239 if (WSAGetLastError () == WSAEWOULDBLOCK
) {
241 if (errno
== EAGAIN
) {
243 add_to_be_sent (sock
, buf
, len
, FALSE
);
248 } else if ((guint
)ret
< len
) {
249 add_to_be_sent (sock
, buf
+ ret
, len
- ret
, TRUE
);
253 if (g_queue_get_length(&priv
->send_queue
) >= MAX_QUEUE_LENGTH
) {
255 struct to_be_sent
*tbs
= NULL
;
256 while ((tbs
= g_queue_peek_nth (&priv
->send_queue
, peek_idx
)) != NULL
) {
258 tbs
= g_queue_pop_nth (&priv
->send_queue
, peek_idx
);
260 g_slice_free (struct to_be_sent
, tbs
);
267 add_to_be_sent (sock
, buf
, len
, FALSE
);
274 socket_is_reliable (NiceSocket
*sock
)
283 * 0 = have more to send
284 * 1 = sent everything
290 GIOCondition condition
,
293 NiceSocket
*sock
= (NiceSocket
*) data
;
294 TcpPriv
*priv
= sock
->priv
;
295 struct to_be_sent
*tbs
= NULL
;
299 if (g_source_is_destroyed (g_main_current_source ())) {
300 nice_debug ("Source was destroyed. "
301 "Avoided race condition in tcp-bsd.c:socket_send_more");
306 while ((tbs
= g_queue_pop_head (&priv
->send_queue
)) != NULL
) {
309 ret
= send (sock
->fileno
, tbs
->buf
, tbs
->length
, 0);
313 if (WSAGetLastError () == WSAEWOULDBLOCK
) {
315 if (errno
== EAGAIN
) {
317 add_to_be_sent (sock
, tbs
->buf
, tbs
->length
, TRUE
);
319 g_slice_free (struct to_be_sent
, tbs
);
322 } else if (ret
< (int) tbs
->length
) {
323 add_to_be_sent (sock
, tbs
->buf
+ ret
, tbs
->length
- ret
, TRUE
);
325 g_slice_free (struct to_be_sent
, tbs
);
330 g_slice_free (struct to_be_sent
, tbs
);
333 if (g_queue_is_empty (&priv
->send_queue
)) {
334 g_io_channel_unref (priv
->io_channel
);
335 priv
->io_channel
= NULL
;
336 g_source_destroy (priv
->io_source
);
337 g_source_unref (priv
->io_source
);
338 priv
->io_source
= NULL
;
350 add_to_be_sent (NiceSocket
*sock
, const gchar
*buf
, guint len
, gboolean head
)
352 TcpPriv
*priv
= sock
->priv
;
353 struct to_be_sent
*tbs
= NULL
;
358 tbs
= g_slice_new0 (struct to_be_sent
);
359 tbs
->buf
= g_memdup (buf
, len
);
361 tbs
->can_drop
= !head
;
363 g_queue_push_head (&priv
->send_queue
, tbs
);
365 g_queue_push_tail (&priv
->send_queue
, tbs
);
367 if (priv
->io_channel
== NULL
) {
368 priv
->io_channel
= g_io_channel_unix_new (sock
->fileno
);
369 priv
->io_source
= g_io_create_watch (priv
->io_channel
, G_IO_OUT
);
370 g_source_set_callback (priv
->io_source
, (GSourceFunc
) socket_send_more
,
372 g_source_attach (priv
->io_source
, priv
->context
);
379 free_to_be_sent (struct to_be_sent
*tbs
)
382 g_slice_free (struct to_be_sent
, tbs
);