1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the copyright holder nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28#include <stdlib.h>
29#include <string.h>
30
31#include "containers/containers.h"
32#include "containers/core/containers_common.h"
33#include "containers/core/containers_io.h"
34#include "containers/core/containers_uri.h"
35#include "containers/net/net_sockets.h"
36
37/* Uncomment this macro definition to capture data read and written through this interface */
38/* #define IO_NET_CAPTURE_PACKETS */
39
40#ifdef IO_NET_CAPTURE_PACKETS
41#include <stdio.h>
42
43#ifdef ENABLE_CONTAINERS_STANDALONE
44#ifdef _MSC_VER
45#define IO_NET_CAPTURE_PREFIX "C:\\"
46#else /* !_MSC_VER */
47#define IO_NET_CAPTURE_PREFIX "~/"
48#endif
49#else /* !ENABLE_CONTAINERS_STANDALONE */
50#define IO_NET_CAPTURE_PREFIX "/mfs/sd/"
51#endif
52
53#define IO_NET_CAPTURE_READ_FILE "capture_read_%s_%s%c.pkt"
54#define IO_NET_CAPTURE_WRITE_FILE "capture_write_%s_%s%c.pkt"
55#define IO_NET_CAPTURE_READ_FORMAT IO_NET_CAPTURE_PREFIX IO_NET_CAPTURE_READ_FILE
56#define IO_NET_CAPTURE_WRITE_FORMAT IO_NET_CAPTURE_PREFIX IO_NET_CAPTURE_WRITE_FILE
57
58#define CAPTURE_FILENAME_BUFFER_SIZE 300
59
60#define CAPTURE_BUFFER_SIZE 65536
61
62/** Native byte order word */
63#define NATIVE_BYTE_ORDER 0x50415753
64#endif
65
66/******************************************************************************
67Defines and constants.
68******************************************************************************/
69
70/******************************************************************************
71Type definitions
72******************************************************************************/
73typedef struct VC_CONTAINER_IO_MODULE_T
74{
75 VC_CONTAINER_NET_T *sock;
76#ifdef IO_NET_CAPTURE_PACKETS
77 FILE *read_capture_file;
78 FILE *write_capture_file;
79#endif
80} VC_CONTAINER_IO_MODULE_T;
81
82/** List of recognised network URI schemes (TCP or UDP).
83 * Note: always use lower case for the scheme name. */
84static struct
85{
86 const char *scheme;
87 bool is_udp;
88} recognised_schemes[] = {
89 { "rtp:", true },
90 { "rtsp:", false },
91};
92
93/******************************************************************************
94Function prototypes
95******************************************************************************/
96VC_CONTAINER_STATUS_T vc_container_io_net_open( VC_CONTAINER_IO_T *, const char *,
97 VC_CONTAINER_IO_MODE_T );
98
99/******************************************************************************
100Local Functions
101******************************************************************************/
102
103#ifdef IO_NET_CAPTURE_PACKETS
104/*****************************************************************************/
105static FILE *io_net_open_capture_file(const char *host_str,
106 const char *port_str,
107 bool is_udp,
108 VC_CONTAINER_IO_MODE_T mode)
109{
110 char filename[CAPTURE_FILENAME_BUFFER_SIZE];
111 const char *format;
112 FILE *stream = NULL;
113 uint32_t byte_order = NATIVE_BYTE_ORDER;
114
115 switch (mode)
116 {
117 case VC_CONTAINER_IO_MODE_WRITE:
118 format = IO_NET_CAPTURE_WRITE_FORMAT;
119 break;
120 case VC_CONTAINER_IO_MODE_READ:
121 format = IO_NET_CAPTURE_READ_FORMAT;
122 break;
123 default:
124 /* Invalid mode */
125 return NULL;
126 }
127
128 if (!host_str)
129 host_str = "";
130 if (!port_str)
131 port_str = "";
132
133 /* Check filename will fit in buffer */
134 if (strlen(format) + strlen(host_str) + strlen(port_str) - 4 > CAPTURE_FILENAME_BUFFER_SIZE)
135 return NULL;
136
137 /* Create the file */
138 sprintf(filename, format, host_str, port_str, is_udp ? 'u' : 't');
139 stream = fopen(filename, "wb");
140 if (!stream)
141 return NULL;
142
143 /* Buffer plenty of data at a time, if possible */
144 setvbuf(stream, NULL, _IOFBF, CAPTURE_BUFFER_SIZE);
145
146 /* Start file with a byte order marker */
147 if (fwrite(&byte_order, 1, sizeof(byte_order), stream) != sizeof(byte_order))
148 {
149 /* Failed to write even just the byte order mark - abort */
150 fclose(stream);
151 stream = NULL;
152 remove(filename);
153 }
154
155 return stream;
156}
157
158/*****************************************************************************/
159static void io_net_capture_write_packet( FILE *stream,
160 const char *buffer,
161 uint32_t buffer_size )
162{
163 if (stream && buffer && buffer_size)
164 {
165 fwrite(&buffer_size, 1, sizeof(buffer_size), stream);
166 fwrite(buffer, 1, buffer_size, stream);
167 }
168}
169#endif
170
171/*****************************************************************************/
172static bool io_net_recognise_scheme(const char *uri, bool *is_udp)
173{
174 size_t ii;
175 const char *scheme;
176
177 if (!uri)
178 return false;
179
180 for (ii = 0; ii < countof(recognised_schemes); ii++)
181 {
182 scheme = recognised_schemes[ii].scheme;
183 if (strncmp(scheme, uri, strlen(scheme)) == 0)
184 {
185 *is_udp = recognised_schemes[ii].is_udp;
186 return true;
187 }
188 }
189
190 return false;
191}
192
193/*****************************************************************************/
194static VC_CONTAINER_STATUS_T translate_net_status_to_container_status(vc_container_net_status_t net_status)
195{
196 switch (net_status)
197 {
198 case VC_CONTAINER_NET_SUCCESS: return VC_CONTAINER_SUCCESS;
199 case VC_CONTAINER_NET_ERROR_INVALID_SOCKET: return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
200 case VC_CONTAINER_NET_ERROR_NOT_ALLOWED: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
201 case VC_CONTAINER_NET_ERROR_INVALID_PARAMETER: return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
202 case VC_CONTAINER_NET_ERROR_NO_MEMORY: return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
203 case VC_CONTAINER_NET_ERROR_IN_USE: return VC_CONTAINER_ERROR_URI_OPEN_FAILED;
204 case VC_CONTAINER_NET_ERROR_NETWORK: return VC_CONTAINER_ERROR_EOS;
205 case VC_CONTAINER_NET_ERROR_CONNECTION_LOST: return VC_CONTAINER_ERROR_EOS;
206 case VC_CONTAINER_NET_ERROR_NOT_CONNECTED: return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
207 case VC_CONTAINER_NET_ERROR_TIMED_OUT: return VC_CONTAINER_ERROR_ABORTED;
208 case VC_CONTAINER_NET_ERROR_CONNECTION_REFUSED: return VC_CONTAINER_ERROR_NOT_FOUND;
209 case VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND: return VC_CONTAINER_ERROR_NOT_FOUND;
210 case VC_CONTAINER_NET_ERROR_TRY_AGAIN: return VC_CONTAINER_ERROR_CONTINUE;
211 default: return VC_CONTAINER_ERROR_FAILED;
212 }
213}
214
215/*****************************************************************************/
216static VC_CONTAINER_STATUS_T io_net_close( VC_CONTAINER_IO_T *p_ctx )
217{
218 VC_CONTAINER_IO_MODULE_T *module = p_ctx->module;
219
220 if (!module)
221 return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
222
223 if (module->sock)
224 vc_container_net_close(module->sock);
225#ifdef IO_NET_CAPTURE_PACKETS
226 if (module->read_capture_file)
227 fclose(module->read_capture_file);
228 if (module->write_capture_file)
229 fclose(module->write_capture_file);
230#endif
231 free(module);
232 p_ctx->module = NULL;
233
234 return VC_CONTAINER_SUCCESS;
235}
236
237/*****************************************************************************/
238static size_t io_net_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size)
239{
240 size_t ret = vc_container_net_read(p_ctx->module->sock, buffer, size);
241 vc_container_net_status_t net_status;
242
243 net_status = vc_container_net_status(p_ctx->module->sock);
244 p_ctx->status = translate_net_status_to_container_status(net_status);
245
246#ifdef IO_NET_CAPTURE_PACKETS
247 if (p_ctx->status == VC_CONTAINER_SUCCESS)
248 io_net_capture_write_packet(p_ctx->module->read_capture_file, (const char *)buffer, ret);
249#endif
250
251 return ret;
252}
253
254/*****************************************************************************/
255static size_t io_net_write(VC_CONTAINER_IO_T *p_ctx, const void *buffer, size_t size)
256{
257 size_t ret = vc_container_net_write(p_ctx->module->sock, buffer, size);
258 vc_container_net_status_t net_status;
259
260 net_status = vc_container_net_status(p_ctx->module->sock);
261 p_ctx->status = translate_net_status_to_container_status(net_status);
262
263#ifdef IO_NET_CAPTURE_PACKETS
264 if (p_ctx->status == VC_CONTAINER_SUCCESS)
265 io_net_capture_write_packet(p_ctx->module->write_capture_file, (const char *)buffer, ret);
266#endif
267
268 return ret;
269}
270
271/*****************************************************************************/
272static VC_CONTAINER_STATUS_T io_net_control(struct VC_CONTAINER_IO_T *p_ctx,
273 VC_CONTAINER_CONTROL_T operation,
274 va_list args)
275{
276 vc_container_net_status_t net_status;
277 VC_CONTAINER_STATUS_T status;
278
279 switch (operation)
280 {
281 case VC_CONTAINER_CONTROL_IO_SET_READ_BUFFER_SIZE:
282 net_status = vc_container_net_control(p_ctx->module->sock, VC_CONTAINER_NET_CONTROL_SET_READ_BUFFER_SIZE, args);
283 break;
284 case VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS:
285 net_status = vc_container_net_control(p_ctx->module->sock, VC_CONTAINER_NET_CONTROL_SET_READ_TIMEOUT_MS, args);
286 break;
287 default:
288 net_status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED;
289 }
290
291 status = translate_net_status_to_container_status(net_status);
292 p_ctx->status = status;
293
294 return status;
295}
296
297/*****************************************************************************/
298static VC_CONTAINER_STATUS_T io_net_open_socket(VC_CONTAINER_IO_T *ctx,
299 VC_CONTAINER_IO_MODE_T mode, bool is_udp)
300{
301 VC_CONTAINER_IO_MODULE_T *module = ctx->module;
302 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
303 const char *host, *port;
304
305 /* Treat empty host or port strings as not defined */
306 port = vc_uri_port(ctx->uri_parts);
307 if (port && !*port)
308 port = NULL;
309
310 /* Require the port to be defined */
311 if (!port) { status = VC_CONTAINER_ERROR_URI_OPEN_FAILED; goto error; }
312
313 host = vc_uri_host(ctx->uri_parts);
314 if (host && !*host)
315 host = NULL;
316
317 if (!host)
318 {
319 /* TCP servers cannot be handled by this interface and UDP senders need a target */
320 if (!is_udp || mode == VC_CONTAINER_IO_MODE_WRITE)
321 {
322 status = VC_CONTAINER_ERROR_URI_OPEN_FAILED;
323 goto error;
324 }
325 }
326
327 module->sock = vc_container_net_open(host, port, is_udp ? 0 : VC_CONTAINER_NET_OPEN_FLAG_STREAM, NULL);
328 if (!module->sock) { status = VC_CONTAINER_ERROR_URI_NOT_FOUND; goto error; }
329
330#ifdef IO_NET_CAPTURE_PACKETS
331 if (!is_udp || mode == VC_CONTAINER_IO_MODE_READ)
332 module->read_capture_file = io_net_open_capture_file(host, port, is_udp, VC_CONTAINER_IO_MODE_READ);
333 if (!is_udp || mode == VC_CONTAINER_IO_MODE_WRITE)
334 module->write_capture_file = io_net_open_capture_file(host, port, is_udp, VC_CONTAINER_IO_MODE_WRITE);
335#endif
336
337error:
338 return status;
339}
340
341/*****************************************************************************
342Functions exported as part of the I/O Module API
343 *****************************************************************************/
344
345/*****************************************************************************/
346VC_CONTAINER_STATUS_T vc_container_io_net_open( VC_CONTAINER_IO_T *p_ctx,
347 const char *unused, VC_CONTAINER_IO_MODE_T mode )
348{
349 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
350 VC_CONTAINER_IO_MODULE_T *module = 0;
351 bool is_udp;
352 VC_CONTAINER_PARAM_UNUSED(unused);
353
354 if (!io_net_recognise_scheme(p_ctx->uri, &is_udp))
355 { status = VC_CONTAINER_ERROR_URI_NOT_FOUND; goto error; }
356
357 module = (VC_CONTAINER_IO_MODULE_T *)malloc( sizeof(*module) );
358 if (!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
359 memset(module, 0, sizeof(*module));
360 p_ctx->module = module;
361
362 status = io_net_open_socket(p_ctx, mode, is_udp);
363 if (status != VC_CONTAINER_SUCCESS)
364 goto error;
365
366 p_ctx->pf_close = io_net_close;
367 p_ctx->pf_read = io_net_read;
368 p_ctx->pf_write = io_net_write;
369 p_ctx->pf_control = io_net_control;
370
371 /* Disable caching, as this will block waiting for enough data to fill the cache or an error */
372 p_ctx->capabilities = VC_CONTAINER_IO_CAPS_CANT_SEEK;
373
374 return VC_CONTAINER_SUCCESS;
375
376error:
377 io_net_close(p_ctx);
378 return status;
379}
380