summaryrefslogtreecommitdiffstats
path: root/media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.c
diff options
context:
space:
mode:
Diffstat (limited to 'media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.c')
-rw-r--r--media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.c686
1 files changed, 686 insertions, 0 deletions
diff --git a/media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.c b/media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.c
new file mode 100644
index 000000000..b26f2d278
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.c
@@ -0,0 +1,686 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+Copyright (c) 2013, Mozilla
+
+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 Adobe Systems, Network Resonance, Mozilla 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
+OWNER 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 <nr_api.h>
+
+#include <assert.h>
+
+#include "nr_proxy_tunnel.h"
+
+#define MAX_HTTP_CONNECT_ADDR_SIZE 256
+#define MAX_HTTP_CONNECT_BUFFER_SIZE 1024
+#define MAX_ALPN_LENGTH 64
+#ifndef CRLF
+#define CRLF "\r\n"
+#endif
+#define END_HEADERS CRLF CRLF
+
+typedef enum {
+ PROXY_TUNNEL_NONE=0,
+ PROXY_TUNNEL_REQUESTED,
+ PROXY_TUNNEL_CONNECTED,
+ PROXY_TUNNEL_CLOSED,
+ PROXY_TUNNEL_FAILED
+} nr_socket_proxy_tunnel_state;
+
+typedef struct nr_socket_proxy_tunnel_ {
+ nr_proxy_tunnel_config *config;
+ nr_socket *inner;
+ nr_transport_addr remote_addr;
+ nr_socket_proxy_tunnel_state state;
+ char buffer[MAX_HTTP_CONNECT_BUFFER_SIZE];
+ size_t buffered_bytes;
+ void *resolver_handle;
+} nr_socket_proxy_tunnel;
+
+typedef struct nr_socket_wrapper_factory_proxy_tunnel_ {
+ nr_proxy_tunnel_config *config;
+} nr_socket_wrapper_factory_proxy_tunnel;
+
+static int nr_socket_proxy_tunnel_destroy(void **objpp);
+static int nr_socket_proxy_tunnel_getfd(void *obj, NR_SOCKET *fd);
+static int nr_socket_proxy_tunnel_getaddr(void *obj, nr_transport_addr *addrp);
+static int nr_socket_proxy_tunnel_connect(void *sock, nr_transport_addr *addr);
+static int nr_socket_proxy_tunnel_write(void *obj, const void *msg, size_t len, size_t *written);
+static int nr_socket_proxy_tunnel_read(void *obj, void * restrict buf, size_t maxlen, size_t *len);
+static int nr_socket_proxy_tunnel_close(void *obj);
+
+int nr_proxy_tunnel_config_copy(nr_proxy_tunnel_config *config, nr_proxy_tunnel_config **copypp);
+
+int nr_socket_wrapper_factory_proxy_tunnel_wrap(void *obj,
+ nr_socket *inner,
+ nr_socket **socketpp);
+
+int nr_socket_wrapper_factory_proxy_tunnel_destroy(void **objpp);
+
+static nr_socket_vtbl nr_socket_proxy_tunnel_vtbl={
+ 1,
+ nr_socket_proxy_tunnel_destroy,
+ 0,
+ 0,
+ nr_socket_proxy_tunnel_getfd,
+ nr_socket_proxy_tunnel_getaddr,
+ nr_socket_proxy_tunnel_connect,
+ nr_socket_proxy_tunnel_write,
+ nr_socket_proxy_tunnel_read,
+ nr_socket_proxy_tunnel_close
+};
+
+static int send_http_connect(nr_socket_proxy_tunnel *sock)
+{
+ int r, _status;
+ int port;
+ int printed;
+ char addr[MAX_HTTP_CONNECT_ADDR_SIZE];
+ char mesg[MAX_HTTP_CONNECT_ADDR_SIZE + MAX_ALPN_LENGTH + 128];
+ size_t offset = 0;
+ size_t bytes_sent;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"send_http_connect");
+
+ if ((r=nr_transport_addr_get_port(&sock->remote_addr, &port))) {
+ ABORT(r);
+ }
+
+ if ((r=nr_transport_addr_get_addrstring(&sock->remote_addr, addr, sizeof(addr)))) {
+ ABORT(r);
+ }
+
+ printed = snprintf(mesg + offset, sizeof(mesg) - offset,
+ "CONNECT %s:%d HTTP/1.0", addr, port);
+ offset += printed;
+ if (printed < 0 || (offset >= sizeof(mesg))) {
+ ABORT(R_FAILED);
+ }
+
+ if (sock->config->alpn) {
+ printed = snprintf(mesg + offset, sizeof(mesg) - offset,
+ CRLF "ALPN: %s", sock->config->alpn);
+ offset += printed;
+ if (printed < 0 || (offset >= sizeof(mesg))) {
+ ABORT(R_FAILED);
+ }
+ }
+ if (offset + sizeof(END_HEADERS) >= sizeof(mesg)) {
+ ABORT(R_FAILED);
+ }
+ memcpy(mesg + offset, END_HEADERS, strlen(END_HEADERS));
+ offset += strlen(END_HEADERS);
+
+ if ((r=nr_socket_write(sock->inner, mesg, offset, &bytes_sent, 0))) {
+ ABORT(r);
+ }
+
+ if (bytes_sent < offset) {
+ /* TODO(bug 1116583): buffering and wait for */
+ r_log(LOG_GENERIC,LOG_DEBUG,"send_http_connect should be buffering %lu", (unsigned long)bytes_sent);
+ ABORT(R_IO_ERROR);
+ }
+
+ sock->state = PROXY_TUNNEL_REQUESTED;
+
+ _status = 0;
+abort:
+ return(_status);
+}
+
+static char *find_http_terminator(char *response, size_t len)
+{
+ char *term = response;
+ char *end = response + len;
+ int N = strlen(END_HEADERS);
+
+ for (; term = memchr(term, '\r', end - term); ++term) {
+ if (end - term >= N && memcmp(term, END_HEADERS, N) == 0) {
+ return term;
+ }
+ }
+
+ return NULL;
+}
+
+static int parse_http_response(char *begin, char *end, unsigned int *status)
+{
+ size_t len = end - begin;
+ char response[MAX_HTTP_CONNECT_BUFFER_SIZE + 1];
+
+ // len should *never* be greater than nr_socket_proxy_tunnel::buffered_bytes.
+ // Which in turn should never be greater nr_socket_proxy_tunnel::buffer size.
+ assert(len <= MAX_HTTP_CONNECT_BUFFER_SIZE);
+ if (len > MAX_HTTP_CONNECT_BUFFER_SIZE) {
+ return R_BAD_DATA;
+ }
+
+ memcpy(response, begin, len);
+ response[len] = '\0';
+
+ // http://www.rfc-editor.org/rfc/rfc7230.txt
+ // status-line = HTTP-version SP status-code SP reason-phrase CRLF
+ // HTTP-version = HTTP-name "/" DIGIT "." DIGIT
+ // HTTP-name = "HTTP" ; "HTTP", case-sensitive
+ // status-code = 3DIGIT
+
+ if (sscanf(response, "HTTP/%*u.%*u %u", status) != 1) {
+ r_log(LOG_GENERIC,LOG_WARNING,"parse_http_response failed to find status (%s)", response);
+ return R_BAD_DATA;
+ }
+
+ return 0;
+}
+
+static int nr_socket_proxy_tunnel_destroy(void **objpp)
+{
+ nr_socket_proxy_tunnel *sock;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_destroy");
+
+ if (!objpp || !*objpp)
+ return 0;
+
+ sock = (nr_socket_proxy_tunnel *)*objpp;
+ *objpp = 0;
+
+ if (sock->resolver_handle) {
+ nr_resolver_cancel(sock->config->resolver, sock->resolver_handle);
+ }
+
+ nr_proxy_tunnel_config_destroy(&sock->config);
+ nr_socket_destroy(&sock->inner);
+ RFREE(sock);
+
+ return 0;
+}
+
+static int nr_socket_proxy_tunnel_getfd(void *obj, NR_SOCKET *fd)
+{
+ nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel *)obj;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_getfd");
+
+ return nr_socket_getfd(sock->inner, fd);
+}
+
+static int nr_socket_proxy_tunnel_getaddr(void *obj, nr_transport_addr *addrp)
+{
+ nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel *)obj;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_getaddr");
+
+ return nr_socket_getaddr(sock->inner, addrp);
+}
+
+static int nr_socket_proxy_tunnel_resolved_cb(void *obj, nr_transport_addr *proxy_addr)
+{
+ int r, _status;
+ nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel*)obj;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_resolved_cb");
+
+ /* Mark the socket resolver as completed */
+ sock->resolver_handle = 0;
+
+ if (proxy_addr) {
+ r_log(LOG_GENERIC,LOG_DEBUG,"Resolved proxy address %s -> %s",
+ sock->config->proxy_host, proxy_addr->as_string);
+ }
+ else {
+ r_log(LOG_GENERIC,LOG_WARNING,"Failed to resolve proxy %s",
+ sock->config->proxy_host);
+ /* TODO: Mozilla bug 1241758: because of the callback the return value goes
+ * nowhere, so we can't mark the candidate as failed, so everything depends
+ * on the overall timeouts in this case. */
+ sock->state = PROXY_TUNNEL_FAILED;
+ ABORT(R_NOT_FOUND);
+ }
+
+ if ((r=nr_socket_connect(sock->inner, proxy_addr))) {
+ ABORT(r);
+ }
+
+ _status = 0;
+abort:
+ return(_status);
+}
+
+int nr_socket_proxy_tunnel_connect(void *obj, nr_transport_addr *addr)
+{
+ int r, _status;
+ int has_addr;
+ nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel*)obj;
+ nr_proxy_tunnel_config *config = sock->config;
+ nr_transport_addr proxy_addr, local_addr;
+ nr_resolver_resource resource;
+
+ if ((r=nr_transport_addr_copy(&sock->remote_addr, addr))) {
+ ABORT(r);
+ }
+
+ assert(config->proxy_host);
+
+ /* Check if the proxy_host is already an IP address */
+ has_addr = !nr_str_port_to_transport_addr(config->proxy_host,
+ config->proxy_port, IPPROTO_TCP, &proxy_addr);
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_connect: %s", config->proxy_host);
+
+ if (!has_addr && !config->resolver) {
+ r_log(LOG_GENERIC,LOG_ERR,"nr_socket_proxy_tunnel_connect name resolver not configured");
+ ABORT(R_NOT_FOUND);
+ }
+
+ if (!has_addr) {
+ resource.domain_name=config->proxy_host;
+ resource.port=config->proxy_port;
+ resource.stun_turn=NR_RESOLVE_PROTOCOL_TURN;
+ resource.transport_protocol=IPPROTO_TCP;
+
+ if ((r=nr_socket_getaddr(sock->inner, &local_addr))) {
+ r_log(LOG_GENERIC,LOG_ERR,"nr_socket_proxy_tunnel_connect failed to get local address");
+ ABORT(r);
+ }
+
+ switch(local_addr.ip_version) {
+ case NR_IPV4:
+ resource.address_family=AF_INET;
+ break;
+ case NR_IPV6:
+ resource.address_family=AF_INET6;
+ break;
+ default:
+ ABORT(R_BAD_ARGS);
+ }
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_connect: nr_resolver_resolve");
+ if ((r=nr_resolver_resolve(config->resolver, &resource,
+ nr_socket_proxy_tunnel_resolved_cb, (void *)sock, &sock->resolver_handle))) {
+ r_log(LOG_GENERIC,LOG_ERR,"Could not invoke DNS resolver");
+ ABORT(r);
+ }
+
+ ABORT(R_WOULDBLOCK);
+ }
+
+ if ((r=nr_socket_connect(sock->inner, &proxy_addr))) {
+ ABORT(r);
+ }
+
+ _status=0;
+abort:
+ return(_status);
+}
+
+int nr_socket_proxy_tunnel_write(void *obj, const void *msg, size_t len,
+ size_t *written)
+{
+ int r, _status;
+ nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel*)obj;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_write");
+
+ if (sock->state >= PROXY_TUNNEL_CLOSED) {
+ return R_FAILED;
+ }
+
+ if (sock->state == PROXY_TUNNEL_NONE) {
+ if ((r=send_http_connect(sock))) {
+ ABORT(r);
+ }
+ }
+
+ if (sock->state != PROXY_TUNNEL_CONNECTED) {
+ return R_WOULDBLOCK;
+ }
+
+ if ((r=nr_socket_write(sock->inner, msg, len, written, 0))) {
+ ABORT(r);
+ }
+
+ _status=0;
+abort:
+ return(_status);
+}
+
+int nr_socket_proxy_tunnel_read(void *obj, void * restrict buf, size_t maxlen,
+ size_t *len)
+{
+ int r, _status;
+ char *ptr, *http_term;
+ size_t bytes_read, available_buffer_len, maxlen_int;
+ size_t pending;
+ nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel*)obj;
+ unsigned int http_status;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_read");
+
+ *len = 0;
+
+ if (sock->state >= PROXY_TUNNEL_CLOSED) {
+ return R_FAILED;
+ }
+
+ if (sock->state == PROXY_TUNNEL_CONNECTED) {
+ return nr_socket_read(sock->inner, buf, maxlen, len, 0);
+ }
+
+ if (sock->buffered_bytes >= sizeof(sock->buffer)) {
+ r_log(LOG_GENERIC,LOG_ERR,"buffer filled waiting for CONNECT response");
+ assert(sock->buffered_bytes == sizeof(sock->buffer));
+ ABORT(R_INTERNAL);
+ }
+
+ /* Do not read more than maxlen bytes */
+ available_buffer_len = sizeof(sock->buffer) - sock->buffered_bytes;
+ maxlen_int = maxlen < available_buffer_len ? maxlen : available_buffer_len;
+ if ((r=nr_socket_read(sock->inner, sock->buffer + sock->buffered_bytes,
+ maxlen_int, &bytes_read, 0))) {
+ ABORT(r);
+ }
+
+ sock->buffered_bytes += bytes_read;
+
+ if (http_term = find_http_terminator(sock->buffer, sock->buffered_bytes)) {
+ if ((r = parse_http_response(sock->buffer, http_term, &http_status))) {
+ ABORT(r);
+ }
+
+ /* TODO (bug 1115934): Handle authentication challenges. */
+ if (http_status < 200 || http_status >= 300) {
+ r_log(LOG_GENERIC,LOG_ERR,"nr_socket_proxy_tunnel_read unable to connect %u",
+ http_status);
+ ABORT(R_FAILED);
+ }
+
+ sock->state = PROXY_TUNNEL_CONNECTED;
+
+ ptr = http_term + strlen(END_HEADERS);
+ pending = sock->buffered_bytes - (ptr - sock->buffer);
+
+ if (pending == 0) {
+ ABORT(R_WOULDBLOCK);
+ }
+
+ assert(pending <= maxlen);
+ *len = pending;
+
+ memcpy(buf, ptr, *len);
+ }
+
+ _status=0;
+abort:
+ if (_status && _status != R_WOULDBLOCK) {
+ sock->state = PROXY_TUNNEL_FAILED;
+ }
+ return(_status);
+}
+
+int nr_socket_proxy_tunnel_close(void *obj)
+{
+ nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel*)obj;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_close");
+
+ if (sock->resolver_handle) {
+ nr_resolver_cancel(sock->config->resolver, sock->resolver_handle);
+ sock->resolver_handle = 0;
+ }
+
+ sock->state = PROXY_TUNNEL_CLOSED;
+
+ return nr_socket_close(sock->inner);
+}
+
+int nr_proxy_tunnel_config_create(nr_proxy_tunnel_config **configpp)
+{
+ int _status;
+ nr_proxy_tunnel_config *configp=0;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_proxy_tunnel_config_create");
+
+ if (!(configp=RCALLOC(sizeof(nr_proxy_tunnel_config))))
+ ABORT(R_NO_MEMORY);
+
+ *configpp=configp;
+ _status=0;
+abort:
+ return(_status);
+}
+
+int nr_proxy_tunnel_config_destroy(nr_proxy_tunnel_config **configpp)
+{
+ nr_proxy_tunnel_config *configp;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_proxy_tunnel_config_destroy");
+
+ if (!configpp || !*configpp)
+ return 0;
+
+ configp = *configpp;
+ *configpp = 0;
+
+ RFREE(configp->proxy_host);
+ RFREE(configp->alpn);
+ RFREE(configp);
+
+ return 0;
+}
+
+int nr_proxy_tunnel_config_set_proxy(nr_proxy_tunnel_config *config,
+ const char *host, UINT2 port)
+{
+ char *hostdup;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_proxy_tunnel_config_set_proxy %s %d", host, port);
+
+ if (!host) {
+ return R_BAD_ARGS;
+ }
+
+ if (!(hostdup = r_strdup(host))) {
+ return R_NO_MEMORY;
+ }
+
+ if (config->proxy_host) {
+ RFREE(config->proxy_host);
+ }
+
+ config->proxy_host = hostdup;
+ config->proxy_port = port;
+
+ return 0;
+}
+
+int nr_proxy_tunnel_config_set_resolver(nr_proxy_tunnel_config *config,
+ nr_resolver *resolver)
+{
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_proxy_tunnel_config_set_resolver");
+
+ config->resolver = resolver;
+
+ return 0;
+}
+
+int nr_proxy_tunnel_config_set_alpn(nr_proxy_tunnel_config *config,
+ const char *alpn)
+{
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_proxy_tunnel_config_set_alpn");
+
+ if (alpn && (strlen(alpn) > MAX_ALPN_LENGTH)) {
+ return R_BAD_ARGS;
+ }
+
+ if (config->alpn) {
+ RFREE(config->alpn);
+ }
+
+ config->alpn = NULL;
+
+ if (alpn) {
+ char *alpndup = r_strdup(alpn);
+
+ if (!alpndup) {
+ return R_NO_MEMORY;
+ }
+
+ config->alpn = alpndup;
+ }
+
+ return 0;
+}
+
+int nr_proxy_tunnel_config_copy(nr_proxy_tunnel_config *config, nr_proxy_tunnel_config **copypp)
+{
+ int r,_status;
+ nr_proxy_tunnel_config *copy = 0;
+
+ if ((r=nr_proxy_tunnel_config_create(&copy)))
+ ABORT(r);
+
+ if ((r=nr_proxy_tunnel_config_set_proxy(copy, config->proxy_host, config->proxy_port)))
+ ABORT(r);
+
+ if ((r=nr_proxy_tunnel_config_set_resolver(copy, config->resolver)))
+ ABORT(r);
+
+ if ((r=nr_proxy_tunnel_config_set_alpn(copy, config->alpn)))
+ ABORT(r);
+
+ *copypp = copy;
+
+ _status=0;
+abort:
+ if (_status) {
+ nr_proxy_tunnel_config_destroy(&copy);
+ }
+ return(_status);
+}
+
+
+int nr_socket_proxy_tunnel_create(nr_proxy_tunnel_config *config,
+ nr_socket *inner,
+ nr_socket **socketpp)
+{
+ int r, _status;
+ nr_socket_proxy_tunnel *sock=0;
+ void *sockv;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_create");
+
+ if (!config) {
+ ABORT(R_BAD_ARGS);
+ }
+
+ if (!(sock=RCALLOC(sizeof(nr_socket_proxy_tunnel)))) {
+ ABORT(R_NO_MEMORY);
+ }
+
+ sock->inner = inner;
+
+ if ((r=nr_proxy_tunnel_config_copy(config, &sock->config)))
+ ABORT(r);
+
+ if ((r=nr_socket_create_int(sock, &nr_socket_proxy_tunnel_vtbl, socketpp))) {
+ ABORT(r);
+ }
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_created");
+
+ _status=0;
+abort:
+ if (_status) {
+ sockv = sock;
+ nr_socket_proxy_tunnel_destroy(&sockv);
+ }
+ return(_status);
+}
+
+int nr_socket_wrapper_factory_proxy_tunnel_wrap(void *obj,
+ nr_socket *inner,
+ nr_socket **socketpp)
+{
+ nr_socket_wrapper_factory_proxy_tunnel *wrapper = (nr_socket_wrapper_factory_proxy_tunnel *)obj;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_wrapper_factory_proxy_tunnel_wrap");
+
+ return nr_socket_proxy_tunnel_create(wrapper->config, inner, socketpp);
+}
+
+
+int nr_socket_wrapper_factory_proxy_tunnel_destroy(void **objpp) {
+ nr_socket_wrapper_factory_proxy_tunnel *wrapper;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_wrapper_factory_proxy_tunnel_destroy");
+
+ if (!objpp || !*objpp)
+ return 0;
+
+ wrapper = (nr_socket_wrapper_factory_proxy_tunnel *)*objpp;
+ *objpp = 0;
+
+ nr_proxy_tunnel_config_destroy(&wrapper->config);
+ RFREE(wrapper);
+
+ return 0;
+}
+
+static nr_socket_wrapper_factory_vtbl proxy_tunnel_wrapper_vtbl = {
+ nr_socket_wrapper_factory_proxy_tunnel_wrap,
+ nr_socket_wrapper_factory_proxy_tunnel_destroy
+};
+
+int nr_socket_wrapper_factory_proxy_tunnel_create(nr_proxy_tunnel_config *config,
+ nr_socket_wrapper_factory **factory) {
+ int r,_status;
+ nr_socket_wrapper_factory_proxy_tunnel *wrapper=0;
+ void *wrapperv;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_wrapper_factory_proxy_tunnel_create");
+
+ if (!(wrapper=RCALLOC(sizeof(nr_socket_wrapper_factory_proxy_tunnel))))
+ ABORT(R_NO_MEMORY);
+
+ if ((r=nr_proxy_tunnel_config_copy(config, &wrapper->config)))
+ ABORT(r);
+
+ if ((r=nr_socket_wrapper_factory_create_int(wrapper, &proxy_tunnel_wrapper_vtbl, factory)))
+ ABORT(r);
+
+ _status=0;
+abort:
+ if (_status) {
+ wrapperv = wrapper;
+ nr_socket_wrapper_factory_proxy_tunnel_destroy(&wrapperv);
+ }
+ return(_status);
+}