3 * Copyright (C) 2014 YouView TV Ltd
4 * Authors: Mariusz Buras <mariusz.buras@youview.com>
5 * Mathieu Duponchelle <mathieu.duponchelle@collabora.com>
7 * socket_interposer.c : overrides for standard socket functions
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.
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.
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.
32 #include "../../gst/validate/gst-validate-scenario.h"
34 #if defined(__gnu_linux__) && !defined(__ANDROID__) && !defined (ANDROID)
36 #include <sys/socket.h>
37 #include <netinet/ip.h>
39 #include <sys/types.h>
41 #include <sys/syscall.h>
50 #define MAX_CALLBACKS (16)
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);
58 socket_interposer_callback callback;
60 struct sockaddr_in sockaddr;
62 } callbacks[MAX_CALLBACKS];
65 socket_interposer_remove_callback_unlocked (struct sockaddr_in *addrin,
66 socket_interposer_callback callback, void *userdata)
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]));
82 socket_interposer_set_callback (struct sockaddr_in *addrin,
83 socket_interposer_callback callback, void *userdata)
86 pthread_mutex_lock (&mutex);
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));
99 pthread_mutex_unlock (&mutex);
103 connect (int socket, const struct sockaddr_in *addrin, socklen_t address_len)
106 int override_errno = 0;
107 typedef ssize_t (*real_connect_fn) (int, const struct sockaddr_in *,
109 static real_connect_fn real_connect = 0;
112 pthread_mutex_lock (&mutex);
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) {
118 callbacks[i].fd = socket;
120 if (callbacks[i].callback) {
121 int ret = callbacks[i].callback (callbacks[i].userdata, NULL,
124 override_errno = ret;
125 else /* Remove the callback */
126 memset (&callbacks[i], 0, sizeof (callbacks[0]));
133 pthread_mutex_unlock (&mutex);
136 real_connect = (real_connect_fn) dlsym (RTLD_NEXT, "connect");
139 if (!override_errno) {
140 ret = real_connect (socket, addrin, address_len);
143 errno = override_errno;
150 send (int socket, const void *buffer, size_t len, int flags)
153 int override_errno = 0;
154 typedef ssize_t (*real_send_fn) (int, const void *, size_t, int);
156 static real_send_fn real_send = 0;
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,
165 override_errno = ret;
166 else /* Remove the callback */
167 memset (&callbacks[i], 0, sizeof (callbacks[0]));
172 pthread_mutex_unlock (&mutex);
175 real_send = (real_send_fn) dlsym (RTLD_NEXT, "send");
178 ret = real_send (socket, buffer, len, flags);
181 if (override_errno != 0) {
182 errno = override_errno;
191 recv (int socket, void *buffer, size_t length, int flags)
195 typedef ssize_t (*real_recv_fn) (int, void *, size_t, int);
197 static real_recv_fn real_recv = 0;
200 real_recv = (real_recv_fn) dlsym (RTLD_NEXT, "recv");
203 ret = real_recv (socket, buffer, length, flags);
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,
214 old_errno = newerrno;
216 } else { /* Remove the callback */
217 memset (&callbacks[i], 0, sizeof (callbacks[0]));
223 pthread_mutex_unlock (&mutex);
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},
247 socket_callback_ (GstValidateAction * action, const void *buff, size_t len)
252 gst_structure_get_int (action->structure, "times", ×);
253 gst_structure_get_int (action->structure, "real_errno", &real_errno);
256 gst_structure_set (action->structure, "times", G_TYPE_INT, times, NULL);
258 gst_validate_action_set_done (action);
266 errno_string_to_int (const gchar * errno_str)
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;
279 _fault_injector_loaded (void)
281 const gchar *ld_preload = g_getenv ("LD_PRELOAD");
284 return (ld_preload && strstr (ld_preload, "libfaultinjection-1.0.so"));
288 _execute_corrupt_socket_recv (GstValidateScenario * scenario,
289 GstValidateAction * action)
291 struct sockaddr_in addr =
292 { AF_INET, htons (42), {htonl (INADDR_LOOPBACK)}, {0} };
293 gint server_port, times;
294 const gchar *errno_str;
297 if (!_fault_injector_loaded ()) {
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");
304 if (!gst_structure_get_int (action->structure, "port", &server_port)) {
305 GST_ERROR ("could not get port to corrupt recv on.");
309 if (!gst_structure_get_int (action->structure, "times", ×)) {
310 gst_structure_set (action->structure, "times", G_TYPE_INT, 1, NULL);
313 errno_str = gst_structure_get_string (action->structure, "errno");
315 GST_ERROR ("Could not get errno string");
319 real_errno = errno_string_to_int (errno_str);
321 if (real_errno == 0) {
322 GST_ERROR ("unrecognized errno");
326 gst_structure_set (action->structure, "real_errno", G_TYPE_INT, real_errno,
329 addr.sin_port = htons (server_port);
331 socket_interposer_set_callback (&addr,
332 (socket_interposer_callback) socket_callback_, action);
334 return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
338 socket_interposer_init (GstPlugin * plugin)
341 gst_validate_register_action_type_dynamic (plugin, "corrupt-socket-recv",
343 _execute_corrupt_socket_recv, ((GstValidateActionParameter[]) {
346 .description = "The port the socket to be corrupted listens on",
349 .possible_variables = NULL,
353 .description = "errno to set when failing",
359 .description = "Number of times to corrupt recv, default is one",
362 .possible_variables = NULL,
367 "corrupt the next socket receive", GST_VALIDATE_ACTION_TYPE_ASYNC);
373 #else /* No LD_PRELOAD tricks on Windows */
376 socket_interposer_init (GstPlugin * plugin)
383 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
385 validatefaultinjection,
386 "Fault injector plugin for GstValidate",
387 socket_interposer_init, VERSION, "LGPL", GST_PACKAGE_NAME,