Imported Upstream version 0.1.17
[platform/upstream/libnice.git] / tests / test-drop-invalid.c
1 /*
2  * This file is part of the Nice GLib ICE library.
3  *
4  * Unit test for ICE full-mode related features.
5  *
6  * (C) 2007 Nokia Corporation. All rights reserved.
7  *  Contact: Kai Vehmanen
8  * (C) 2017 Collabora Ltd
9  *  Contact: Olivier Crete <olivier.crete@collabora.com>
10  *
11  * The contents of this file are subject to the Mozilla Public License Version
12  * 1.1 (the "License"); you may not use this file except in compliance with
13  * the License. You may obtain a copy of the License at
14  * http://www.mozilla.org/MPL/
15  *
16  * Software distributed under the License is distributed on an "AS IS" basis,
17  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
18  * for the specific language governing rights and limitations under the
19  * License.
20  *
21  * The Original Code is the Nice GLib ICE library.
22  *
23  * The Initial Developers of the Original Code are Collabora Ltd and Nokia
24  * Corporation. All Rights Reserved.
25  *
26  * Contributors:
27  *   Kai Vehmanen, Nokia
28  *
29  * Alternatively, the contents of this file may be used under the terms of the
30  * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
31  * case the provisions of LGPL are applicable instead of those above. If you
32  * wish to allow use of your version of this file only under the terms of the
33  * LGPL and not to allow others to use your version of this file under the
34  * MPL, indicate your decision by deleting the provisions above and replace
35  * them with the notice and other provisions required by the LGPL. If you do
36  * not delete the provisions above, a recipient may use your version of this
37  * file under either the MPL or the LGPL.
38  */
39 #ifdef HAVE_CONFIG_H
40 # include <config.h>
41 #endif
42
43 #include "agent.h"
44
45 #include "socket/socket.h"
46
47 #include <stdlib.h>
48 #include <string.h>
49
50
51
52 static NiceComponentState global_lagent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST };
53 static NiceComponentState global_ragent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST };
54 static guint global_components_ready = 0;
55 static guint global_components_ready_exit = 0;
56 static guint global_components_failed = 0;
57 static guint global_components_failed_exit = 0;
58 static GMainLoop *global_mainloop = NULL;
59 static gboolean global_lagent_gathering_done = FALSE;
60 static gboolean global_ragent_gathering_done = FALSE;
61 static gboolean global_lagent_ibr_received = FALSE;
62 static gboolean global_ragent_ibr_received = FALSE;
63 static int global_lagent_cands = 0;
64 static int global_ragent_cands = 0;
65 static gint global_ragent_read = 0;
66 static guint global_exit_when_ibr_received = 0;
67
68 static void priv_print_global_status (void)
69 {
70   g_debug ("\tgathering_done=%d", global_lagent_gathering_done && global_ragent_gathering_done);
71   g_debug ("\tlstate[rtp]=%d [rtcp]=%d", global_lagent_state[0], global_lagent_state[1]);
72   g_debug ("\trstate[rtp]=%d [rtcp]=%d", global_ragent_state[0], global_ragent_state[1]);
73   g_debug ("\tL cands=%d R cands=%d", global_lagent_cands, global_ragent_cands);
74 }
75
76 static gboolean timer_cb (gpointer pointer)
77 {
78   g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, pointer);
79
80   /* signal status via a global variable */
81
82   /* note: should not be reached, abort */
83   g_error ("ERROR: test has got stuck, aborting...");
84
85   return FALSE;
86 }
87
88 static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data)
89 {
90   g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, user_data);
91
92   /* XXX: dear compiler, these are for you: */
93   (void)agent; (void)stream_id; (void)component_id; (void)buf;
94
95   /* Core of the test
96    * Assert on any unreleated packet received. This would include anything
97    * send before the negotiation is over.
98    */
99   g_assert_cmpuint (len, ==, 16);
100   g_assert (strncmp ("1234567812345678", buf, 16) == 0);
101
102   if (component_id == 2)
103     return;
104
105   if (GPOINTER_TO_UINT (user_data) == 2) {
106     g_debug ("right agent received %d bytes, stopping mainloop", len);
107     global_ragent_read = len;
108     g_main_loop_quit (global_mainloop);
109   }
110 }
111
112 static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data)
113 {
114   g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data);
115
116   if (GPOINTER_TO_UINT (data) == 1)
117     global_lagent_gathering_done = TRUE;
118   else if (GPOINTER_TO_UINT (data) == 2)
119     global_ragent_gathering_done = TRUE;
120
121   if (global_lagent_gathering_done &&
122       global_ragent_gathering_done)
123     g_main_loop_quit (global_mainloop);
124
125   /* XXX: dear compiler, these are for you: */
126   (void)agent;
127 }
128
129 static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data)
130 {
131   gboolean ready_to_connected = FALSE;
132   g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data);
133
134   if (GPOINTER_TO_UINT (data) == 1) {
135     if (global_lagent_state[component_id - 1] == NICE_COMPONENT_STATE_READY &&
136         state == NICE_COMPONENT_STATE_CONNECTED)
137       ready_to_connected = TRUE;
138     global_lagent_state[component_id - 1] = state;
139   } else if (GPOINTER_TO_UINT (data) == 2) {
140     if (global_ragent_state[component_id - 1] == NICE_COMPONENT_STATE_READY &&
141         state == NICE_COMPONENT_STATE_CONNECTED)
142       ready_to_connected = TRUE;
143     global_ragent_state[component_id - 1] = state;
144   }
145
146   if (state == NICE_COMPONENT_STATE_READY)
147     global_components_ready++;
148   else if (state == NICE_COMPONENT_STATE_CONNECTED && ready_to_connected)
149     global_components_ready--;
150   if (state == NICE_COMPONENT_STATE_FAILED)
151     global_components_failed++;
152
153   g_debug ("test-drop-invalid: checks READY/EXIT-AT %u/%u.", global_components_ready, global_components_ready_exit);
154   g_debug ("test-drop-invalid: checks FAILED/EXIT-AT %u/%u.", global_components_failed, global_components_failed_exit);
155
156   /* signal status via a global variable */
157   if (global_components_ready == global_components_ready_exit &&
158       global_components_failed == global_components_failed_exit) {
159     g_debug ("Components ready/failed achieved. Stopping mailoop");
160     g_main_loop_quit (global_mainloop); 
161     return;
162   }
163
164   /* XXX: dear compiler, these are for you: */
165   (void)agent; (void)stream_id; (void)data; (void)component_id;
166 }
167
168 static void cb_new_selected_pair(NiceAgent *agent, guint stream_id, guint component_id, 
169                                  gchar *lfoundation, gchar* rfoundation, gpointer data)
170 {
171   g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data);
172
173   if (GPOINTER_TO_UINT (data) == 1)
174     ++global_lagent_cands;
175   else if (GPOINTER_TO_UINT (data) == 2)
176     ++global_ragent_cands;
177
178   /* XXX: dear compiler, these are for you: */
179   (void)agent; (void)stream_id; (void)component_id; (void)lfoundation; (void)rfoundation;
180 }
181
182 static void cb_new_candidate(NiceAgent *agent, guint stream_id, guint component_id, 
183                              gchar *foundation, gpointer data)
184 {
185   g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data);
186
187   /* XXX: dear compiler, these are for you: */
188   (void)agent; (void)stream_id; (void)data; (void)component_id; (void)foundation;
189 }
190
191 static void cb_initial_binding_request_received(NiceAgent *agent, guint stream_id, gpointer data)
192 {
193   g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data);
194
195   if (GPOINTER_TO_UINT (data) == 1)
196     global_lagent_ibr_received = TRUE;
197   else if (GPOINTER_TO_UINT (data) == 2)
198     global_ragent_ibr_received = TRUE;
199
200   if (global_exit_when_ibr_received) {
201     g_debug ("Received initial binding request. Stopping mailoop");
202     g_main_loop_quit (global_mainloop);
203   }
204
205   /* XXX: dear compiler, these are for you: */
206   (void)agent; (void)stream_id; (void)data;
207 }
208
209 static void set_candidates (NiceAgent *from, guint from_stream,
210     NiceAgent *to, guint to_stream, guint component)
211 {
212   GSList *cands = NULL;
213   GSList *peer_cands = NULL;
214   GSList *item1, *item2;
215
216   cands = nice_agent_get_local_candidates (from, from_stream, component);
217   peer_cands = nice_agent_get_local_candidates (to, to_stream, component);
218
219   /*
220    * Core of the test:
221    *
222    * Send packets that shoudl be dropped.
223    */
224
225   for (item1 = cands; item1; item1 = item1->next) {
226     NiceCandidate *cand = item1->data;
227     NiceSocket *nicesock = cand->sockptr;
228
229     g_assert (nicesock);
230
231     for (item2 = peer_cands; item2; item2 = item2->next) {
232       NiceCandidate *target_cand = item2->data;
233
234       nice_socket_send (nicesock, &target_cand->addr, 12, "123456789AB");
235     }
236
237   }
238
239   nice_agent_set_remote_candidates (to, to_stream, component, cands);
240
241   g_slist_free_full (cands, (GDestroyNotify) nice_candidate_free);
242   g_slist_free_full (peer_cands, (GDestroyNotify) nice_candidate_free);
243 }
244
245 static void set_credentials (NiceAgent *lagent, guint lstream,
246     NiceAgent *ragent, guint rstream)
247 {
248   gchar *ufrag = NULL, *password = NULL;
249
250   nice_agent_get_local_credentials(lagent, lstream, &ufrag, &password);
251   nice_agent_set_remote_credentials (ragent, rstream, ufrag, password);
252   g_free (ufrag);
253   g_free (password);
254   nice_agent_get_local_credentials(ragent, rstream, &ufrag, &password);
255   nice_agent_set_remote_credentials (lagent, lstream, ufrag, password);
256   g_free (ufrag);
257   g_free (password);
258 }
259
260 static guint16
261 get_port (NiceAgent *agent, guint stream_id, guint component_id)
262 {
263   GSList *cands = nice_agent_get_local_candidates (agent, stream_id,
264       component_id);
265   GSList *item;
266   guint16 port = 0;
267
268   g_assert (cands != NULL);
269
270   for (item = cands; item; item = item->next) {
271     NiceCandidate *cand = item->data;
272     port = nice_address_get_port (&cand->addr);
273     break;
274   }
275   g_assert (port != 0);
276
277   g_slist_free_full (cands, (GDestroyNotify) nice_candidate_free);
278
279   return port;
280 }
281
282 static int run_full_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress *baseaddr, guint ready, guint failed)
283 {
284   guint ls_id, rs_id;
285   gint ret;
286   guint16 port;
287
288   /* XXX: dear compiler, this is for you */
289   (void)baseaddr;
290
291   /* step: initialize variables modified by the callbacks */
292   global_components_ready = 0;
293   global_components_ready_exit = ready;
294   global_components_failed = 0;
295   global_components_failed_exit = failed;
296   global_lagent_gathering_done = FALSE;
297   global_ragent_gathering_done = FALSE;
298   global_lagent_ibr_received =
299     global_ragent_ibr_received = FALSE;
300   global_lagent_cands =
301     global_ragent_cands = 0;
302
303   g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL);
304   g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL);
305
306   /* step: add one stream, with RTP+RTCP components, to each agent */
307   ls_id = nice_agent_add_stream (lagent, 2);
308
309   rs_id = nice_agent_add_stream (ragent, 2);
310   g_assert_cmpuint (ls_id, >, 0);
311   g_assert_cmpuint (rs_id, >, 0);
312
313   /* Gather candidates and test nice_agent_set_port_range */
314   nice_agent_set_port_range (lagent, ls_id, 1, 10000, 11000);
315   nice_agent_set_port_range (lagent, ls_id, 2, 11000, 12000);
316   g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE);
317
318   port = get_port (lagent, ls_id, 1);
319   nice_agent_set_port_range (ragent, rs_id, 1, 12000, 13000);
320   nice_agent_set_port_range (ragent, rs_id, 2, port, port);
321   g_assert (nice_agent_gather_candidates (ragent, rs_id) == FALSE);
322   g_assert (nice_agent_get_local_candidates (ragent, rs_id, 1) == NULL);
323   g_assert (nice_agent_get_local_candidates (ragent, rs_id, 2) == NULL);
324   nice_agent_set_port_range (ragent, rs_id, 2, 13000, 14000);
325   g_assert (nice_agent_gather_candidates (ragent, rs_id) == TRUE);
326
327   {
328     GSList *cands = NULL, *i;
329     NiceCandidate *cand = NULL;
330
331     cands = nice_agent_get_local_candidates (lagent, ls_id, 1);
332     g_assert_cmpuint (g_slist_length (cands), ==, 1);
333     cand = cands->data;
334     g_assert_cmpint (cand->type, ==, NICE_CANDIDATE_TYPE_HOST);
335     g_assert_cmpuint (nice_address_get_port (&cand->addr), >=, 10000);
336     g_assert_cmpuint (nice_address_get_port (&cand->addr), <=, 11000);
337     for (i = cands; i; i = i->next)
338       nice_candidate_free ((NiceCandidate *) i->data);
339     g_slist_free (cands);
340
341     cands = nice_agent_get_local_candidates (lagent, ls_id, 2);
342     g_assert_cmpuint (g_slist_length (cands), ==, 1);
343     cand = cands->data;
344     g_assert_cmpuint (cand->type, ==, NICE_CANDIDATE_TYPE_HOST);
345     g_assert_cmpuint (nice_address_get_port (&cand->addr), >=, 11000);
346     g_assert_cmpuint (nice_address_get_port (&cand->addr), <=, 12000);
347     for (i = cands; i; i = i->next)
348       nice_candidate_free ((NiceCandidate *) i->data);
349     g_slist_free (cands);
350
351     cands = nice_agent_get_local_candidates (ragent, rs_id, 1);
352     g_assert_cmpuint (g_slist_length (cands), ==, 1);
353     cand = cands->data;
354     g_assert_cmpint (cand->type, ==, NICE_CANDIDATE_TYPE_HOST);
355     g_assert_cmpuint (nice_address_get_port (&cand->addr), >=, 12000);
356     g_assert_cmpuint (nice_address_get_port (&cand->addr), <=, 13000);
357     for (i = cands; i; i = i->next)
358       nice_candidate_free ((NiceCandidate *) i->data);
359     g_slist_free (cands);
360
361     cands = nice_agent_get_local_candidates (ragent, rs_id, 2);
362     g_assert_cmpuint (g_slist_length (cands), ==, 1);
363     cand = cands->data;
364     g_assert_cmpint (cand->type, ==, NICE_CANDIDATE_TYPE_HOST);
365     g_assert_cmpuint (nice_address_get_port (&cand->addr), >=, 13000);
366     g_assert_cmpuint (nice_address_get_port (&cand->addr), <=, 14000);
367     for (i = cands; i; i = i->next)
368       nice_candidate_free ((NiceCandidate *) i->data);
369     g_slist_free (cands);
370
371   }
372
373   /* step: attach to mainloop (needed to register the fds) */
374   nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTP,
375       g_main_loop_get_context (global_mainloop), cb_nice_recv,
376       GUINT_TO_POINTER (1));
377   nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTCP,
378       g_main_loop_get_context (global_mainloop), cb_nice_recv,
379       GUINT_TO_POINTER (1));
380   nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTP,
381       g_main_loop_get_context (global_mainloop), cb_nice_recv,
382       GUINT_TO_POINTER (2));
383   nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTCP,
384       g_main_loop_get_context (global_mainloop), cb_nice_recv,
385       GUINT_TO_POINTER (2));
386
387   /* step: run mainloop until local candidates are ready
388    *       (see timer_cb() above) */
389   if (global_lagent_gathering_done != TRUE ||
390       global_ragent_gathering_done != TRUE) {
391     g_debug ("test-drop-invalid: Added streams, running mainloop until 'candidate-gathering-done'...");
392     g_main_loop_run (global_mainloop);
393     g_assert (global_lagent_gathering_done == TRUE);
394     g_assert (global_ragent_gathering_done == TRUE);
395   }
396
397   set_credentials (lagent, ls_id, ragent, rs_id);
398
399   /* step: pass the remote candidates to agents  */
400   set_candidates (ragent, rs_id, lagent, ls_id, NICE_COMPONENT_TYPE_RTP);
401   set_candidates (ragent, rs_id, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP);
402   set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTP);
403   set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTCP);
404
405   g_debug ("test-drop-invalid: Set properties, next running mainloop until connectivity checks succeed...");
406
407   /* step: run the mainloop until connectivity checks succeed
408    *       (see timer_cb() above) */
409   g_main_loop_run (global_mainloop);
410
411   /* note: verify that STUN binding requests were sent */
412   g_assert (global_lagent_ibr_received == TRUE);
413   g_assert (global_ragent_ibr_received == TRUE);
414
415   /* note: Send a packet from another address */
416   /* These should also be ignored */
417   {
418     NiceCandidate *local_cand = NULL;
419     NiceCandidate *remote_cand = NULL;
420     NiceSocket *tmpsock;
421
422     g_assert (nice_agent_get_selected_pair (lagent, ls_id, 1, &local_cand,
423             &remote_cand));
424     g_assert (local_cand);
425     g_assert (remote_cand);
426
427     tmpsock = nice_udp_bsd_socket_new (NULL);
428     nice_socket_send (tmpsock, &remote_cand->addr, 4, "ABCD");
429     nice_socket_send (tmpsock, &local_cand->addr, 5, "ABCDE");
430     nice_socket_free (tmpsock);
431   }
432
433   /* note: test payload send and receive */
434   global_ragent_read = 0;
435   ret = nice_agent_send (lagent, ls_id, 1, 16, "1234567812345678");
436   g_assert (ret != -1);
437   g_debug ("Sent %d bytes", ret);
438   g_assert_cmpint (ret, ==, 16);
439   while (global_ragent_read != 16)
440     g_main_context_iteration (NULL, TRUE);
441   g_assert_cmpint (global_ragent_read, ==, 16);
442
443   g_debug ("test-drop-invalid: Ran mainloop, removing streams...");
444
445   /* step: clean up resources and exit */
446
447   nice_agent_remove_stream (lagent, ls_id);
448   nice_agent_remove_stream (ragent, rs_id);
449
450   return 0;
451 }
452
453 int main (void)
454 {
455   NiceAgent *lagent, *ragent;      /* agent's L and R */
456   NiceAddress baseaddr;
457   int result;
458   guint timer_id;
459
460 #ifdef G_OS_WIN32
461   WSADATA w;
462
463   WSAStartup(0x0202, &w);
464 #endif
465
466   global_mainloop = g_main_loop_new (NULL, FALSE);
467
468   /* step: create the agents L and R */
469   lagent = nice_agent_new (g_main_loop_get_context (global_mainloop),
470       NICE_COMPATIBILITY_RFC5245);
471   ragent = nice_agent_new (g_main_loop_get_context (global_mainloop),
472       NICE_COMPATIBILITY_RFC5245);
473
474   g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE,  NULL);
475   g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE,  NULL);
476
477   g_object_set (G_OBJECT (lagent), "upnp", FALSE, NULL);
478   g_object_set (G_OBJECT (ragent), "upnp", FALSE, NULL);
479
480   nice_agent_set_software (lagent, "test-drop-invalid, Left Agent");
481   nice_agent_set_software (ragent, "test-drop-invalid, Right Agent");
482
483   /* step: add a timer to catch state changes triggered by signals */
484   timer_id = g_timeout_add (30000, timer_cb, NULL);
485
486   /* step: specify which local interface to use */
487   if (!nice_address_set_from_string (&baseaddr, "127.0.0.1"))
488     g_assert_not_reached ();
489   nice_agent_add_local_address (lagent, &baseaddr);
490   nice_agent_add_local_address (ragent, &baseaddr);
491
492   g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done",
493       G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER(1));
494   g_signal_connect (G_OBJECT (ragent), "candidate-gathering-done",
495       G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER (2));
496   g_signal_connect (G_OBJECT (lagent), "component-state-changed",
497       G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (1));
498   g_signal_connect (G_OBJECT (ragent), "component-state-changed",
499       G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (2));
500   g_signal_connect (G_OBJECT (lagent), "new-selected-pair",
501       G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER(1));
502   g_signal_connect (G_OBJECT (ragent), "new-selected-pair",
503       G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER (2));
504   g_signal_connect (G_OBJECT (lagent), "new-candidate",
505       G_CALLBACK (cb_new_candidate), GUINT_TO_POINTER (1));
506   g_signal_connect (G_OBJECT (ragent), "new-candidate",
507       G_CALLBACK (cb_new_candidate), GUINT_TO_POINTER (2));
508   g_signal_connect (G_OBJECT (lagent), "initial-binding-request-received",
509       G_CALLBACK (cb_initial_binding_request_received),
510       GUINT_TO_POINTER (1));
511   g_signal_connect (G_OBJECT (ragent), "initial-binding-request-received",
512       G_CALLBACK (cb_initial_binding_request_received),
513       GUINT_TO_POINTER (2));
514
515   g_object_set (G_OBJECT (lagent), "upnp", FALSE,  NULL);
516   g_object_set (G_OBJECT (ragent), "upnp", FALSE,  NULL);
517
518
519   /* step: run test the first time */
520   g_debug ("test-drop-invalid: TEST STARTS / running test for the 1st time");
521   result = run_full_test (lagent, ragent, &baseaddr, 4 ,0);
522   priv_print_global_status ();
523   g_assert_cmpint (result, ==, 0);
524   g_assert_cmpint (global_lagent_state[0], ==, NICE_COMPONENT_STATE_READY);
525   g_assert_cmpint (global_lagent_state[1], ==, NICE_COMPONENT_STATE_READY);
526   g_assert_cmpint (global_ragent_state[0], ==, NICE_COMPONENT_STATE_READY);
527   g_assert_cmpint (global_ragent_state[1], ==, NICE_COMPONENT_STATE_READY);
528   /* When using TURN, we get peer reflexive candidates for the host cands
529      that we removed so we can get another new_selected_pair signal later
530      depending on timing/racing, we could double (or not) the amount we expected
531   */
532
533   /* note: verify that correct number of local candidates were reported */
534   g_assert_cmpint (global_lagent_cands, ==, 2);
535   g_assert_cmpint (global_ragent_cands, ==, 2);
536
537   g_object_unref (lagent);
538   g_object_unref (ragent);
539
540   g_main_loop_unref (global_mainloop);
541   global_mainloop = NULL;
542
543   g_source_remove (timer_id);
544 #ifdef G_OS_WIN32
545   WSACleanup();
546 #endif
547   return result;
548 }