Merging gst-devtools
[platform/upstream/gstreamer.git] / validate / plugins / fault_injection / socket_interposer.c
1 /* GStreamer
2  *
3  * Copyright (C) 2014 YouView TV Ltd
4  *  Authors: Mariusz Buras <mariusz.buras@youview.com>
5  *           Mathieu Duponchelle <mathieu.duponchelle@collabora.com>
6  *
7  * socket_interposer.c : overrides for standard socket functions
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 #define _GNU_SOURCE
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include <gst/gst.h>
32 #include "../../gst/validate/gst-validate-scenario.h"
33
34 #if defined(__gnu_linux__) && !defined(__ANDROID__) && !defined (ANDROID)
35
36 #include <sys/socket.h>
37 #include <netinet/ip.h>
38 #include <dlfcn.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <sys/syscall.h>
42 #include <unistd.h>
43 #include <fcntl.h>
44 #include <stdio.h>
45 #include <stdarg.h>
46 #include <string.h>
47 #include <pthread.h>
48 #include <errno.h>
49
50 #define MAX_CALLBACKS (16)
51
52 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
53 /* Return 0 to remove the callback immediately */
54 typedef int (*socket_interposer_callback) (void *, const void *, size_t);
55
56 struct
57 {
58   socket_interposer_callback callback;
59   void *userdata;
60   struct sockaddr_in sockaddr;
61   int fd;
62 } callbacks[MAX_CALLBACKS];
63
64 static int
65 socket_interposer_remove_callback_unlocked (struct sockaddr_in *addrin,
66     socket_interposer_callback callback, void *userdata)
67 {
68   size_t i;
69   for (i = 0; i < MAX_CALLBACKS; i++) {
70     if (callbacks[i].callback == callback
71         && callbacks[i].userdata == userdata
72         && callbacks[i].sockaddr.sin_addr.s_addr == addrin->sin_addr.s_addr
73         && callbacks[i].sockaddr.sin_port == addrin->sin_port) {
74       memset (&callbacks[i], 0, sizeof (callbacks[0]));
75       return 1;
76     }
77   }
78   return 0;
79 }
80
81 static void
82 socket_interposer_set_callback (struct sockaddr_in *addrin,
83     socket_interposer_callback callback, void *userdata)
84 {
85   size_t i;
86   pthread_mutex_lock (&mutex);
87
88
89   socket_interposer_remove_callback_unlocked (addrin, callback, userdata);
90   for (i = 0; i < MAX_CALLBACKS; i++) {
91     if (callbacks[i].callback == NULL) {
92       callbacks[i].callback = callback;
93       callbacks[i].userdata = userdata;
94       memcpy (&callbacks[i].sockaddr, addrin, sizeof (struct sockaddr_in));
95       callbacks[i].fd = -1;
96       break;
97     }
98   }
99   pthread_mutex_unlock (&mutex);
100 }
101
102 int
103 connect (int socket, const struct sockaddr_in *addrin, socklen_t address_len)
104 {
105   size_t i;
106   int override_errno = 0;
107   typedef ssize_t (*real_connect_fn) (int, const struct sockaddr_in *,
108       socklen_t);
109   static real_connect_fn real_connect = 0;
110   ssize_t ret = 0;
111
112   pthread_mutex_lock (&mutex);
113
114   for (i = 0; i < MAX_CALLBACKS; i++) {
115     if (callbacks[i].sockaddr.sin_addr.s_addr == addrin->sin_addr.s_addr
116         && callbacks[i].sockaddr.sin_port == addrin->sin_port) {
117
118       callbacks[i].fd = socket;
119
120       if (callbacks[i].callback) {
121         int ret = callbacks[i].callback (callbacks[i].userdata, NULL,
122             0);
123         if (ret != 0)
124           override_errno = ret;
125         else                    /* Remove the callback */
126           memset (&callbacks[i], 0, sizeof (callbacks[0]));
127       }
128
129       break;
130     }
131   }
132
133   pthread_mutex_unlock (&mutex);
134
135   if (!real_connect) {
136     real_connect = (real_connect_fn) dlsym (RTLD_NEXT, "connect");
137   }
138
139   if (!override_errno) {
140     ret = real_connect (socket, addrin, address_len);
141   } else {
142     // override errno
143     errno = override_errno;
144     ret = -1;
145   }
146   return ret;
147 }
148
149 ssize_t
150 send (int socket, const void *buffer, size_t len, int flags)
151 {
152   size_t i;
153   int override_errno = 0;
154   typedef ssize_t (*real_send_fn) (int, const void *, size_t, int);
155   ssize_t ret;
156   static real_send_fn real_send = 0;
157
158   pthread_mutex_lock (&mutex);
159   for (i = 0; i < MAX_CALLBACKS; i++) {
160     if (callbacks[i].fd != 0 && callbacks[i].fd == socket) {
161       int ret = callbacks[i].callback (callbacks[i].userdata, buffer,
162           len);
163
164       if (ret != 0)
165         override_errno = ret;
166       else                      /* Remove the callback */
167         memset (&callbacks[i], 0, sizeof (callbacks[0]));
168
169       break;
170     }
171   }
172   pthread_mutex_unlock (&mutex);
173
174   if (!real_send) {
175     real_send = (real_send_fn) dlsym (RTLD_NEXT, "send");
176   }
177
178   ret = real_send (socket, buffer, len, flags);
179
180   // override errno
181   if (override_errno != 0) {
182     errno = override_errno;
183     ret = -1;
184   }
185
186   return ret;
187
188 }
189
190 ssize_t
191 recv (int socket, void *buffer, size_t length, int flags)
192 {
193   size_t i;
194   int old_errno;
195   typedef ssize_t (*real_recv_fn) (int, void *, size_t, int);
196   ssize_t ret;
197   static real_recv_fn real_recv = 0;
198
199   if (!real_recv) {
200     real_recv = (real_recv_fn) dlsym (RTLD_NEXT, "recv");
201   }
202
203   ret = real_recv (socket, buffer, length, flags);
204   old_errno = errno;
205
206   pthread_mutex_lock (&mutex);
207   for (i = 0; i < MAX_CALLBACKS; i++) {
208     if (callbacks[i].fd != 0 && callbacks[i].fd == socket) {
209       int newerrno = callbacks[i].callback (callbacks[i].userdata, buffer,
210           ret);
211
212       // override errno
213       if (newerrno != 0) {
214         old_errno = newerrno;
215         ret = -1;
216       } else {                  /* Remove the callback */
217         memset (&callbacks[i], 0, sizeof (callbacks[0]));
218       }
219
220       break;
221     }
222   }
223   pthread_mutex_unlock (&mutex);
224
225   errno = old_errno;
226
227   return ret;
228 }
229
230 struct errno_entry
231 {
232   const gchar *str;
233   int _errno;
234 };
235
236 static struct errno_entry errno_map[] = {
237   {"ECONNABORTED", ECONNABORTED},
238   {"ECONNRESET", ECONNRESET},
239   {"ENETRESET", ENETRESET},
240   {"ECONNREFUSED", ECONNREFUSED},
241   {"EHOSTUNREACH", EHOSTUNREACH},
242   {"EHOSTDOWN", EHOSTDOWN},
243   {NULL, 0},
244 };
245
246 static int
247 socket_callback_ (GstValidateAction * action, const void *buff, size_t len)
248 {
249   gint times;
250   gint real_errno;
251
252   gst_structure_get_int (action->structure, "times", &times);
253   gst_structure_get_int (action->structure, "real_errno", &real_errno);
254
255   times -= 1;
256   gst_structure_set (action->structure, "times", G_TYPE_INT, times, NULL);
257   if (times <= 0) {
258     gst_validate_action_set_done (action);
259     return 0;
260   }
261
262   return real_errno;
263 }
264
265 static gint
266 errno_string_to_int (const gchar * errno_str)
267 {
268   gint i;
269
270   for (i = 0; errno_map[i]._errno; i += 1) {
271     if (!g_ascii_strcasecmp (errno_map[i].str, errno_str))
272       return errno_map[i]._errno;
273   }
274
275   return 0;
276 }
277
278 static gboolean
279 _fault_injector_loaded (void)
280 {
281   const gchar *ld_preload = g_getenv ("LD_PRELOAD");
282
283
284   return (ld_preload && strstr (ld_preload, "libfaultinjection-1.0.so"));
285 }
286
287 static gboolean
288 _execute_corrupt_socket_recv (GstValidateScenario * scenario,
289     GstValidateAction * action)
290 {
291   struct sockaddr_in addr =
292       { AF_INET, htons (42), {htonl (INADDR_LOOPBACK)}, {0} };
293   gint server_port, times;
294   const gchar *errno_str;
295   gint real_errno;
296
297   if (!_fault_injector_loaded ()) {
298     GST_ERROR
299         ("The fault injector wasn't preloaded, can't execute socket recv corruption\n"
300         "You should set LD_PRELOAD to the path of libfaultinjection.so");
301     return FALSE;
302   }
303
304   if (!gst_structure_get_int (action->structure, "port", &server_port)) {
305     GST_ERROR ("could not get port to corrupt recv on.");
306     return FALSE;
307   }
308
309   if (!gst_structure_get_int (action->structure, "times", &times)) {
310     gst_structure_set (action->structure, "times", G_TYPE_INT, 1, NULL);
311   }
312
313   errno_str = gst_structure_get_string (action->structure, "errno");
314   if (!errno_str) {
315     GST_ERROR ("Could not get errno string");
316     return FALSE;
317   }
318
319   real_errno = errno_string_to_int (errno_str);
320
321   if (real_errno == 0) {
322     GST_ERROR ("unrecognized errno");
323     return FALSE;
324   }
325
326   gst_structure_set (action->structure, "real_errno", G_TYPE_INT, real_errno,
327       NULL);
328
329   addr.sin_port = htons (server_port);
330
331   socket_interposer_set_callback (&addr,
332       (socket_interposer_callback) socket_callback_, action);
333
334   return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
335 }
336
337 static gboolean
338 socket_interposer_init (GstPlugin * plugin)
339 {
340 /*  *INDENT-OFF* */
341   gst_validate_register_action_type_dynamic (plugin, "corrupt-socket-recv",
342       GST_RANK_PRIMARY,
343       _execute_corrupt_socket_recv, ((GstValidateActionParameter[]) {
344             {
345               .name = "port",
346               .description = "The port the socket to be corrupted listens on",
347               .mandatory = TRUE,
348               .types = "int",
349               .possible_variables = NULL,
350             },
351             {
352               .name = "errno",
353               .description = "errno to set when failing",
354               .mandatory = TRUE,
355               .types = "string",
356             },
357             {
358               .name = "times",
359               .description = "Number of times to corrupt recv, default is one",
360               .mandatory = FALSE,
361               .types = "int",
362               .possible_variables = NULL,
363               .def = "1",
364             },
365             {NULL}
366           }),
367       "corrupt the next socket receive", GST_VALIDATE_ACTION_TYPE_ASYNC);
368 /*  *INDENT-ON* */
369
370   return TRUE;
371 }
372
373 #else /* No LD_PRELOAD tricks on Windows */
374
375 static gboolean
376 socket_interposer_init (GstPlugin * plugin)
377 {
378   return TRUE;
379 }
380
381 #endif
382
383 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
384     GST_VERSION_MINOR,
385     validatefaultinjection,
386     "Fault injector plugin for GstValidate",
387     socket_interposer_init, VERSION, "LGPL", GST_PACKAGE_NAME,
388     GST_PACKAGE_ORIGIN)