forked from Qortal/Brooklyn
380 lines
13 KiB
C
380 lines
13 KiB
C
/*
|
|
Copyright (c) 2012, Broadcom Europe Ltd
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of the copyright holder nor the
|
|
names of its contributors may be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "containers/containers.h"
|
|
#include "containers/core/containers_common.h"
|
|
#include "containers/core/containers_io.h"
|
|
#include "containers/core/containers_uri.h"
|
|
#include "containers/net/net_sockets.h"
|
|
|
|
/* Uncomment this macro definition to capture data read and written through this interface */
|
|
/* #define IO_NET_CAPTURE_PACKETS */
|
|
|
|
#ifdef IO_NET_CAPTURE_PACKETS
|
|
#include <stdio.h>
|
|
|
|
#ifdef ENABLE_CONTAINERS_STANDALONE
|
|
#ifdef _MSC_VER
|
|
#define IO_NET_CAPTURE_PREFIX "C:\\"
|
|
#else /* !_MSC_VER */
|
|
#define IO_NET_CAPTURE_PREFIX "~/"
|
|
#endif
|
|
#else /* !ENABLE_CONTAINERS_STANDALONE */
|
|
#define IO_NET_CAPTURE_PREFIX "/mfs/sd/"
|
|
#endif
|
|
|
|
#define IO_NET_CAPTURE_READ_FILE "capture_read_%s_%s%c.pkt"
|
|
#define IO_NET_CAPTURE_WRITE_FILE "capture_write_%s_%s%c.pkt"
|
|
#define IO_NET_CAPTURE_READ_FORMAT IO_NET_CAPTURE_PREFIX IO_NET_CAPTURE_READ_FILE
|
|
#define IO_NET_CAPTURE_WRITE_FORMAT IO_NET_CAPTURE_PREFIX IO_NET_CAPTURE_WRITE_FILE
|
|
|
|
#define CAPTURE_FILENAME_BUFFER_SIZE 300
|
|
|
|
#define CAPTURE_BUFFER_SIZE 65536
|
|
|
|
/** Native byte order word */
|
|
#define NATIVE_BYTE_ORDER 0x50415753
|
|
#endif
|
|
|
|
/******************************************************************************
|
|
Defines and constants.
|
|
******************************************************************************/
|
|
|
|
/******************************************************************************
|
|
Type definitions
|
|
******************************************************************************/
|
|
typedef struct VC_CONTAINER_IO_MODULE_T
|
|
{
|
|
VC_CONTAINER_NET_T *sock;
|
|
#ifdef IO_NET_CAPTURE_PACKETS
|
|
FILE *read_capture_file;
|
|
FILE *write_capture_file;
|
|
#endif
|
|
} VC_CONTAINER_IO_MODULE_T;
|
|
|
|
/** List of recognised network URI schemes (TCP or UDP).
|
|
* Note: always use lower case for the scheme name. */
|
|
static struct
|
|
{
|
|
const char *scheme;
|
|
bool is_udp;
|
|
} recognised_schemes[] = {
|
|
{ "rtp:", true },
|
|
{ "rtsp:", false },
|
|
};
|
|
|
|
/******************************************************************************
|
|
Function prototypes
|
|
******************************************************************************/
|
|
VC_CONTAINER_STATUS_T vc_container_io_net_open( VC_CONTAINER_IO_T *, const char *,
|
|
VC_CONTAINER_IO_MODE_T );
|
|
|
|
/******************************************************************************
|
|
Local Functions
|
|
******************************************************************************/
|
|
|
|
#ifdef IO_NET_CAPTURE_PACKETS
|
|
/*****************************************************************************/
|
|
static FILE *io_net_open_capture_file(const char *host_str,
|
|
const char *port_str,
|
|
bool is_udp,
|
|
VC_CONTAINER_IO_MODE_T mode)
|
|
{
|
|
char filename[CAPTURE_FILENAME_BUFFER_SIZE];
|
|
const char *format;
|
|
FILE *stream = NULL;
|
|
uint32_t byte_order = NATIVE_BYTE_ORDER;
|
|
|
|
switch (mode)
|
|
{
|
|
case VC_CONTAINER_IO_MODE_WRITE:
|
|
format = IO_NET_CAPTURE_WRITE_FORMAT;
|
|
break;
|
|
case VC_CONTAINER_IO_MODE_READ:
|
|
format = IO_NET_CAPTURE_READ_FORMAT;
|
|
break;
|
|
default:
|
|
/* Invalid mode */
|
|
return NULL;
|
|
}
|
|
|
|
if (!host_str)
|
|
host_str = "";
|
|
if (!port_str)
|
|
port_str = "";
|
|
|
|
/* Check filename will fit in buffer */
|
|
if (strlen(format) + strlen(host_str) + strlen(port_str) - 4 > CAPTURE_FILENAME_BUFFER_SIZE)
|
|
return NULL;
|
|
|
|
/* Create the file */
|
|
sprintf(filename, format, host_str, port_str, is_udp ? 'u' : 't');
|
|
stream = fopen(filename, "wb");
|
|
if (!stream)
|
|
return NULL;
|
|
|
|
/* Buffer plenty of data at a time, if possible */
|
|
setvbuf(stream, NULL, _IOFBF, CAPTURE_BUFFER_SIZE);
|
|
|
|
/* Start file with a byte order marker */
|
|
if (fwrite(&byte_order, 1, sizeof(byte_order), stream) != sizeof(byte_order))
|
|
{
|
|
/* Failed to write even just the byte order mark - abort */
|
|
fclose(stream);
|
|
stream = NULL;
|
|
remove(filename);
|
|
}
|
|
|
|
return stream;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
static void io_net_capture_write_packet( FILE *stream,
|
|
const char *buffer,
|
|
uint32_t buffer_size )
|
|
{
|
|
if (stream && buffer && buffer_size)
|
|
{
|
|
fwrite(&buffer_size, 1, sizeof(buffer_size), stream);
|
|
fwrite(buffer, 1, buffer_size, stream);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*****************************************************************************/
|
|
static bool io_net_recognise_scheme(const char *uri, bool *is_udp)
|
|
{
|
|
size_t ii;
|
|
const char *scheme;
|
|
|
|
if (!uri)
|
|
return false;
|
|
|
|
for (ii = 0; ii < countof(recognised_schemes); ii++)
|
|
{
|
|
scheme = recognised_schemes[ii].scheme;
|
|
if (strncmp(scheme, uri, strlen(scheme)) == 0)
|
|
{
|
|
*is_udp = recognised_schemes[ii].is_udp;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
static VC_CONTAINER_STATUS_T translate_net_status_to_container_status(vc_container_net_status_t net_status)
|
|
{
|
|
switch (net_status)
|
|
{
|
|
case VC_CONTAINER_NET_SUCCESS: return VC_CONTAINER_SUCCESS;
|
|
case VC_CONTAINER_NET_ERROR_INVALID_SOCKET: return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
|
|
case VC_CONTAINER_NET_ERROR_NOT_ALLOWED: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
|
|
case VC_CONTAINER_NET_ERROR_INVALID_PARAMETER: return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
|
|
case VC_CONTAINER_NET_ERROR_NO_MEMORY: return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
|
|
case VC_CONTAINER_NET_ERROR_IN_USE: return VC_CONTAINER_ERROR_URI_OPEN_FAILED;
|
|
case VC_CONTAINER_NET_ERROR_NETWORK: return VC_CONTAINER_ERROR_EOS;
|
|
case VC_CONTAINER_NET_ERROR_CONNECTION_LOST: return VC_CONTAINER_ERROR_EOS;
|
|
case VC_CONTAINER_NET_ERROR_NOT_CONNECTED: return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
|
|
case VC_CONTAINER_NET_ERROR_TIMED_OUT: return VC_CONTAINER_ERROR_ABORTED;
|
|
case VC_CONTAINER_NET_ERROR_CONNECTION_REFUSED: return VC_CONTAINER_ERROR_NOT_FOUND;
|
|
case VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND: return VC_CONTAINER_ERROR_NOT_FOUND;
|
|
case VC_CONTAINER_NET_ERROR_TRY_AGAIN: return VC_CONTAINER_ERROR_CONTINUE;
|
|
default: return VC_CONTAINER_ERROR_FAILED;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
static VC_CONTAINER_STATUS_T io_net_close( VC_CONTAINER_IO_T *p_ctx )
|
|
{
|
|
VC_CONTAINER_IO_MODULE_T *module = p_ctx->module;
|
|
|
|
if (!module)
|
|
return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
|
|
|
|
if (module->sock)
|
|
vc_container_net_close(module->sock);
|
|
#ifdef IO_NET_CAPTURE_PACKETS
|
|
if (module->read_capture_file)
|
|
fclose(module->read_capture_file);
|
|
if (module->write_capture_file)
|
|
fclose(module->write_capture_file);
|
|
#endif
|
|
free(module);
|
|
p_ctx->module = NULL;
|
|
|
|
return VC_CONTAINER_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
static size_t io_net_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size)
|
|
{
|
|
size_t ret = vc_container_net_read(p_ctx->module->sock, buffer, size);
|
|
vc_container_net_status_t net_status;
|
|
|
|
net_status = vc_container_net_status(p_ctx->module->sock);
|
|
p_ctx->status = translate_net_status_to_container_status(net_status);
|
|
|
|
#ifdef IO_NET_CAPTURE_PACKETS
|
|
if (p_ctx->status == VC_CONTAINER_SUCCESS)
|
|
io_net_capture_write_packet(p_ctx->module->read_capture_file, (const char *)buffer, ret);
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
static size_t io_net_write(VC_CONTAINER_IO_T *p_ctx, const void *buffer, size_t size)
|
|
{
|
|
size_t ret = vc_container_net_write(p_ctx->module->sock, buffer, size);
|
|
vc_container_net_status_t net_status;
|
|
|
|
net_status = vc_container_net_status(p_ctx->module->sock);
|
|
p_ctx->status = translate_net_status_to_container_status(net_status);
|
|
|
|
#ifdef IO_NET_CAPTURE_PACKETS
|
|
if (p_ctx->status == VC_CONTAINER_SUCCESS)
|
|
io_net_capture_write_packet(p_ctx->module->write_capture_file, (const char *)buffer, ret);
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
static VC_CONTAINER_STATUS_T io_net_control(struct VC_CONTAINER_IO_T *p_ctx,
|
|
VC_CONTAINER_CONTROL_T operation,
|
|
va_list args)
|
|
{
|
|
vc_container_net_status_t net_status;
|
|
VC_CONTAINER_STATUS_T status;
|
|
|
|
switch (operation)
|
|
{
|
|
case VC_CONTAINER_CONTROL_IO_SET_READ_BUFFER_SIZE:
|
|
net_status = vc_container_net_control(p_ctx->module->sock, VC_CONTAINER_NET_CONTROL_SET_READ_BUFFER_SIZE, args);
|
|
break;
|
|
case VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS:
|
|
net_status = vc_container_net_control(p_ctx->module->sock, VC_CONTAINER_NET_CONTROL_SET_READ_TIMEOUT_MS, args);
|
|
break;
|
|
default:
|
|
net_status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED;
|
|
}
|
|
|
|
status = translate_net_status_to_container_status(net_status);
|
|
p_ctx->status = status;
|
|
|
|
return status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
static VC_CONTAINER_STATUS_T io_net_open_socket(VC_CONTAINER_IO_T *ctx,
|
|
VC_CONTAINER_IO_MODE_T mode, bool is_udp)
|
|
{
|
|
VC_CONTAINER_IO_MODULE_T *module = ctx->module;
|
|
VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
|
|
const char *host, *port;
|
|
|
|
/* Treat empty host or port strings as not defined */
|
|
port = vc_uri_port(ctx->uri_parts);
|
|
if (port && !*port)
|
|
port = NULL;
|
|
|
|
/* Require the port to be defined */
|
|
if (!port) { status = VC_CONTAINER_ERROR_URI_OPEN_FAILED; goto error; }
|
|
|
|
host = vc_uri_host(ctx->uri_parts);
|
|
if (host && !*host)
|
|
host = NULL;
|
|
|
|
if (!host)
|
|
{
|
|
/* TCP servers cannot be handled by this interface and UDP senders need a target */
|
|
if (!is_udp || mode == VC_CONTAINER_IO_MODE_WRITE)
|
|
{
|
|
status = VC_CONTAINER_ERROR_URI_OPEN_FAILED;
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
module->sock = vc_container_net_open(host, port, is_udp ? 0 : VC_CONTAINER_NET_OPEN_FLAG_STREAM, NULL);
|
|
if (!module->sock) { status = VC_CONTAINER_ERROR_URI_NOT_FOUND; goto error; }
|
|
|
|
#ifdef IO_NET_CAPTURE_PACKETS
|
|
if (!is_udp || mode == VC_CONTAINER_IO_MODE_READ)
|
|
module->read_capture_file = io_net_open_capture_file(host, port, is_udp, VC_CONTAINER_IO_MODE_READ);
|
|
if (!is_udp || mode == VC_CONTAINER_IO_MODE_WRITE)
|
|
module->write_capture_file = io_net_open_capture_file(host, port, is_udp, VC_CONTAINER_IO_MODE_WRITE);
|
|
#endif
|
|
|
|
error:
|
|
return status;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
Functions exported as part of the I/O Module API
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************/
|
|
VC_CONTAINER_STATUS_T vc_container_io_net_open( VC_CONTAINER_IO_T *p_ctx,
|
|
const char *unused, VC_CONTAINER_IO_MODE_T mode )
|
|
{
|
|
VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
|
|
VC_CONTAINER_IO_MODULE_T *module = 0;
|
|
bool is_udp;
|
|
VC_CONTAINER_PARAM_UNUSED(unused);
|
|
|
|
if (!io_net_recognise_scheme(p_ctx->uri, &is_udp))
|
|
{ status = VC_CONTAINER_ERROR_URI_NOT_FOUND; goto error; }
|
|
|
|
module = (VC_CONTAINER_IO_MODULE_T *)malloc( sizeof(*module) );
|
|
if (!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
|
|
memset(module, 0, sizeof(*module));
|
|
p_ctx->module = module;
|
|
|
|
status = io_net_open_socket(p_ctx, mode, is_udp);
|
|
if (status != VC_CONTAINER_SUCCESS)
|
|
goto error;
|
|
|
|
p_ctx->pf_close = io_net_close;
|
|
p_ctx->pf_read = io_net_read;
|
|
p_ctx->pf_write = io_net_write;
|
|
p_ctx->pf_control = io_net_control;
|
|
|
|
/* Disable caching, as this will block waiting for enough data to fill the cache or an error */
|
|
p_ctx->capabilities = VC_CONTAINER_IO_CAPS_CANT_SEEK;
|
|
|
|
return VC_CONTAINER_SUCCESS;
|
|
|
|
error:
|
|
io_net_close(p_ctx);
|
|
return status;
|
|
}
|