Add support for HTTP and SOCKS proxies\nSincity final iOS features

This commit is contained in:
bossiel 2015-06-20 17:58:34 +00:00
parent aac1d07d67
commit 91cf3de635
50 changed files with 9025 additions and 5693 deletions

View File

@ -6,7 +6,7 @@
#
AC_PREREQ([2.0])
AC_INIT(libdoubango, 2.0.1311, doubango(at)googlegroups(dot)com)
AC_INIT(libdoubango, 2.0.1313, doubango(at)googlegroups(dot)com)
AC_CANONICAL_SYSTEM
AM_INIT_AUTOMAKE([subdir-objects])
AC_CONFIG_MACRO_DIR([m4])

View File

@ -45,6 +45,7 @@
#if defined(__APPLE__)
# define TDAV_UNDER_APPLE 1
# include <TargetConditionals.h>
# include <Availability.h>
#endif
#if TARGET_OS_MAC
# define TDAV_UNDER_MAC 1

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,8 @@ libtinyHTTP_la_SOURCES = \
src/thttp_event.c\
src/thttp_message.c\
src/thttp_session.c\
src/thttp_url.c
src/thttp_url.c\
src/thttp_proxy_node_plugin.c
libtinyHTTP_la_SOURCES += src/auth/thttp_auth.c\
src/auth/thttp_challenge.c

View File

@ -160,6 +160,9 @@ typedef struct thttp_stack_s
}
thttp_stack_t;
TINYHTTP_API int thttp_startup();
TINYHTTP_API int thttp_cleanup();
TINYHTTP_API thttp_stack_handle_t *thttp_stack_create(thttp_stack_callback_f callback, ...);
TINYHTTP_API int thttp_stack_start(thttp_stack_handle_t *self);
TINYHTTP_API int thttp_stack_set(thttp_stack_handle_t *self, ...);

View File

@ -1,7 +1,5 @@
/*
* Copyright (C) 2010-2011 Mamadou Diop.
*
* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
* Copyright (C) 2010-2015 Mamadou DIOP.
*
* This file is part of Open Source Doubango Framework.
*
@ -23,9 +21,6 @@
/**@file thttp_challenge.h
* @brief HTTP authentication challenge.
*
* @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
*
*/
#ifndef TINYHTTP_AUTHENTICATION_CHALLENGE_H
#define TINYHTTP_AUTHENTICATION_CHALLENGE_H
@ -38,6 +33,7 @@
#include "tinyhttp/auth/thttp_auth.h"
#include "tsk_object.h"
#include "tsk_buffer.h"
#include "tsk_list.h"
#include "tsk_md5.h"
@ -65,8 +61,11 @@ typedef tsk_list_t thttp_challenges_L_t;
int thttp_challenge_update(thttp_challenge_t *self, const char* scheme, const char* realm, const char* nonce, const char* opaque, const char* algorithm, const char* qop);
thttp_header_t *thttp_challenge_create_header_authorization(thttp_challenge_t *self, const char* username, const char* password, const thttp_request_t *request);
thttp_header_t *thttp_challenge_create_header_authorization_2(thttp_challenge_t *self, const char* username, const char* password, const char* method, const char *uristring, const tsk_buffer_t* entity_body);
thttp_challenge_t* thttp_challenge_create(tsk_bool_t isproxy,const char* scheme, const char* realm, const char* nonce, const char* opaque, const char* algorithm, const char* qop);
thttp_challenge_t* thttp_challenge_dup(const thttp_challenge_t* self);
TINYHTTP_GEXTERN const tsk_object_def_t *thttp_challenge_def_t;

View File

@ -1,7 +1,6 @@
/*
* Copyright (C) 2010-2011 Mamadou Diop.
* Copyright (C) 2010-2015 Mamadou DIOP.
*
* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
*
* This file is part of Open Source Doubango Framework.
*
@ -24,9 +23,6 @@
* @brief Represents a HTTP message. A HTTP message is either a request from a client to a server, or a
* response from a server to a client.
*
* @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
*
*/
#ifndef THTTP_MESSAGE_H
#define THTTP_MESSAGE_H

View File

@ -0,0 +1,33 @@
/*
* Copyright (C) 2010-2015 Mamadou DIOP.
* Copyright (C) 2015 Doubango Telecom.
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DOUBANGO.
*
*/
#if !defined(TINYHTTP_PROXY_NODE_PLUGIN_H)
#define TINYHTTP_PROXY_NODE_PLUGIN_H
#include "tinyhttp_config.h"
THTTP_BEGIN_DECLS
TINYHTTP_GEXTERN const struct tnet_proxy_node_plugin_def_s* thttp_proxy_node_plugin_def_t;
THTTP_END_DECLS
#endif /* TINYHTTP_PROXY_NODE_PLUGIN_H */

View File

@ -1,7 +1,5 @@
/*
* Copyright (C) 2010-2011 Mamadou Diop.
*
* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
* Copyright (C) 2010-2015 Mamadou DIOP.
*
* This file is part of Open Source Doubango Framework.
*
@ -23,9 +21,6 @@
/**@file thttp_session.h
* @brief HTTP session.
*
* @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
*
*/
#ifndef THTTP_SESSION_H
#define THTTP_SESSION_H

View File

@ -1,8 +1,6 @@
/*
* Copyright (C) 2010-2011 Mamadou Diop.
* Copyright (C) 2010-2015 Mamadou DIOP.
*
* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
@ -23,9 +21,6 @@
/**@file thttp_url.h
* @brief HTTP/HTTPS URL.
*
* @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
*
*/
#ifndef TINYHTTP_URL_H
#define TINYHTTP_URL_H

View File

@ -35,6 +35,22 @@
# endif
#endif
// OS X or iOS
#if defined(__APPLE__)
# define THTTP_UNDER_APPLE 1
# include <TargetConditionals.h>
# include <Availability.h>
#endif
#if TARGET_OS_MAC
# define THTTP_UNDER_MAC 1
#endif
#if TARGET_OS_IPHONE
# define THTTP_UNDER_IPHONE 1
#endif
#if TARGET_IPHONE_SIMULATOR
# define THTTP_UNDER_IPHONE_SIMULATOR 1
#endif
#if (THTTP_UNDER_WINDOWS || defined(__SYMBIAN32__)) && defined(TINYHTTP_EXPORTS)
# define TINYHTTP_API __declspec(dllexport)
# define TINYHTTP_GEXTERN extern __declspec(dllexport)

View File

@ -154,7 +154,7 @@ int thttp_auth_digest_HA2(const char* method, const char* url, const tsk_buffer_
}
else if (tsk_striequals(qop, "auth-int"))
{
if (entity_body && entity_body->data){
if (entity_body && entity_body->data && entity_body->size){
tsk_md5string_t hEntity;
if ((ret = tsk_md5compute(entity_body->data, entity_body->size, &hEntity))){
goto bail;

View File

@ -1,22 +1,22 @@
/*
* Copyright (C) 2010-2015 Mamadou DIOP.
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DOUBANGO.
*
*/
* Copyright (C) 2010-2015 Mamadou DIOP.
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DOUBANGO.
*
*/
/**@file thttp_challenge.c
* @brief HTTP authentication challenge.
@ -40,170 +40,212 @@
#define THTTP_CHALLENGE_IS_AKAv1(self) ((self) ? tsk_striequals((self)->algorithm, "AKAv1-MD5") : tsk_false)
#define THTTP_CHALLENGE_IS_AKAv2(self) ((self) ? tsk_striequals((self)->algorithm, "AKAv2-MD5") : tsk_false)
static int _thttp_challenge_reset_cnonce(thttp_challenge_t *self);
thttp_challenge_t* thttp_challenge_create(tsk_bool_t isproxy, const char* scheme, const char* realm, const char* nonce, const char* opaque, const char* algorithm, const char* qop)
{
return tsk_object_new(thttp_challenge_def_t, isproxy, scheme, realm, nonce, opaque, algorithm, qop);
thttp_challenge_t* challenge = tsk_object_new(thttp_challenge_def_t);
if (challenge) {
challenge->isproxy = isproxy;
challenge->scheme = tsk_strdup(scheme);
challenge->realm = tsk_strdup(realm);
challenge->nonce = tsk_strdup(nonce);
challenge->opaque = tsk_strdup(opaque);
challenge->algorithm = tsk_strdup(algorithm);
if (!tsk_strnullORempty(qop)) {
challenge->qop = tsk_strcontains(qop, tsk_strlen(qop), "auth-int") ? "auth-int" :
(tsk_strcontains(qop, tsk_strlen(qop), "auth") ? "auth" : tsk_null);
}
if (challenge->qop) {
_thttp_challenge_reset_cnonce(challenge);
}
}
return challenge;
}
int thttp_challenge_reset_cnonce(thttp_challenge_t *self)
thttp_challenge_t* thttp_challenge_dup(const thttp_challenge_t* self)
{
if (self)
{
if (self->qop) /* client nonce is only used if qop=auth, auth-int or both */
{
#if 0
memcpy(self->cnonce, "f221681c1e42fb5f8f9957bf7e72eb2b", 32);
#else
tsk_istr_t istr;
if (!self) {
TSK_DEBUG_ERROR("Invalid parameter");
return tsk_null;
}
return thttp_challenge_create(self->isproxy, self->scheme, self->realm, self->nonce, self->opaque, self->algorithm, self->qop);
}
tsk_strrandom(&istr);
tsk_md5compute(istr, tsk_strlen(istr), &self->cnonce);
static int _thttp_challenge_reset_cnonce(thttp_challenge_t *self)
{
if (self)
{
if (self->qop) /* client nonce is only used if qop=auth, auth-int or both */
{
#if 0
memcpy(self->cnonce, "f221681c1e42fb5f8f9957bf7e72eb2b", 32);
#else
tsk_istr_t istr;
tsk_strrandom(&istr);
tsk_md5compute(istr, tsk_strlen(istr), &self->cnonce);
#endif
self->nc = 1;
}
}
return -1;
self->nc = 1;
}
}
return -1;
}
int thttp_challenge_get_digest_response(thttp_challenge_t *self, const char* username, const char* password, const char* method, const char* uristring, const tsk_buffer_t* entity_body, char** response)
{
if (THTTP_CHALLENGE_IS_DIGEST(self)){
tsk_md5string_t ha1, ha2, md5_response;
nonce_count_t nc;
/* ===
Calculate HA1 = MD5(A1) = M5(username:realm:secret)
*/
thttp_auth_digest_HA1(username, self->realm, password, &ha1);
/* ===
HA2
*/
thttp_auth_digest_HA2(method,
uristring,
entity_body,
self->qop,
&ha2);
/* RESPONSE */
if (self->nc){
THTTP_NCOUNT_2_STRING(self->nc, nc);
}
thttp_auth_digest_response((const tsk_md5string_t *)&ha1,
self->nonce,
nc,
self->cnonce,
self->qop,
(const tsk_md5string_t *)&ha2,
&md5_response);
if (self->qop){
self->nc++;
}
if (response && !*response){
*response = tsk_strdup(md5_response);
}
return 0;
}
return -1;
if (THTTP_CHALLENGE_IS_DIGEST(self)){
tsk_md5string_t ha1, ha2, md5_response;
nonce_count_t nc;
/* ===
Calculate HA1 = MD5(A1) = M5(username:realm:secret)
*/
thttp_auth_digest_HA1(username, self->realm, password, &ha1);
/* ===
HA2
*/
thttp_auth_digest_HA2(method,
uristring,
entity_body,
self->qop,
&ha2);
/* RESPONSE */
if (self->nc){
THTTP_NCOUNT_2_STRING(self->nc, nc);
}
thttp_auth_digest_response((const tsk_md5string_t *)&ha1,
self->nonce,
nc,
self->cnonce,
self->qop,
(const tsk_md5string_t *)&ha2,
&md5_response);
if (self->qop){
self->nc++;
}
if (response && !*response){
*response = tsk_strdup(md5_response);
}
return 0;
}
return -1;
}
int thttp_challenge_update(thttp_challenge_t *self, const char* scheme, const char* realm, const char* nonce, const char* opaque, const char* algorithm, const char* qop)
{
if (self)
{
int noncechanged = !tsk_striequals(self->nonce, nonce);
tsk_strupdate(&self->scheme, scheme);
tsk_strupdate(&self->realm, realm);
tsk_strupdate(&self->nonce, nonce);
tsk_strupdate(&self->opaque, opaque);
tsk_strupdate(&self->algorithm, algorithm);
if (qop){
self->qop = tsk_strcontains(qop, tsk_strlen(qop), "auth-int") ? "auth-int" :
(tsk_strcontains(qop, tsk_strlen(qop), "auth") ? "auth" : tsk_null);
}
if (noncechanged && self->qop){
thttp_challenge_reset_cnonce(self);
}
return 0;
}
return -1;
if (self) {
int noncechanged = !tsk_striequals(self->nonce, nonce);
tsk_strupdate(&self->scheme, scheme);
tsk_strupdate(&self->realm, realm);
tsk_strupdate(&self->nonce, nonce);
tsk_strupdate(&self->opaque, opaque);
tsk_strupdate(&self->algorithm, algorithm);
if (qop) {
self->qop = tsk_strcontains(qop, tsk_strlen(qop), "auth-int") ? "auth-int" :
(tsk_strcontains(qop, tsk_strlen(qop), "auth") ? "auth" : tsk_null);
}
if (noncechanged && self->qop) {
_thttp_challenge_reset_cnonce(self);
}
return 0;
}
return -1;
}
thttp_header_t *thttp_challenge_create_header_authorization(thttp_challenge_t *self, const char* username, const char* password, const thttp_request_t *request)
{
char* response = tsk_null;
tsk_size_t response_size = 0;
nonce_count_t nc;
char *uristring = tsk_null;
thttp_header_t *header = 0;
if (!self || !request || !request->line.request.url){
goto bail;
}
/* Sets URI: hpath do not start with / ==> append a '/'*/
tsk_sprintf(&uristring, "/%s", request->line.request.url->hpath ? request->line.request.url->hpath : "");
/* We compute the nc here because @ref thttp_challenge_get_response function will increment it's value. */
if (self->nc){
THTTP_NCOUNT_2_STRING(self->nc, nc);
}
/* Computes the response (Basic and Digest)*/
if (THTTP_CHALLENGE_IS_DIGEST(self)){
if (thttp_challenge_get_digest_response(self, username, password, request->line.request.method, uristring, request->Content, &response)){
goto bail;
}
response_size = (TSK_MD5_DIGEST_SIZE * 2);
}
else if (THTTP_CHALLENGE_IS_BASIC(self)){
response_size = thttp_auth_basic_response(username, password, &response);
}
else{
TSK_DEBUG_ERROR("%s not supported as scheme.", self->scheme);
goto bail;
}
#define THTTP_AUTH_COPY_VALUES(hdr) \
hdr->username = tsk_strdup(username); \
hdr->scheme = tsk_strdup(self->scheme); \
hdr->realm = tsk_strdup(self->realm); \
hdr->nonce = tsk_strdup(self->nonce); \
hdr->qop = tsk_strdup(self->qop); \
hdr->opaque = tsk_strdup(self->opaque); \
hdr->algorithm = self->algorithm ? tsk_strdup(self->algorithm) : tsk_strdup("MD5"); \
hdr->cnonce = self->nc? tsk_strdup(self->cnonce) : 0; \
hdr->uri = tsk_strdup(uristring); \
hdr->nc = self->nc? tsk_strdup(nc) : 0; \
hdr->response = tsk_strndup(response, response_size); \
if (self->isproxy){
thttp_header_Proxy_Authorization_t *proxy_auth = thttp_header_authorization_create(); // Very bad way to create Proxy_auth header.
THTTP_HEADER(proxy_auth)->type = thttp_htype_Proxy_Authorization;
THTTP_AUTH_COPY_VALUES(proxy_auth);
header = THTTP_HEADER(proxy_auth);
}
else{
thttp_header_Authorization_t *auth = thttp_header_authorization_create();
THTTP_AUTH_COPY_VALUES(auth);
header = THTTP_HEADER(auth);
}
char *uristring = tsk_null;
thttp_header_t *header = tsk_null;
if (!self || !request || !request->line.request.url) {
TSK_DEBUG_ERROR("Invalid parameter");
goto bail;
}
/* Sets URI: hpath do not start with / ==> append a '/'*/
tsk_sprintf(&uristring, "/%s", request->line.request.url->hpath ? request->line.request.url->hpath : "");
header = thttp_challenge_create_header_authorization_2(self, username, password, request->line.request.method, uristring, request->Content);
bail:
TSK_FREE(uristring);
TSK_FREE(response);
return header;
#undef THTTP_AUTH_COPY_VALUES
TSK_FREE(uristring);
return header;
}
thttp_header_t *thttp_challenge_create_header_authorization_2(thttp_challenge_t *self, const char* username, const char* password, const char* method, const char *uristring, const tsk_buffer_t* entity_body)
{
char* response = tsk_null;
tsk_size_t response_size = 0;
nonce_count_t nc;
thttp_header_t *header = tsk_null;
if (!self || tsk_strnullORempty(uristring)) {
TSK_DEBUG_ERROR("Invalid parameter");
goto bail;
}
/* We compute the nc here because @ref thttp_challenge_get_response function will increment it's value. */
if (self->nc) {
THTTP_NCOUNT_2_STRING(self->nc, nc);
}
/* Computes the response (Basic and Digest)*/
if (THTTP_CHALLENGE_IS_DIGEST(self)) {
if (thttp_challenge_get_digest_response(self, username, password, method, uristring, entity_body, &response)){
goto bail;
}
response_size = (TSK_MD5_DIGEST_SIZE * 2);
}
else if (THTTP_CHALLENGE_IS_BASIC(self)) {
response_size = thttp_auth_basic_response(username, password, &response);
}
else {
TSK_DEBUG_ERROR("%s not supported as scheme.", self->scheme);
goto bail;
}
#define THTTP_AUTH_COPY_VALUES(hdr) \
hdr->username = tsk_strdup(username); \
hdr->scheme = tsk_strdup(self->scheme); \
hdr->realm = tsk_strdup(self->realm); \
hdr->nonce = tsk_strdup(self->nonce); \
hdr->qop = tsk_strdup(self->qop); \
hdr->opaque = tsk_strdup(self->opaque); \
hdr->algorithm = self->algorithm ? tsk_strdup(self->algorithm) : tsk_strdup("MD5"); \
hdr->cnonce = self->nc? tsk_strdup(self->cnonce) : 0; \
hdr->uri = tsk_strdup(uristring); \
hdr->nc = self->nc? tsk_strdup(nc) : 0; \
hdr->response = tsk_strndup(response, response_size); \
if (self->isproxy) {
thttp_header_Proxy_Authorization_t *proxy_auth = thttp_header_authorization_create(); // Very bad way to create Proxy_auth header.
THTTP_HEADER(proxy_auth)->type = thttp_htype_Proxy_Authorization;
THTTP_AUTH_COPY_VALUES(proxy_auth);
header = THTTP_HEADER(proxy_auth);
}
else{
thttp_header_Authorization_t *auth = thttp_header_authorization_create();
THTTP_AUTH_COPY_VALUES(auth);
header = THTTP_HEADER(auth);
}
bail:
TSK_FREE(response);
return header;
#undef THTTP_AUTH_COPY_VALUES
}
@ -232,60 +274,43 @@ bail:
//
/**@ingroup thttp_challenge_group
*/
*/
static tsk_object_t* thttp_challenge_ctor(tsk_object_t *self, va_list * app)
{
thttp_challenge_t *challenge = self;
if (challenge){
const char* qop;
challenge->isproxy = va_arg(*app, tsk_bool_t);
challenge->scheme = tsk_strdup(va_arg(*app, const char*));
challenge->realm = tsk_strdup(va_arg(*app, const char*));
challenge->nonce = tsk_strdup(va_arg(*app, const char*));
challenge->opaque = tsk_strdup(va_arg(*app, const char*));
challenge->algorithm = tsk_strdup(va_arg(*app, const char*));
qop = va_arg(*app, const char*);
if (qop){
challenge->qop = tsk_strcontains(qop, tsk_strlen(qop), "auth-int") ? "auth-int" :
(tsk_strcontains(qop, tsk_strlen(qop), "auth") ? "auth" : tsk_null);
}
if (challenge->qop){
thttp_challenge_reset_cnonce(challenge);
}
}
else TSK_DEBUG_ERROR("Failed to create new http challenge object.");
return self;
thttp_challenge_t *challenge = self;
if (challenge) {
}
return self;
}
/**@ingroup thttp_challenge_group
*/
*/
static tsk_object_t* thttp_challenge_dtor(tsk_object_t *self)
{
thttp_challenge_t *challenge = self;
if (challenge){
TSK_FREE(challenge->scheme);
TSK_FREE(challenge->realm);
TSK_FREE(challenge->nonce);
TSK_FREE(challenge->opaque);
TSK_FREE(challenge->algorithm);
//TSK_FREE(challenge->qop);
}
else{
TSK_DEBUG_ERROR("Null HTTP challenge object.");
}
return self;
thttp_challenge_t *challenge = self;
if (challenge){
TSK_FREE(challenge->scheme);
TSK_FREE(challenge->realm);
TSK_FREE(challenge->nonce);
TSK_FREE(challenge->opaque);
TSK_FREE(challenge->algorithm);
//TSK_FREE(challenge->qop);
}
else{
TSK_DEBUG_ERROR("Null HTTP challenge object.");
}
return self;
}
static const tsk_object_def_t thttp_challenge_def_s =
{
sizeof(thttp_challenge_t),
thttp_challenge_ctor,
thttp_challenge_dtor,
tsk_null
sizeof(thttp_challenge_t),
thttp_challenge_ctor,
thttp_challenge_dtor,
tsk_null
};
const tsk_object_def_t *thttp_challenge_def_t = &thttp_challenge_def_s;

View File

@ -32,7 +32,10 @@
#include "tinyhttp/thttp_dialog.h"
#include "tinyhttp/thttp_proxy_node_plugin.h"
#include "tnet.h"
#include "tnet_proxy_plugin.h"
#include "tsk_runnable.h"
#include "tsk_time.h"
@ -266,6 +269,32 @@ int ret = thttp_action_GET(session, "http://ipv6.google.com",
/**@defgroup thttp_stack_group HTTP/HTTPS stack
*/
static tsk_bool_t __thttp_started = tsk_false;
int thttp_startup()
{
int ret = tnet_startup();
if (ret != 0) {
return ret;
}
if (!__thttp_started) {
if ((ret = tnet_proxy_node_plugin_register(thttp_proxy_node_plugin_def_t)) != 0) {
return ret;
}
}
__thttp_started = (ret == 0) ? tsk_true : tsk_false;
return ret;
}
int thttp_cleanup()
{
if (__thttp_started) {
tnet_proxy_node_plugin_unregister(thttp_proxy_node_plugin_def_t);
__thttp_started = tsk_false;
}
return 0;
}
/* min size of a stream chunck to form a valid HTTP message */
#define THTTP_MIN_STREAM_CHUNCK_SIZE 0x32

View File

@ -0,0 +1,878 @@
/*
* Copyright (C) 2010-2015 Mamadou DIOP.
* Copyright (C) 2015 Doubango Telecom.
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DOUBANGO.
*
*/
#include "tinyhttp/thttp_proxy_node_plugin.h"
#include "tinyhttp/thttp_message.h"
#include "tinyhttp/parsers/thttp_parser_message.h"
#include "tinyhttp/auth/thttp_challenge.h"
#include "tinyhttp/headers/thttp_header_Authorization.h"
#include "tinyhttp/headers/thttp_header_WWW_Authenticate.h"
#include "tinyhttp/headers/thttp_header_Transfer_Encoding.h"
#include "tinyhttp/headers/thttp_header_Dummy.h"
#include "tnet_proxy_plugin.h"
#include "tsk_safeobj.h"
#include "tsk_buffer.h"
#include "tsk_memory.h"
#include "tsk_string.h"
#include "tsk_debug.h"
#define ONE_CRLF "\r\n"
#define ONE_CRLF_LEN 2
#define TWO_CRLF ONE_CRLF ONE_CRLF
#define TWO_CRLF_LEN 4
#define CONNECT_METHOD_NAME "CONNECT"
#define END_OF_ADD_HEADERS() tsk_null
#define BUILD_USING_PARSE 1
#if !defined(THTTP_PROXY_AUTH_CACHE)
# define THTTP_PROXY_AUTH_CACHE 0
#endif
// Without closing CRLF
#define HTTP_CONNECT_FORMAT "CONNECT %s:%d HTTP/1.0\r\n" \
"User-Agent: Doubango Telecom\r\n"\
"Host: %s:%d\r\n" \
"Content-Length: 0\r\n"\
"Proxy-Connection: keep-Alive\r\n" \
"Pragma: no-cache\r\n"
typedef struct thttp_proxy_node_plugin_s
{
TNET_DECLARE_PROXY_NONE;
struct {
tsk_bool_t completed;
tsk_bool_t started;
void* pending_data_ptr;
int pending_data_len;
thttp_request_t *req_connect;
tsk_buffer_t* buff;
tsk_bool_t chanllenge_answered;
thttp_challenge_t *challenge;
#if THTTP_PROXY_AUTH_CACHE
struct thttp_proxy_auth_cache_s* proxy_auth_cache;
#endif
} handshacking;
TSK_DECLARE_SAFEOBJ;
}
thttp_proxy_node_plugin_t;
#if THTTP_PROXY_AUTH_CACHE
struct thttp_proxy_auth_cache_s;
static thttp_challenge_t* _thttp_proxy_auth_cache_challenge_get(struct thttp_proxy_auth_cache_s* self, const char* proxy_host, tnet_port_t proxy_port, const char* scheme, const char* realm, const char* nonce, const char* opaque, const char* algorithm, const char* qop);
static thttp_challenge_t* _thttp_proxy_auth_cache_challenge_get_2(struct thttp_proxy_auth_cache_s* self, const char* proxy_host, tnet_port_t proxy_port);
static thttp_header_t * _thttp_proxy_auth_cache_create_header_authorization(struct thttp_proxy_auth_cache_s* self, const char* proxy_host, tnet_port_t proxy_port, const char* username, const char* password, const char* uristring);
static struct thttp_proxy_auth_cache_s* _thttp_proxy_auth_cache_get_ref();
static int _thttp_proxy_auth_cache_lock(struct thttp_proxy_auth_cache_s* self);
static int _thttp_proxy_auth_cache_unlock(struct thttp_proxy_auth_cache_s* self);
#endif /* THTTP_PROXY_AUTH_CACHE */
static int _thttp_proxy_node_plugin_update_challenge(thttp_proxy_node_plugin_t *self, const thttp_response_t* response, tsk_bool_t answered);
static int _thttp_proxy_node_plugin_configure(tnet_proxy_node_t* self, ...)
{
va_list ap;
int ret = 0;
thttp_proxy_node_plugin_t* node = (thttp_proxy_node_plugin_t*)self;
// input parameters already checked by the caller
va_start(ap, self);
tsk_safeobj_lock(node);
ret = tnet_proxy_node_configure_2(self, &ap);
tsk_safeobj_unlock(node);
va_end(ap);
return ret;
}
static int _thttp_proxy_node_plugin_start_handshaking(tnet_proxy_node_t* self)
{
thttp_proxy_node_plugin_t* node = (thttp_proxy_node_plugin_t*)self;
int ret = 0;
#define kConnectContentData tsk_null
// input parameters already checked by the caller
if (tsk_strnullORempty(self->dst_host) || !self->dst_port) {
TSK_DEBUG_ERROR("Invalid destination address for HTTP proxy node: %s:%d", self->dst_host, self->dst_port);
return -1;
}
tsk_safeobj_lock(node);
if (node->handshacking.started) {
TSK_DEBUG_ERROR("handshaking already started");
goto bail;
}
TSK_OBJECT_SAFE_FREE(node->handshacking.req_connect);
TSK_FREE(node->handshacking.pending_data_ptr);
node->handshacking.pending_data_len = 0;
/* Build the HTTP(S) CONNECT Request */
#if BUILD_USING_PARSE
{
tsk_ragel_state_t state;
thttp_header_t* auth_hdr = tsk_null;
char* uristring = tsk_null;
tsk_sprintf((char**)&node->handshacking.pending_data_ptr, HTTP_CONNECT_FORMAT, self->dst_host, self->dst_port, self->dst_host, self->dst_port);
# if THTTP_PROXY_AUTH_CACHE
tsk_sprintf(&uristring, "%s:%d", self->dst_host, self->dst_port);
auth_hdr = _thttp_proxy_auth_cache_create_header_authorization(node->handshacking.proxy_auth_cache, self->proxy_host, self->proxy_port, self->login, self->password, uristring);
TSK_FREE(uristring);
# endif
if (auth_hdr) {
char *auth_hdr_string = tsk_null;
if (auth_hdr && (auth_hdr_string = thttp_header_tostring(THTTP_HEADER(auth_hdr)))) {
tsk_strcat((char**)&node->handshacking.pending_data_ptr, auth_hdr_string);
TSK_FREE(auth_hdr_string);
}
TSK_OBJECT_SAFE_FREE(auth_hdr);
}
tsk_strcat((char**)&node->handshacking.pending_data_ptr, ONE_CRLF);
node->handshacking.pending_data_len = (int)tsk_strlen(node->handshacking.pending_data_ptr);
tsk_ragel_state_init(&state, node->handshacking.pending_data_ptr, node->handshacking.pending_data_len);
if ((ret = thttp_message_parse(&state, &node->handshacking.req_connect, tsk_false/* do not extract the content */)) != 0) {
TSK_DEBUG_ERROR("Failed to parse HTTP CONNECT message: %.*s", node->handshacking.pending_data_len, (const char*)node->handshacking.pending_data_ptr);
TSK_FREE(node->handshacking.pending_data_ptr);
node->handshacking.pending_data_len = 0;
ret = -4;
goto bail;
}
}
#else
{
thttp_url_t* url_connect = tsk_null;
thttp_header_t* auth_hdr = tsk_null;
char* uristring = tsk_null;
if (!(url_connect = thttp_url_create(self->type == tnet_proxy_type_http ? thttp_url_http : thttp_url_https))) {
TSK_DEBUG_ERROR("Failed to create HTTP(S) URL");
ret = -2;
goto bail;
}
tsk_strupdate(&url_connect->host, self->dst_host);
url_connect->port = self->dst_port;
if (!(node->handshacking.req_connect = thttp_message_create(CONNECT_METHOD_NAME, url_connect))) {
TSK_DEBUG_ERROR("Failed to create HTTP(S) message");
TSK_OBJECT_SAFE_FREE(url_connect);
ret = -3;
goto bail;
}
TSK_OBJECT_SAFE_FREE(url_connect);
ret = thttp_message_add_headers_2(node->handshacking.req_connect,
THTTP_HEADER_DUMMY_VA_ARGS("User-Agent", "Doubango Telecom"),
// Host header will be added by serialize()
THTTP_HEADER_CONTENT_LENGTH_VA_ARGS(0),
THTTP_HEADER_DUMMY_VA_ARGS("Proxy-Connection", "keep-Alive"),
THTTP_HEADER_DUMMY_VA_ARGS("Connection", "keep-Alive"),
THTTP_HEADER_DUMMY_VA_ARGS("Pragma", "no-cache"),
END_OF_ADD_HEADERS()
);
# if THTTP_PROXY_AUTH_CACHE
tsk_sprintf(&uristring, "%s:%d", self->dst_host, self->dst_port);
auth_hdr = _thttp_proxy_auth_cache_create_header_authorization(node->handshacking.proxy_auth_cache, self->proxy_host, self->proxy_port, self->login, self->password, uristring);
TSK_FREE(uristring);
# endif
if (auth_hdr) {
thttp_message_add_header(node->handshacking.req_connect, auth_hdr);
TSK_OBJECT_SAFE_FREE(auth_hdr);
}
if (ret != 0) {
TSK_OBJECT_SAFE_FREE(node->handshacking.req_connect);
TSK_DEBUG_ERROR("Failed to add HTTP(S) headers");
goto bail;
}
}
#endif
/* Parse the connect method */
if (!node->handshacking.pending_data_ptr) { // see "BUILD_USING_PARSE"
node->handshacking.pending_data_ptr = thttp_message_tostring(node->handshacking.req_connect);
node->handshacking.pending_data_len = (int)tsk_strlen((const char*)node->handshacking.pending_data_ptr);
}
if (!(node->handshacking.started = (node->handshacking.pending_data_len > 0))) {
TSK_DEBUG_ERROR("Failed to parse HTTP connect data");
ret = -3;
goto bail;
}
bail:
tsk_safeobj_unlock(node);
return ret;
}
static int _thttp_proxy_node_plugin_set_handshaking_data(tnet_proxy_node_t* self, const void* data_ptr, tsk_size_t data_size)
{
int ret = 0, endOfheaders = -1;
tsk_ragel_state_t state;
thttp_message_t *message = tsk_null;
tsk_bool_t have_all_content = tsk_false;
thttp_proxy_node_plugin_t* node = (thttp_proxy_node_plugin_t*)self;
// input parameters already checked by the caller
TSK_DEBUG_INFO("HTTP(s) incoming proxy handshaking data:%.*s", (int)data_size, data_ptr);
tsk_safeobj_lock(node);
if (!node->handshacking.started) {
TSK_DEBUG_ERROR("handshaking not started");
ret = -3;
goto bail;
}
if (!node->handshacking.buff) {
if (!(node->handshacking.buff = tsk_buffer_create(data_ptr, data_size))) {
ret = -2;
goto bail;
}
}
else {
if ((ret = tsk_buffer_append(node->handshacking.buff, data_ptr, data_size)) != 0) {
goto bail;
}
}
/* Check if we have all HTTP headers. */
parse_buffer:
if ((endOfheaders = tsk_strindexOf(TSK_BUFFER_DATA(node->handshacking.buff), TSK_BUFFER_SIZE(node->handshacking.buff), TWO_CRLF)) < 0) {
TSK_DEBUG_INFO("No all HTTP headers in the TCP buffer.");
goto bail;
}
/* If we are here this mean that we have all HTTP headers.
* ==> Parse the HTTP message without the content.
*/
tsk_ragel_state_init(&state, TSK_BUFFER_DATA(node->handshacking.buff), endOfheaders + TWO_CRLF_LEN);
if ((ret = thttp_message_parse(&state, &message, tsk_false/* do not extract the content */)) == 0) {
const thttp_header_Transfer_Encoding_t* transfer_Encoding;
/* chunked? */
if ((transfer_Encoding = (const thttp_header_Transfer_Encoding_t*)thttp_message_get_header(message, thttp_htype_Transfer_Encoding)) && tsk_striequals(transfer_Encoding->encoding, "chunked")) {
const char* start = (const char*)(TSK_BUFFER_TO_U8(node->handshacking.buff) + (endOfheaders + TWO_CRLF_LEN));
const char* end = (const char*)(TSK_BUFFER_TO_U8(node->handshacking.buff) + TSK_BUFFER_SIZE(node->handshacking.buff));
int index;
TSK_DEBUG_INFO("CHUNKED transfer.");
while (start < end) {
/* RFC 2616 - 19.4.6 Introduction of Transfer-Encoding */
// read chunk-size, chunk-extension (if any) and CRLF
tsk_size_t chunk_size = (tsk_size_t)tsk_atox(start);
if ((index = tsk_strindexOf(start, (tsk_size_t)(end-start), ONE_CRLF)) >=0) {
start += index + ONE_CRLF_LEN;
}
else {
TSK_DEBUG_ERROR("Parsing chunked data has failed.");
break;
}
if (chunk_size == 0 && ((start + 2) <= end) && *start == '\r' && *(start+ 1) == '\n') {
int parsed_len = (int)(start - (const char*)(TSK_BUFFER_TO_U8(node->handshacking.buff))) + TWO_CRLF_LEN;
tsk_buffer_remove(node->handshacking.buff, 0, parsed_len);
have_all_content = tsk_true;
break;
}
thttp_message_append_content(message, start, chunk_size);
start += chunk_size + ONE_CRLF_LEN;
}
}
else {
tsk_size_t clen = THTTP_MESSAGE_CONTENT_LENGTH(message); /* MUST have content-length header. */
if (clen == 0) { /* No content */
tsk_buffer_remove(node->handshacking.buff, 0, (endOfheaders + TWO_CRLF_LEN)); /* Remove HTTP headers and CRLF ==> must never happen */
have_all_content = tsk_true;
}
else { /* There is a content */
if ((endOfheaders + TWO_CRLF_LEN + clen) > TSK_BUFFER_SIZE(node->handshacking.buff)) { /* There is content but not all the content. */
TSK_DEBUG_INFO("No all HTTP content in the TCP buffer.");
goto bail;
}
else {
/* Add the content to the message. */
thttp_message_add_content(message, tsk_null, TSK_BUFFER_TO_U8(node->handshacking.buff) + endOfheaders + TWO_CRLF_LEN, clen);
/* Remove HTTP headers, CRLF and the content. */
tsk_buffer_remove(node->handshacking.buff, 0, (endOfheaders + TWO_CRLF_LEN + clen));
have_all_content = tsk_true;
}
}
}
}
/* Alert the dialog (FSM) */
if (message) {
if (have_all_content) { /* only if we have all data */
// CHECK MESSAGE HERE
if (THTTP_MESSAGE_IS_RESPONSE(message)) {
if (THTTP_RESPONSE_IS_1XX(message)) {
// nothing to do...ignore provisional responses
}
else if (THTTP_RESPONSE_IS_2XX(message)) {
node->handshacking.completed = tsk_true;
ret = 0;
}
else if (message->line.response.status_code == 407 || message->line.response.status_code == 401) {
ret = _thttp_proxy_node_plugin_update_challenge(node, (const thttp_response_t*)message, node->handshacking.chanllenge_answered);
if (ret != 0) {
TSK_DEBUG_ERROR("Breaking HTTP(s) handshaking process (failed to update challenges)");
goto bail;
}
// Add creadentials to the CONNECT request
if (node->handshacking.req_connect && node->handshacking.challenge) {
thttp_header_t* auth_hdr;
#if BUILD_USING_PARSE
char* auth_hdr_string = tsk_null, *uristring = tsk_null;
#endif /* BUILD_USING_PARSE */
TSK_FREE(node->handshacking.pending_data_ptr);
node->handshacking.pending_data_len = 0;
#if BUILD_USING_PARSE
node->handshacking.pending_data_len = tsk_sprintf((char**)&node->handshacking.pending_data_ptr, HTTP_CONNECT_FORMAT, self->dst_host, self->dst_port, self->dst_host, self->dst_port);
#endif /* BUILD_USING_PARSE */
tsk_sprintf(&uristring, "%s:%d", self->dst_host, self->dst_port);
# if THTTP_PROXY_AUTH_CACHE
auth_hdr = _thttp_proxy_auth_cache_create_header_authorization(node->handshacking.proxy_auth_cache, self->proxy_host, self->proxy_port, self->login, self->password, uristring);
# else
auth_hdr = thttp_challenge_create_header_authorization_2(node->handshacking.challenge, self->login, self->password, "CONNECT", uristring, kConnectContentData);
# endif /* THTTP_PROXY_AUTH_CACHE */
if (auth_hdr) {
#if BUILD_USING_PARSE
if ((auth_hdr_string = thttp_header_tostring(THTTP_HEADER(auth_hdr)))) {
tsk_strcat((char**)&node->handshacking.pending_data_ptr, auth_hdr_string);
TSK_FREE(auth_hdr_string);
}
#else
thttp_message_add_header(node->handshacking.req_connect, auth_hdr);
#endif /* BUILD_USING_PARSE */
tsk_object_unref(auth_hdr), auth_hdr = tsk_null;
}
TSK_FREE(uristring);
#if BUILD_USING_PARSE
tsk_strcat((char**)&node->handshacking.pending_data_ptr, ONE_CRLF);
#endif /* BUILD_USING_PARSE */
// update pending handshaking data
if (!node->handshacking.pending_data_ptr && !(node->handshacking.pending_data_ptr = thttp_message_tostring(node->handshacking.req_connect))) {
TSK_DEBUG_ERROR("Breaking HTTP(s) handshaking process (failed to parse request)");
ret = -4;
goto bail;
}
node->handshacking.pending_data_len = (int)tsk_strlen((const char*)node->handshacking.pending_data_ptr);
node->handshacking.chanllenge_answered = (node->handshacking.pending_data_len > 0);
}
}
else {
// any other error must break the handshaking process
TSK_DEBUG_ERROR("Breaking HTTP(s) handshaking process (status code = %hu)", message->line.response.status_code);
ret = -5;
goto bail;
}
}
else {
TSK_DEBUG_ERROR("Incoming HTTP request not expected from the proxy");
ret = -4;
goto bail;
}
/* Parse next chunck */
if (TSK_BUFFER_SIZE(node->handshacking.buff) > 0) {
TSK_OBJECT_SAFE_FREE(message);
goto parse_buffer;
}
}
}
bail:
tsk_safeobj_unlock(node);
TSK_OBJECT_SAFE_FREE(message);
return ret;
}
static int _thttp_proxy_node_plugin_get_handshaking_pending_data(tnet_proxy_node_t* self, void** data_pptr, tsk_size_t* data_psize)
{
thttp_proxy_node_plugin_t* node = (thttp_proxy_node_plugin_t*)self;
int ret = -1;
// input parameters already checked by the caller
tsk_safeobj_lock(node);
if (node->handshacking.pending_data_ptr && node->handshacking.pending_data_len > 0) {
if ((*data_pptr = tsk_realloc(*data_pptr, node->handshacking.pending_data_len))) {
memcpy(*data_pptr, node->handshacking.pending_data_ptr, (tsk_size_t) node->handshacking.pending_data_len);
*data_psize = (tsk_size_t) node->handshacking.pending_data_len;
ret = 0;
}
// reset the pending data. Up to the caller to hold the returned data and send it as many as required (e.g. when using unreliable transport)
TSK_FREE(node->handshacking.pending_data_ptr);
node->handshacking.pending_data_len = 0;
}
bail:
tsk_safeobj_unlock(node);
return ret;
}
static int _thttp_proxy_node_plugin_get_handshaking_completed(tnet_proxy_node_t* self, tsk_bool_t* completed)
{
thttp_proxy_node_plugin_t* node = (thttp_proxy_node_plugin_t*)self;
tsk_safeobj_lock(node);
*completed = node->handshacking.completed;
tsk_safeobj_unlock(node);
return 0;
}
static int _thttp_proxy_node_plugin_update_challenge(thttp_proxy_node_plugin_t *self, const thttp_response_t* response, tsk_bool_t answered)
{
#if THTTP_PROXY_AUTH_CACHE
tnet_proxy_node_t *base = (tnet_proxy_node_t *)self;
#endif
int ret = 0;
const thttp_header_Proxy_Authenticate_t *Proxy_Authenticate;
if (!self) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
#define kIsProxyYes tsk_true
tsk_safeobj_lock(self);
#if THTTP_PROXY_AUTH_CACHE
_thttp_proxy_auth_cache_lock(self->handshacking.proxy_auth_cache);
#endif
/* RFC 2617 - Digest Operation
* (A) The client response to a WWW-Authenticate challenge for a protection
space starts an authentication session with that protection space.
The authentication session lasts until the client receives another
WWW-Authenticate challenge from any server in the protection space.
(B) The server may return a 401 response with a new nonce value, causing the client
to retry the request; by specifying stale=TRUE with this response,
the server tells the client to retry with the new nonce, but without
prompting for a new username and password.
*/
/* RFC 2617 - 1.2 Access Authentication Framework
The realm directive (case-insensitive) is required for all authentication schemes that issue a challenge.
*/
Proxy_Authenticate = (const thttp_header_Proxy_Authenticate_t*)thttp_message_get_headerAt(response, thttp_htype_Proxy_Authenticate, 0);
if (Proxy_Authenticate) {
if (self->handshacking.challenge) {
if (tsk_striequals(self->handshacking.challenge->realm, Proxy_Authenticate->realm) && (Proxy_Authenticate->stale || !answered)) {
#if THTTP_PROXY_AUTH_CACHE
TSK_OBJECT_SAFE_FREE(self->handshacking.challenge);
self->handshacking.challenge = _thttp_proxy_auth_cache_challenge_get(self->handshacking.proxy_auth_cache,
base->proxy_host,
base->proxy_port,
Proxy_Authenticate->scheme,
Proxy_Authenticate->realm,
Proxy_Authenticate->nonce,
Proxy_Authenticate->opaque,
Proxy_Authenticate->algorithm,
Proxy_Authenticate->qop);
#else
ret = thttp_challenge_update(self->handshacking.challenge,
Proxy_Authenticate->scheme,
Proxy_Authenticate->realm,
Proxy_Authenticate->nonce,
Proxy_Authenticate->opaque,
Proxy_Authenticate->algorithm,
Proxy_Authenticate->qop);
#endif
if (ret != 0) {
goto bail;
}
}
else {
TSK_DEBUG_ERROR("Authentication failed");
ret = -1;
goto bail;
}
}
else {
#if THTTP_PROXY_AUTH_CACHE
self->handshacking.challenge = _thttp_proxy_auth_cache_challenge_get(self->handshacking.proxy_auth_cache,
base->proxy_host,
base->proxy_port,
Proxy_Authenticate->scheme,
Proxy_Authenticate->realm,
Proxy_Authenticate->nonce,
Proxy_Authenticate->opaque,
Proxy_Authenticate->algorithm,
Proxy_Authenticate->qop);
#else
self->handshacking.challenge = thttp_challenge_create(kIsProxyYes,
Proxy_Authenticate->scheme,
Proxy_Authenticate->realm,
Proxy_Authenticate->nonce,
Proxy_Authenticate->opaque,
Proxy_Authenticate->algorithm,
Proxy_Authenticate->qop);
#endif /* THTTP_PROXY_AUTH_CACHE */
if (!self->handshacking.challenge) {
ret = -1;
goto bail;
}
}
}
else {
TSK_DEBUG_ERROR("Proxy-Authenticate header is missing");
ret = -1;
goto bail;
}
bail:
#if THTTP_PROXY_AUTH_CACHE
_thttp_proxy_auth_cache_unlock(self->handshacking.proxy_auth_cache);
#endif
tsk_safeobj_unlock(self);
return ret;
}
/* constructor */
static tsk_object_t* thttp_proxy_node_plugin_ctor(tsk_object_t * self, va_list * app)
{
thttp_proxy_node_plugin_t *node = self;
if (node) {
/* init base */
tnet_proxy_node_init(TNET_PROXY_NODE(node));
/* init self */
#if THTTP_PROXY_AUTH_CACHE
node->handshacking.proxy_auth_cache = _thttp_proxy_auth_cache_get_ref();
#endif
tsk_safeobj_init(node);
TSK_DEBUG_INFO("Create HTTP(s) proxy node");
}
return self;
}
/* destructor */
static tsk_object_t* thttp_proxy_node_plugin_dtor(tsk_object_t * self)
{
thttp_proxy_node_plugin_t *node = self;
if (node) {
/* deinit base */
tnet_proxy_node_deinit(TNET_PROXY_NODE(node));
/* deinit self */
TSK_OBJECT_SAFE_FREE(node->handshacking.buff);
TSK_FREE(node->handshacking.pending_data_ptr);
TSK_OBJECT_SAFE_FREE(node->handshacking.req_connect);
TSK_OBJECT_SAFE_FREE(node->handshacking.challenge);
#if THTTP_PROXY_AUTH_CACHE
TSK_OBJECT_SAFE_FREE(node->handshacking.proxy_auth_cache);
#endif
tsk_safeobj_deinit(node);
TSK_DEBUG_INFO("*** HTTP proxy node destroyed ***");
}
return self;
}
/* object definition */
static const tsk_object_def_t thttp_proxy_node_def_s =
{
sizeof(thttp_proxy_node_plugin_t),
thttp_proxy_node_plugin_ctor,
thttp_proxy_node_plugin_dtor,
tsk_null,
};
/* plugin definition*/
static const struct tnet_proxy_node_plugin_def_s thttp_proxy_node_plugin_def_s =
{
&thttp_proxy_node_def_s,
tnet_proxy_type_http | tnet_proxy_type_https,
"HTTP(s) proxy node plugin",
_thttp_proxy_node_plugin_configure,
_thttp_proxy_node_plugin_start_handshaking,
_thttp_proxy_node_plugin_set_handshaking_data,
_thttp_proxy_node_plugin_get_handshaking_pending_data,
_thttp_proxy_node_plugin_get_handshaking_completed
};
const struct tnet_proxy_node_plugin_def_s *thttp_proxy_node_plugin_def_t = &thttp_proxy_node_plugin_def_s;
#if THTTP_PROXY_AUTH_CACHE
extern const tsk_object_def_t* thttp_proxy_auth_cache_def_t;
extern const tsk_object_def_t* thttp_proxy_auth_def_t;
typedef struct thttp_proxy_auth_s {
TSK_DECLARE_OBJECT;
char* hostname;
tnet_port_t port;
struct {
char* scheme;
char* realm;
char* nonce;
char* opaque;
char* algorithm;
char* qop;
unsigned nc;
tsk_md5string_t cnonce;
tsk_bool_t is_active;
} challenge;
}
thttp_proxy_auth_t;
typedef tsk_list_t thttp_proxy_auths_L_t;
typedef struct thttp_proxy_auth_cache_s {
TSK_DECLARE_OBJECT;
thttp_proxy_auths_L_t* auths_list;
TSK_DECLARE_SAFEOBJ;
}
thttp_proxy_auth_cache_t;
static const thttp_proxy_auth_t* _thttp_proxy_auth_find(thttp_proxy_auths_L_t* list, const char* hostname, tnet_port_t port);
static const thttp_proxy_auth_t* _thttp_proxy_auth_add(thttp_proxy_auths_L_t* list, const char* hostname, tnet_port_t port);
static int _thttp_proxy_auth_set_nc(thttp_proxy_auth_t* self, unsigned nc);
static thttp_proxy_auth_cache_t* _thttp_proxy_auth_cache_get_ref()
{
static thttp_proxy_auth_cache_t* instance = tsk_null;
if (!instance) {
instance = tsk_object_new(thttp_proxy_auth_cache_def_t);
return instance; // to have refcount = 1
}
return (thttp_proxy_auth_cache_t*)tsk_object_ref(instance);
}
static int _thttp_proxy_auth_cache_lock(thttp_proxy_auth_cache_t* self)
{
if (!self) {
TSK_DEBUG_ERROR("Invalid parameter")
return -1;
}
tsk_safeobj_lock(self);
return 0;
}
static int _thttp_proxy_auth_cache_unlock(thttp_proxy_auth_cache_t* self)
{
if (!self) {
TSK_DEBUG_ERROR("Invalid parameter")
return -1;
}
tsk_safeobj_unlock(self);
return 0;
}
static thttp_challenge_t* _thttp_proxy_auth_cache_challenge_get(thttp_proxy_auth_cache_t* self, const char* proxy_host, tnet_port_t proxy_port, const char* scheme, const char* realm, const char* nonce, const char* opaque, const char* algorithm, const char* qop)
{
if (self) {
const thttp_proxy_auth_t* proxy_auth = _thttp_proxy_auth_add(self->auths_list, proxy_host, proxy_port);
if (proxy_auth) {
if (!proxy_auth->challenge.is_active || !tsk_striequals((proxy_auth)->challenge.nonce, nonce)) {
tsk_istr_t istr;
TSK_DEBUG_INFO("HTTP proxy auth cache (%s:%d) reset with nonce = %s", proxy_host, proxy_port, nonce);
tsk_strupdate(&((thttp_proxy_auth_t*)proxy_auth)->challenge.scheme, scheme);
tsk_strupdate(&((thttp_proxy_auth_t*)proxy_auth)->challenge.realm, realm);
tsk_strupdate(&((thttp_proxy_auth_t*)proxy_auth)->challenge.nonce, nonce);
tsk_strupdate(&((thttp_proxy_auth_t*)proxy_auth)->challenge.opaque, opaque);
tsk_strupdate(&((thttp_proxy_auth_t*)proxy_auth)->challenge.algorithm, algorithm);
tsk_strupdate(&((thttp_proxy_auth_t*)proxy_auth)->challenge.qop, qop);
((thttp_proxy_auth_t*)proxy_auth)->challenge.nc = 1;
tsk_strrandom(&istr);
tsk_md5compute(istr, tsk_strlen(istr), &((thttp_proxy_auth_t*)proxy_auth)->challenge.cnonce);
((thttp_proxy_auth_t*)proxy_auth)->challenge.is_active = tsk_true;
}
}
#define kIsProxyYes tsk_true
return thttp_challenge_create(kIsProxyYes, scheme, realm, nonce, opaque, algorithm, qop);
}
return tsk_null;
}
static thttp_challenge_t* _thttp_proxy_auth_cache_challenge_get_2(thttp_proxy_auth_cache_t* self, const char* proxy_host, tnet_port_t proxy_port)
{
if (self) {
const thttp_proxy_auth_t* proxy_auth = _thttp_proxy_auth_find(self->auths_list, proxy_host, proxy_port);
if (proxy_auth && proxy_auth->challenge.is_active) {
#define kIsProxyYes tsk_true
return thttp_challenge_create(kIsProxyYes, proxy_auth->challenge.scheme, proxy_auth->challenge.realm, proxy_auth->challenge.nonce, proxy_auth->challenge.opaque, proxy_auth->challenge.algorithm, proxy_auth->challenge.qop);
}
}
return tsk_null;
}
static thttp_header_t * _thttp_proxy_auth_cache_create_header_authorization(struct thttp_proxy_auth_cache_s* self, const char* proxy_host, tnet_port_t proxy_port, const char* username, const char* password, const char* uristring)
{
thttp_challenge_t* challenge = tsk_null;
thttp_header_t * header = tsk_null;
if (!self || !uristring) {
TSK_DEBUG_ERROR("Invalid parameter");
return tsk_null;
}
challenge = _thttp_proxy_auth_cache_challenge_get_2(self, proxy_host, proxy_port);
if (challenge) {
#define kNullContentData tsk_null
const thttp_proxy_auth_t* proxy_auth = _thttp_proxy_auth_find(self->auths_list, proxy_host, proxy_port);
if (!proxy_auth) {
TSK_DEBUG_ERROR("Cannot find cached info for proxy (%s: %d)", proxy_host, proxy_port);
}
else {
challenge->nc = proxy_auth->challenge.nc;
memcpy(challenge->cnonce, proxy_auth->challenge.cnonce, sizeof(tsk_md5string_t));
header = thttp_challenge_create_header_authorization_2(challenge, username, password, "CONNECT", uristring, kNullContentData);
if (header) {
_thttp_proxy_auth_set_nc((thttp_proxy_auth_t*)proxy_auth, challenge->nc);
}
}
}
TSK_OBJECT_SAFE_FREE(challenge);
return header;
}
static int _thttp_proxy_auth_set_nc(thttp_proxy_auth_t* self, unsigned nc)
{
if (!self) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
self->challenge.nc = nc;
return 0;
}
static const thttp_proxy_auth_t* _thttp_proxy_auth_add(thttp_proxy_auths_L_t* list, const char* hostname, tnet_port_t port)
{
const thttp_proxy_auth_t* old = _thttp_proxy_auth_find(list, hostname, port);
if (!old) {
thttp_proxy_auth_t* new = tsk_object_new(thttp_proxy_auth_def_t);
if (new) {
const thttp_proxy_auth_t* newptr = new; // saveptr
new->hostname = tsk_strdup(hostname);
new->port = port;
tsk_list_push_back_data(list, (void**)&new);
TSK_OBJECT_SAFE_FREE(new);
return newptr;
}
}
return old;
}
static const thttp_proxy_auth_t* _thttp_proxy_auth_find(thttp_proxy_auths_L_t* list, const char* hostname, tnet_port_t port)
{
if (list) {
const tsk_list_item_t* item;
tsk_list_foreach(item, list) {
const thttp_proxy_auth_t* auth = (thttp_proxy_auth_t*)item->data;
if (auth->port == port && tsk_striequals(auth->hostname, hostname)) {
return auth;
}
}
}
return tsk_null;
}
static const tsk_bool_t _thttp_proxy_auth_exists(thttp_proxy_auths_L_t* list, const char* hostname, tnet_port_t port)
{
return _thttp_proxy_auth_find(list, hostname, port) ? tsk_true : tsk_false;
}
static tsk_object_t* _thttp_proxy_auth_cache_ctor(tsk_object_t * self, va_list * app)
{
thttp_proxy_auth_cache_t *cache = (thttp_proxy_auth_cache_t *)self;
if (cache) {
if (!(cache->auths_list = tsk_list_create())) {
return tsk_null;
}
tsk_safeobj_init(cache);
}
return self;
}
static tsk_object_t* _thttp_proxy_auth_cache_dtor(tsk_object_t * self)
{
thttp_proxy_auth_cache_t *cache = (thttp_proxy_auth_cache_t *)self;
if (cache) {
TSK_OBJECT_SAFE_FREE(cache->auths_list);
tsk_safeobj_deinit(cache);
}
return self;
}
static const tsk_object_def_t thttp_proxy_auth_cache_def_s =
{
sizeof(thttp_proxy_auth_cache_t),
_thttp_proxy_auth_cache_ctor,
_thttp_proxy_auth_cache_dtor,
tsk_null,
};
const tsk_object_def_t* thttp_proxy_auth_cache_def_t = &thttp_proxy_auth_cache_def_s;
static tsk_object_t* _thttp_proxy_auth_ctor(tsk_object_t * self, va_list * app)
{
thttp_proxy_auth_t *auth = (thttp_proxy_auth_t *)self;
if (auth) {
}
return self;
}
static tsk_object_t* _thttp_proxy_auth_dtor(tsk_object_t * self)
{
thttp_proxy_auth_t *auth = (thttp_proxy_auth_t *)self;
if (auth) {
TSK_FREE(auth->challenge.scheme);
TSK_FREE(auth->challenge.realm);
TSK_FREE(auth->challenge.nonce);
TSK_FREE(auth->challenge.opaque);
TSK_FREE(auth->challenge.algorithm);
TSK_FREE(auth->challenge.qop);
TSK_FREE(auth->hostname);
}
return self;
}
static const tsk_object_def_t thttp_proxy_auth_def_s =
{
sizeof(thttp_proxy_auth_t),
_thttp_proxy_auth_ctor,
_thttp_proxy_auth_dtor,
tsk_null,
};
const tsk_object_def_t* thttp_proxy_auth_def_t = &thttp_proxy_auth_def_s;
#endif /* HTTP_AUTH_CACHE */

View File

@ -1,31 +1,27 @@
/*
* Copyright (C) 2010-2011 Mamadou Diop.
*
* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DOUBANGO.
*
*/
* Copyright (C) 2010-2015 Mamadou DIOP.
*
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DOUBANGO.
*
*/
/**@file thttp_session.c
* @brief HTTP/HTTPS session.
*
* @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
*
*/
#include "tinyhttp/thttp_session.h"
@ -38,403 +34,403 @@
#include "tsk_debug.h"
/**@defgroup thttp_session_group HTTP Session
*/
*/
int thttp_session_signal(thttp_session_t *self, thttp_action_type_t atype);
/**Sets parameters.
*/
*/
int __thttp_session_set(thttp_session_t *self, va_list* app)
{
thttp_session_param_type_t curr;
if(!self){
return -1;
}
while((curr=va_arg(*app, thttp_session_param_type_t)) != httpp_null){
switch(curr){
case httpp_option:
{ /* (thttp_session_option_t)ID_ENUM, (const char*)VALUE_STR */
thttp_session_option_t id = va_arg(*app, thttp_session_option_t);
const char* value = va_arg(*app, const char *);
tsk_options_add_option(&self->options, id, value);
break;
}
case httpp_header:
{ /* (const char*)NAME_STR, (const char*)VALUE_STR */
const char* name = va_arg(*app, const char *);
const char* value = va_arg(*app, const char *);
if(value == ((const char*)-1)){ /* UNSET */
tsk_params_remove_param(self->headers, name);
}
else{ /* SET */
tsk_params_add_param(&self->headers, name, value);
}
break;
}
case httpp_cred:
{ /* (const char*)USERNAME_STR, (const char*)PASSWORD_STR */
tsk_strupdate(&self->cred.usename, va_arg(*app, const char *));
tsk_strupdate(&self->cred.password, va_arg(*app, const char *));
break;
}
case httpp_userdata:
{ /* (const void*)USERDATA_PTR */
self->userdata = va_arg(*app, const void *);
break;
}
default:
{ /* va_list will be unsafe => exit */
TSK_DEBUG_ERROR("NOT SUPPORTED.");
goto bail;
}
} /* sxitch */
} /* while */
return 0;
thttp_session_param_type_t curr;
if(!self){
return -1;
}
while((curr=va_arg(*app, thttp_session_param_type_t)) != httpp_null){
switch(curr){
case httpp_option:
{ /* (thttp_session_option_t)ID_ENUM, (const char*)VALUE_STR */
thttp_session_option_t id = va_arg(*app, thttp_session_option_t);
const char* value = va_arg(*app, const char *);
tsk_options_add_option(&self->options, id, value);
break;
}
case httpp_header:
{ /* (const char*)NAME_STR, (const char*)VALUE_STR */
const char* name = va_arg(*app, const char *);
const char* value = va_arg(*app, const char *);
if(value == ((const char*)-1)){ /* UNSET */
tsk_params_remove_param(self->headers, name);
}
else{ /* SET */
tsk_params_add_param(&self->headers, name, value);
}
break;
}
case httpp_cred:
{ /* (const char*)USERNAME_STR, (const char*)PASSWORD_STR */
tsk_strupdate(&self->cred.usename, va_arg(*app, const char *));
tsk_strupdate(&self->cred.password, va_arg(*app, const char *));
break;
}
case httpp_userdata:
{ /* (const void*)USERDATA_PTR */
self->userdata = va_arg(*app, const void *);
break;
}
default:
{ /* va_list will be unsafe => exit */
TSK_DEBUG_ERROR("NOT SUPPORTED.");
goto bail;
}
} /* sxitch */
} /* while */
return 0;
bail:
return -2;
return -2;
}
/**@ingroup thttp_session_group
* Creates new session.
* @param stack The HTTP/HTTPS @a stack to use. The @a stack shall be created using @ref thttp_stack_create.
* @param ... Any @b THTTP_SESSION_SET_*() macros. MUST ends with @ref THTTP_SESSION_SET_NULL().
* @retval A pointer to the newly created session.
* A session is a well-defined object.
*
* @code
thttp_session_handle_t * session = thttp_session_create(stack,
* Creates new session.
* @param stack The HTTP/HTTPS @a stack to use. The @a stack shall be created using @ref thttp_stack_create.
* @param ... Any @b THTTP_SESSION_SET_*() macros. MUST ends with @ref THTTP_SESSION_SET_NULL().
* @retval A pointer to the newly created session.
* A session is a well-defined object.
*
* @code
thttp_session_handle_t * session = thttp_session_create(stack,
// session-level parameters
THTTP_SESSION_SET_PARAM("timeout", "6000"),
// session-level headers
THTTP_SESSION_SET_HEADER("Pragma", "No-Cache"),
THTTP_SESSION_SET_HEADER("Connection", "Keep-Alive"),
THTTP_SESSION_SET_HEADER("User-Agent", "doubango 1.0"),
THTTP_SESSION_SET_NULL());
* @endcode
*
* @sa @ref thttp_session_set
*/
* @endcode
*
* @sa @ref thttp_session_set
*/
thttp_session_handle_t* thttp_session_create(const thttp_stack_handle_t* stack, ...)
{
thttp_session_handle_t* ret = tsk_null;
if((ret = tsk_object_new(thttp_session_def_t, stack))){
va_list ap;
va_start(ap, stack);
if(__thttp_session_set(ret, &ap)){
TSK_OBJECT_SAFE_FREE(ret);
}
va_end(ap);
}
else{
TSK_DEBUG_ERROR("failed to create new HTTP/HTTPS session.");
}
return ret;
thttp_session_handle_t* ret = tsk_null;
if((ret = tsk_object_new(thttp_session_def_t, stack))){
va_list ap;
va_start(ap, stack);
if(__thttp_session_set(ret, &ap)){
TSK_OBJECT_SAFE_FREE(ret);
}
va_end(ap);
}
else{
TSK_DEBUG_ERROR("failed to create new HTTP/HTTPS session.");
}
return ret;
}
/**@ingroup thttp_session_group
* Updates the session parameters.
* @param self The session to update. The session shall be created using @ref thttp_session_create().
* @param ... Any @b THTTP_SESSION_SET_*() macros. MUST ends with @ref THTTP_SESSION_SET_NULL().
* @retval Zero if succeed and non zero error code otherwise.
*
* @code
int ret = thttp_session_set(session,
* Updates the session parameters.
* @param self The session to update. The session shall be created using @ref thttp_session_create().
* @param ... Any @b THTTP_SESSION_SET_*() macros. MUST ends with @ref THTTP_SESSION_SET_NULL().
* @retval Zero if succeed and non zero error code otherwise.
*
* @code
int ret = thttp_session_set(session,
// session-level parameters
THTTP_SESSION_SET_OPTION(THTTP_SESSION_OPTION_TIMEOUT, "6000"),
// session-level headers
THTTP_SESSION_SET_HEADER("Pragma", "No-Cache"),
THTTP_SESSION_SET_HEADER("Connection", "Keep-Alive"),
THTTP_SESSION_SET_HEADER("User-Agent", "doubango 1.0"),
THTTP_SESSION_SET_NULL());
* @endcode
*
* @sa @ref thttp_session_create
*/
* @endcode
*
* @sa @ref thttp_session_create
*/
int thttp_session_set(thttp_session_handle_t *self, ...)
{
if(self){
int ret;
va_list ap;
thttp_session_t *session = self;
if(session->id == THTTP_SESSION_INVALID_ID){
TSK_DEBUG_ERROR("Using invalid session.");
return -2;
}
va_start(ap, self);
ret = __thttp_session_set(session, &ap);
va_end(ap);
return ret;
}
return -1;
if(self){
int ret;
va_list ap;
thttp_session_t *session = self;
if(session->id == THTTP_SESSION_INVALID_ID){
TSK_DEBUG_ERROR("Using invalid session.");
return -2;
}
va_start(ap, self);
ret = __thttp_session_set(session, &ap);
va_end(ap);
return ret;
}
return -1;
}
/**@ingroup thttp_session_group
* Gets the session id.
* @param self The session for which to get the id.
* @retval The id of the session.
*/
* Gets the session id.
* @param self The session for which to get the id.
* @retval The id of the session.
*/
thttp_session_id_t thttp_session_get_id(const thttp_session_handle_t *self)
{
const thttp_session_t *session = self;
if(session){
return session->id;
}
return THTTP_SESSION_INVALID_ID;
const thttp_session_t *session = self;
if(session){
return session->id;
}
return THTTP_SESSION_INVALID_ID;
}
/**@ingroup thttp_session_group
* Gets the user context (user/application data).
* @param self A pointer to the session from which to get the context.
* @retval A pointer to the context. Previously defined by using @ref THTTP_SESSION_SET_USERDATA() macro.
* @sa @ref THTTP_SESSION_SET_USERDATA()
*/
* Gets the user context (user/application data).
* @param self A pointer to the session from which to get the context.
* @retval A pointer to the context. Previously defined by using @ref THTTP_SESSION_SET_USERDATA() macro.
* @sa @ref THTTP_SESSION_SET_USERDATA()
*/
const void* thttp_session_get_userdata(const thttp_session_handle_t *self)
{
if(self){
return ((const thttp_session_t*)self)->userdata;
}
return tsk_null;
if(self){
return ((const thttp_session_t*)self)->userdata;
}
return tsk_null;
}
int thttp_session_closefd(thttp_session_handle_t *_self)
{
int ret = 0;
thttp_session_t* self = _self;
if(self->fd != TNET_INVALID_FD){
if((ret = tnet_transport_remove_socket(self->stack->transport, &self->fd))){
ret = tnet_sockfd_close(&self->fd);
}
}
return ret;
int ret = 0;
thttp_session_t* self = _self;
if(self->fd != TNET_INVALID_FD){
if((ret = tnet_transport_remove_socket(self->stack->transport, &self->fd))){
ret = tnet_sockfd_close(&self->fd);
}
}
return ret;
}
/** Updates authentications headers.
*/
*/
int thttp_session_update_challenges(thttp_session_t *self, const thttp_response_t* response, tsk_bool_t answered)
{
int ret = 0;
tsk_size_t i;
tsk_list_item_t *item;
thttp_challenge_t *challenge;
const thttp_header_WWW_Authenticate_t *WWW_Authenticate;
const thttp_header_Proxy_Authenticate_t *Proxy_Authenticate;
tsk_safeobj_lock(self);
/* RFC 2617 - Digest Operation
* (A) The client response to a WWW-Authenticate challenge for a protection
space starts an authentication session with that protection space.
The authentication session lasts until the client receives another
WWW-Authenticate challenge from any server in the protection space.
(B) The server may return a 401 response with a new nonce value, causing the client
to retry the request; by specifying stale=TRUE with this response,
the server tells the client to retry with the new nonce, but without
prompting for a new username and password.
*/
/* RFC 2617 - 1.2 Access Authentication Framework
The realm directive (case-insensitive) is required for all authentication schemes that issue a challenge.
*/
/* FIXME: As we perform the same task ==> Use only one loop.
*/
for(i =0; (WWW_Authenticate = (const thttp_header_WWW_Authenticate_t*)thttp_message_get_headerAt(response, thttp_htype_WWW_Authenticate, i)); i++)
{
tsk_bool_t isnew = tsk_true;
tsk_list_foreach(item, self->challenges)
{
challenge = item->data;
if(challenge->isproxy){
continue;
}
if(tsk_striequals(challenge->realm, WWW_Authenticate->realm) && (WWW_Authenticate->stale || !answered)){
/*== (B) ==*/
if((ret = thttp_challenge_update(challenge,
WWW_Authenticate->scheme,
WWW_Authenticate->realm,
WWW_Authenticate->nonce,
WWW_Authenticate->opaque,
WWW_Authenticate->algorithm,
WWW_Authenticate->qop))){
return ret;
}
else{
isnew = tsk_false;
continue;
}
}
else{
ret = -1;
goto bail;
}
}
if(isnew){
if((challenge = thttp_challenge_create(tsk_false, /* Not proxy */
WWW_Authenticate->scheme,
WWW_Authenticate->realm,
WWW_Authenticate->nonce,
WWW_Authenticate->opaque,
WWW_Authenticate->algorithm,
WWW_Authenticate->qop))){
tsk_list_push_back_data(self->challenges, (void**)&challenge);
}
else{
ret = -1;
goto bail;
}
}
}
for(i=0; (Proxy_Authenticate = (const thttp_header_Proxy_Authenticate_t*)thttp_message_get_headerAt(response, thttp_htype_Proxy_Authenticate, i)); i++)
{
tsk_bool_t isnew = tsk_true;
tsk_list_foreach(item, self->challenges){
challenge = item->data;
if(!challenge->isproxy){
continue;
}
if(tsk_striequals(challenge->realm, Proxy_Authenticate->realm) && (Proxy_Authenticate->stale || !answered)){
/*== (B) ==*/
if((ret = thttp_challenge_update(challenge,
Proxy_Authenticate->scheme,
Proxy_Authenticate->realm,
Proxy_Authenticate->nonce,
Proxy_Authenticate->opaque,
Proxy_Authenticate->algorithm,
Proxy_Authenticate->qop)))
{
goto bail;
}
else{
isnew = tsk_false;
continue;
}
}
else{
ret = -1;
goto bail;
}
}
if(isnew){
if((challenge = thttp_challenge_create(tsk_true, /* Proxy */
Proxy_Authenticate->scheme,
Proxy_Authenticate->realm,
Proxy_Authenticate->nonce,
Proxy_Authenticate->opaque,
Proxy_Authenticate->algorithm,
Proxy_Authenticate->qop)))
{
tsk_list_push_back_data(self->challenges, (void**)&challenge);
}
else{
ret = -1;
goto bail;
}
}
}
int ret = 0;
tsk_size_t i;
tsk_list_item_t *item;
thttp_challenge_t *challenge;
const thttp_header_WWW_Authenticate_t *WWW_Authenticate;
const thttp_header_Proxy_Authenticate_t *Proxy_Authenticate;
tsk_safeobj_lock(self);
/* RFC 2617 - Digest Operation
* (A) The client response to a WWW-Authenticate challenge for a protection
space starts an authentication session with that protection space.
The authentication session lasts until the client receives another
WWW-Authenticate challenge from any server in the protection space.
(B) The server may return a 401 response with a new nonce value, causing the client
to retry the request; by specifying stale=TRUE with this response,
the server tells the client to retry with the new nonce, but without
prompting for a new username and password.
*/
/* RFC 2617 - 1.2 Access Authentication Framework
The realm directive (case-insensitive) is required for all authentication schemes that issue a challenge.
*/
/* FIXME: As we perform the same task ==> Use only one loop.
*/
for(i =0; (WWW_Authenticate = (const thttp_header_WWW_Authenticate_t*)thttp_message_get_headerAt(response, thttp_htype_WWW_Authenticate, i)); i++)
{
tsk_bool_t isnew = tsk_true;
tsk_list_foreach(item, self->challenges)
{
challenge = item->data;
if(challenge->isproxy){
continue;
}
if(tsk_striequals(challenge->realm, WWW_Authenticate->realm) && (WWW_Authenticate->stale || !answered)){
/*== (B) ==*/
if ((ret = thttp_challenge_update(challenge,
WWW_Authenticate->scheme,
WWW_Authenticate->realm,
WWW_Authenticate->nonce,
WWW_Authenticate->opaque,
WWW_Authenticate->algorithm,
WWW_Authenticate->qop)) != 0) {
goto bail;
}
else{
isnew = tsk_false;
continue;
}
}
else{
ret = -1;
goto bail;
}
}
if(isnew){
if((challenge = thttp_challenge_create(tsk_false, /* Not proxy */
WWW_Authenticate->scheme,
WWW_Authenticate->realm,
WWW_Authenticate->nonce,
WWW_Authenticate->opaque,
WWW_Authenticate->algorithm,
WWW_Authenticate->qop))){
tsk_list_push_back_data(self->challenges, (void**)&challenge);
}
else{
ret = -1;
goto bail;
}
}
}
for(i=0; (Proxy_Authenticate = (const thttp_header_Proxy_Authenticate_t*)thttp_message_get_headerAt(response, thttp_htype_Proxy_Authenticate, i)); i++)
{
tsk_bool_t isnew = tsk_true;
tsk_list_foreach(item, self->challenges){
challenge = item->data;
if(!challenge->isproxy){
continue;
}
if(tsk_striequals(challenge->realm, Proxy_Authenticate->realm) && (Proxy_Authenticate->stale || !answered)){
/*== (B) ==*/
if ((ret = thttp_challenge_update(challenge,
Proxy_Authenticate->scheme,
Proxy_Authenticate->realm,
Proxy_Authenticate->nonce,
Proxy_Authenticate->opaque,
Proxy_Authenticate->algorithm,
Proxy_Authenticate->qop)) != 0)
{
goto bail;
}
else{
isnew = tsk_false;
continue;
}
}
else{
ret = -1;
goto bail;
}
}
if(isnew){
if((challenge = thttp_challenge_create(tsk_true, /* Proxy */
Proxy_Authenticate->scheme,
Proxy_Authenticate->realm,
Proxy_Authenticate->nonce,
Proxy_Authenticate->opaque,
Proxy_Authenticate->algorithm,
Proxy_Authenticate->qop)))
{
tsk_list_push_back_data(self->challenges, (void**)&challenge);
}
else{
ret = -1;
goto bail;
}
}
}
bail:
tsk_safeobj_unlock(self);
return ret;
tsk_safeobj_unlock(self);
return ret;
}
/* internal function */
int thttp_session_signal(thttp_session_t *self, thttp_action_type_t atype)
{
tsk_list_item_t *item;
if(!self){
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
tsk_safeobj_lock(self);
tsk_list_item_t *item;
if(!self){
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
tsk_safeobj_lock(self);
again:
tsk_list_foreach(item, self->dialogs){
item = tsk_object_ref(item);
thttp_dialog_fsm_act((thttp_dialog_t*)item->data, atype, tsk_null, tsk_null);
/* As the above action could terminate the dialog (which means change the content of self->dialogs)
* => list becomes unsafe */
if(!(item = tsk_object_unref(item))){
goto again;
}
}
switch(atype){
case thttp_thttp_atype_closed:
self->fd = TNET_INVALID_FD;
break;
default:
break;
}
tsk_safeobj_unlock(self);
return 0;
tsk_list_foreach(item, self->dialogs){
item = tsk_object_ref(item);
thttp_dialog_fsm_act((thttp_dialog_t*)item->data, atype, tsk_null, tsk_null);
/* As the above action could terminate the dialog (which means change the content of self->dialogs)
* => list becomes unsafe */
if(!(item = tsk_object_unref(item))){
goto again;
}
}
switch(atype){
case thttp_thttp_atype_closed:
self->fd = TNET_INVALID_FD;
break;
default:
break;
}
tsk_safeobj_unlock(self);
return 0;
}
/** Signals to all dialogs that the connection have been closed. */
int thttp_session_signal_closed(thttp_session_t *self)
{
return thttp_session_signal(self, thttp_thttp_atype_closed);
return thttp_session_signal(self, thttp_thttp_atype_closed);
}
/** Signals to all dialogss that we got an error */
int thttp_session_signal_error(thttp_session_t *self)
{
return thttp_session_signal(self, thttp_atype_error);
return thttp_session_signal(self, thttp_atype_error);
}
/** Retrieves a session by fd */
thttp_session_t* thttp_session_get_by_fd(thttp_sessions_L_t* sessions, tnet_fd_t fd)
{
thttp_session_t* ret = tsk_null;
const tsk_list_item_t *item;
if(!sessions){
goto bail;
}
tsk_list_foreach(item, sessions){
if(((thttp_session_t*)item->data)->fd == fd){
ret = tsk_object_ref(item->data);
goto bail;
}
}
thttp_session_t* ret = tsk_null;
const tsk_list_item_t *item;
if(!sessions){
goto bail;
}
tsk_list_foreach(item, sessions){
if(((thttp_session_t*)item->data)->fd == fd){
ret = tsk_object_ref(item->data);
goto bail;
}
}
bail:
return ret;
return ret;
}
@ -445,78 +441,78 @@ bail:
//
static tsk_object_t* _thttp_session_create(tsk_object_t * self, va_list * app)
{
thttp_session_t *session = self;
static thttp_session_id_t unique_id = THTTP_SESSION_INVALID_ID;
if(session){
tsk_safeobj_init(session);
session->stack = va_arg(*app, const thttp_stack_handle_t*);
session->options = tsk_list_create();
session->headers = tsk_list_create();
session->challenges = tsk_list_create();
session->dialogs = tsk_list_create();
session->fd = TNET_INVALID_FD;
session->id = THTTP_SESSION_INVALID_ID;
/* add the session to the stack */
if(session->stack){
session->id = ++unique_id;
tsk_list_push_back_data(session->stack->sessions, (void**)&session);
}
}
return self;
thttp_session_t *session = self;
static thttp_session_id_t unique_id = THTTP_SESSION_INVALID_ID;
if(session){
tsk_safeobj_init(session);
session->stack = va_arg(*app, const thttp_stack_handle_t*);
session->options = tsk_list_create();
session->headers = tsk_list_create();
session->challenges = tsk_list_create();
session->dialogs = tsk_list_create();
session->fd = TNET_INVALID_FD;
session->id = THTTP_SESSION_INVALID_ID;
/* add the session to the stack */
if(session->stack){
session->id = ++unique_id;
tsk_list_push_back_data(session->stack->sessions, (void**)&session);
}
}
return self;
}
static tsk_object_t* thttp_session_destroy(tsk_object_t * self)
{
thttp_session_t *session = self;
if(session){
TSK_DEBUG_INFO("*** HTTP/HTTPS Session destroyed ***");
/* remove from the stack */
if(session->stack){
tsk_list_remove_item_by_data(session->stack->sessions, session);
}
TSK_OBJECT_SAFE_FREE(session->options);
TSK_OBJECT_SAFE_FREE(session->headers);
TSK_OBJECT_SAFE_FREE(session->challenges);
TSK_OBJECT_SAFE_FREE(session->dialogs);
// cred
TSK_FREE(session->cred.usename);
TSK_FREE(session->cred.password);
// fd
if(session->fd != TNET_INVALID_FD){
if(tnet_transport_remove_socket(session->stack->transport, &session->fd)){
tnet_sockfd_close(&session->fd);
}
}
tsk_safeobj_deinit(session);
}
return self;
thttp_session_t *session = self;
if(session){
TSK_DEBUG_INFO("*** HTTP/HTTPS Session destroyed ***");
/* remove from the stack */
if(session->stack){
tsk_list_remove_item_by_data(session->stack->sessions, session);
}
TSK_OBJECT_SAFE_FREE(session->options);
TSK_OBJECT_SAFE_FREE(session->headers);
TSK_OBJECT_SAFE_FREE(session->challenges);
TSK_OBJECT_SAFE_FREE(session->dialogs);
// cred
TSK_FREE(session->cred.usename);
TSK_FREE(session->cred.password);
// fd
if(session->fd != TNET_INVALID_FD){
if(tnet_transport_remove_socket(session->stack->transport, &session->fd)){
tnet_sockfd_close(&session->fd);
}
}
tsk_safeobj_deinit(session);
}
return self;
}
static int thttp_session_cmp(const tsk_object_t *_session1, const tsk_object_t *_session2)
{
const thttp_session_t *session1 = _session1;
const thttp_session_t *session2 = _session2;
if(session1 && session2){
return (int)(session1->id-session2->id);
}
return -1;
const thttp_session_t *session1 = _session1;
const thttp_session_t *session2 = _session2;
if(session1 && session2){
return (int)(session1->id-session2->id);
}
return -1;
}
static const tsk_object_def_t thttp_session_def_s =
{
sizeof(thttp_session_t),
_thttp_session_create,
thttp_session_destroy,
thttp_session_cmp,
sizeof(thttp_session_t),
_thttp_session_create,
thttp_session_destroy,
thttp_session_cmp,
};
const tsk_object_def_t *thttp_session_def_t = &thttp_session_def_s;

View File

@ -1,7 +1,5 @@
/*
* Copyright (C) 2010-2011 Mamadou Diop.
*
* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
* Copyright (C) 2010-2015 Mamadou DIOP.
*
* This file is part of Open Source Doubango Framework.
*
@ -23,8 +21,6 @@
/**@file tmedia_codec.h
* @brief Base codec object.
*
* @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
*
*/
#ifndef TINYMEDIA_CODEC_H
#define TINYMEDIA_CODEC_H
@ -170,7 +166,9 @@ tmedia_codec_id_t;
/**Max number of plugins (codec types) we can create */
#define TMED_CODEC_MAX_PLUGINS 0xFF
#if !defined(TMED_CODEC_MAX_PLUGINS)
# define TMED_CODEC_MAX_PLUGINS 0xFF
#endif
/** cast any pointer to @ref tmedia_codec_t* object */
#define TMEDIA_CODEC(self) ((tmedia_codec_t*)(self))

View File

@ -136,6 +136,10 @@ TINYMEDIA_API int tmedia_defaults_set_ssl_certs(const char* priv_path, const cha
TINYMEDIA_API int tmedia_defaults_get_ssl_certs(const char** priv_path, const char** pub_path, const char** ca_path, tsk_bool_t *verify);
TINYMEDIA_API int tmedia_defaults_set_max_fds(int32_t max_fds);
TINYMEDIA_API tsk_size_t tmedia_defaults_get_max_fds();
TINYMEDIA_API int tmedia_defaults_set_webproxy_auto_detect(tsk_bool_t auto_detect);
TINYMEDIA_API tsk_bool_t tmedia_defaults_get_webproxy_auto_detect();
TINYMEDIA_API int tmedia_defaults_set_webproxy_info(const char* type, const char* host, unsigned short port, const char* login, const char* password);
TINYMEDIA_API int tmedia_defaults_get_webproxy_info(const char** type, const char** host, unsigned short* port, const char** login, const char** password);
TMEDIA_END_DECLS

View File

@ -1,7 +1,5 @@
/*
* Copyright (C) 2010-2011 Mamadou Diop.
*
* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
* Copyright (C) 2010-2015 Mamadou DIOP.
*
* This file is part of Open Source Doubango Framework.
*
@ -22,8 +20,6 @@
/**@file tmedia_denoise.h
* @brief Denoiser (Noise suppression, AGC, AEC, VAD) Plugin
*
* @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
*/
#ifndef TINYMEDIA_DENOISE_H
#define TINYMEDIA_DENOISE_H

View File

@ -35,6 +35,22 @@
# endif
#endif
// OS X or iOS
#if defined(__APPLE__)
# define TMEDIA_UNDER_APPLE 1
# include <TargetConditionals.h>
# include <Availability.h>
#endif
#if TARGET_OS_MAC
# define TMEDIA_UNDER_MAC 1
#endif
#if TARGET_OS_IPHONE
# define TMEDIA_UNDER_IPHONE 1
#endif
#if TARGET_IPHONE_SIMULATOR
# define TMEDIA_UNDER_IPHONE_SIMULATOR 1
#endif
#if (TMEDIA_UNDER_WINDOWS || defined(__SYMBIAN32__)) && defined(TINYMEDIA_EXPORTS)
# define TINYMEDIA_API __declspec(dllexport)
# define TINYMEDIA_GEXTERN extern __declspec(dllexport)

View File

@ -51,7 +51,7 @@ static int32_t __sx = -1;
static int32_t __sy = -1;
static int32_t __audio_producer_gain = 0;
static int32_t __audio_consumer_gain = 0;
static int32_t __audio_channels_playback = 1;
static int32_t __audio_channels_playback = 1;
static int32_t __audio_channels_record = 1;
static int32_t __audio_ptime = 20;
static uint16_t __rtp_port_range_start = 1024;
@ -84,11 +84,17 @@ static tsk_size_t __avpf_tail_max = 160; // Max size for tail used to honor RTCP
static tmedia_mode_t __avpf_mode = tmedia_mode_optional; // Whether to use AVPF instead of AVP or negotiate. FIXME
static uint32_t __opus_maxcapturerate = 16000; // supported: 8k,12k,16k,24k,48k. IMPORTANT: only 8k and 16k will work with WebRTC AEC
static uint32_t __opus_maxplaybackrate = 48000; // supported: 8k,12k,16k,24k,48k
static char* __ssl_certs_priv_path = tsk_null;
static char* __ssl_certs_pub_path = tsk_null;
static char* __ssl_certs_ca_path = tsk_null;
static char* __ssl_certs_priv_path = tsk_null;
static char* __ssl_certs_pub_path = tsk_null;
static char* __ssl_certs_ca_path = tsk_null;
static tsk_bool_t __ssl_certs_verify = tsk_false;
static tsk_size_t __max_fds = 0; // Maximum number of FDs this process is allowed to open. Zero to disable.
static tsk_bool_t __webproxy_auto_detect = tsk_false;
static char* __webproxy_type = tsk_null;
static char* __webproxy_host = tsk_null;
static unsigned short __webproxy_port = 0;
static char* __webproxy_login = tsk_null;
static char* __webproxy_password = tsk_null;
int tmedia_defaults_set_profile(tmedia_profile_t profile){
__profile = profile;
@ -108,22 +114,22 @@ tmedia_bandwidth_level_t tmedia_defaults_get_bl(){
return __bl;
}
int tmedia_defaults_set_congestion_ctrl_enabled(tsk_bool_t enabled){
__congestion_ctrl_enabled = enabled;
return 0;
}
int tmedia_defaults_set_congestion_ctrl_enabled(tsk_bool_t enabled){
__congestion_ctrl_enabled = enabled;
return 0;
}
tsk_bool_t tmedia_defaults_get_congestion_ctrl_enabled(){
return __congestion_ctrl_enabled;
}
int tmedia_defaults_set_video_fps(int32_t video_fps){
if(video_fps > 0 && video_fps <= 120){
__video_fps = video_fps;
return 0;
}
TSK_DEBUG_ERROR("%d not valid for video fps", video_fps);
return -1;
}
int tmedia_defaults_set_video_fps(int32_t video_fps){
if(video_fps > 0 && video_fps <= 120){
__video_fps = video_fps;
return 0;
}
TSK_DEBUG_ERROR("%d not valid for video fps", video_fps);
return -1;
}
int32_t tmedia_defaults_get_video_fps(){
return __video_fps;
}
@ -146,10 +152,10 @@ int32_t tmedia_defaults_get_bandwidth_video_upload_max(){
return __bw_video_up_max_kbps;
}
int tmedia_defaults_set_bandwidth_video_download_max(int32_t bw_video_down_max_kbps){
int tmedia_defaults_set_bandwidth_video_download_max(int32_t bw_video_down_max_kbps){
__bw_video_down_max_kbps = bw_video_down_max_kbps > 0 ? bw_video_down_max_kbps : INT_MAX;
return 0;
}
return 0;
}
int32_t tmedia_defaults_get_bandwidth_video_download_max(){
return __bw_video_down_max_kbps;
}
@ -276,27 +282,27 @@ int32_t tmedia_defaults_get_screen_y(){
return __sy;
}
int tmedia_defaults_set_audio_ptime(int32_t audio_ptime){
if(audio_ptime > 0){
__audio_ptime = audio_ptime;
return 0;
}
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
int32_t tmedia_defaults_get_audio_ptime(){
return __audio_ptime;
}
int tmedia_defaults_set_audio_channels(int32_t channels_playback, int32_t channels_record){
if(channels_playback != 1 && channels_playback != 2) { TSK_DEBUG_ERROR("Invalid parameter"); return -1; }
if(channels_record != 1 && channels_record != 2) { TSK_DEBUG_ERROR("Invalid parameter"); return -1; }
__audio_channels_playback = channels_playback;
__audio_channels_record = channels_record;
return 0;
}
int32_t tmedia_defaults_get_audio_channels_playback(){
return __audio_channels_playback;
}
int tmedia_defaults_set_audio_ptime(int32_t audio_ptime){
if(audio_ptime > 0){
__audio_ptime = audio_ptime;
return 0;
}
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
int32_t tmedia_defaults_get_audio_ptime(){
return __audio_ptime;
}
int tmedia_defaults_set_audio_channels(int32_t channels_playback, int32_t channels_record){
if(channels_playback != 1 && channels_playback != 2) { TSK_DEBUG_ERROR("Invalid parameter"); return -1; }
if(channels_record != 1 && channels_record != 2) { TSK_DEBUG_ERROR("Invalid parameter"); return -1; }
__audio_channels_playback = channels_playback;
__audio_channels_record = channels_record;
return 0;
}
int32_t tmedia_defaults_get_audio_channels_playback(){
return __audio_channels_playback;
}
int32_t tmedia_defaults_get_audio_channels_record(){
return __audio_channels_record;
}
@ -357,18 +363,18 @@ int32_t tmedia_defaults_get_volume(){
return __volume;
}
int tmedia_producer_set_friendly_name(tmedia_type_t media_type, const char* friendly_name) {
if(media_type != tmedia_audio && media_type != tmedia_video && media_type != tmedia_bfcp_video) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
tsk_strupdate(&__producer_friendly_name[(media_type == tmedia_audio) ? 0 : (media_type == tmedia_bfcp_video ? 2 : 1)], friendly_name);
return 0;
}
int tmedia_producer_set_friendly_name(tmedia_type_t media_type, const char* friendly_name) {
if(media_type != tmedia_audio && media_type != tmedia_video && media_type != tmedia_bfcp_video) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
tsk_strupdate(&__producer_friendly_name[(media_type == tmedia_audio) ? 0 : (media_type == tmedia_bfcp_video ? 2 : 1)], friendly_name);
return 0;
}
const char* tmedia_producer_get_friendly_name(tmedia_type_t media_type){
if(media_type != tmedia_audio && media_type != tmedia_video && media_type != tmedia_bfcp_video) {
TSK_DEBUG_ERROR("Invalid parameter");
return tsk_null;
if(media_type != tmedia_audio && media_type != tmedia_video && media_type != tmedia_bfcp_video) {
TSK_DEBUG_ERROR("Invalid parameter");
return tsk_null;
}
return __producer_friendly_name[(media_type == tmedia_audio) ? 0 : (media_type == tmedia_bfcp_video ? 2 : 1)];
}
@ -425,49 +431,49 @@ int tmedia_defaults_set_rtcpmux_enabled(tsk_bool_t rtcpmux_enabled){
return 0;
}
int tmedia_defaults_set_stun_server(const char* server_ip, uint16_t server_port){
tsk_strupdate(&__stun_server_ip, server_ip);
__stun_server_port = server_port;
return 0;
}
int tmedia_defaults_get_stun_server(const char** server_ip, uint16_t*const server_port){
static const char* __stun_server_ip_default = "numb.viagenie.ca"; // default server for backward compatibility
if(server_ip) *server_ip = tsk_strnullORempty(__stun_server_ip) ? __stun_server_ip_default : __stun_server_ip;
if(server_port) *server_port = __stun_server_port;
return 0;
}
int tmedia_defaults_set_stun_cred(const char* usr_name, const char* usr_pwd) {
tsk_strupdate(&__stun_usr_name, usr_name);
tsk_strupdate(&__stun_usr_pwd, usr_pwd);
return 0;
}
int tmedia_defaults_get_stun_cred(const char** usr_name, const char** usr_pwd){
if(usr_name) *usr_name = __stun_usr_name;
if(usr_pwd) *usr_pwd = __stun_usr_pwd;
return 0;
}
int tmedia_defaults_set_stun_enabled(tsk_bool_t stun_enabled){
__stun_enabled = stun_enabled;
return 0;
}
tsk_bool_t tmedia_defaults_get_stun_enabled(){
return __stun_enabled;
}
int tmedia_defaults_set_icestun_enabled(tsk_bool_t icestun_enabled){
__icestun_enabled = icestun_enabled;
return 0;
}
int tmedia_defaults_set_stun_server(const char* server_ip, uint16_t server_port){
tsk_strupdate(&__stun_server_ip, server_ip);
__stun_server_port = server_port;
return 0;
}
int tmedia_defaults_get_stun_server(const char** server_ip, uint16_t*const server_port){
static const char* __stun_server_ip_default = "numb.viagenie.ca"; // default server for backward compatibility
if(server_ip) *server_ip = tsk_strnullORempty(__stun_server_ip) ? __stun_server_ip_default : __stun_server_ip;
if(server_port) *server_port = __stun_server_port;
return 0;
}
int tmedia_defaults_set_stun_cred(const char* usr_name, const char* usr_pwd) {
tsk_strupdate(&__stun_usr_name, usr_name);
tsk_strupdate(&__stun_usr_pwd, usr_pwd);
return 0;
}
int tmedia_defaults_get_stun_cred(const char** usr_name, const char** usr_pwd){
if(usr_name) *usr_name = __stun_usr_name;
if(usr_pwd) *usr_pwd = __stun_usr_pwd;
return 0;
}
int tmedia_defaults_set_stun_enabled(tsk_bool_t stun_enabled){
__stun_enabled = stun_enabled;
return 0;
}
tsk_bool_t tmedia_defaults_get_stun_enabled(){
return __stun_enabled;
}
int tmedia_defaults_set_icestun_enabled(tsk_bool_t icestun_enabled){
__icestun_enabled = icestun_enabled;
return 0;
}
tsk_bool_t tmedia_defaults_get_icestun_enabled(){
return __icestun_enabled;
}
int tmedia_defaults_set_iceturn_enabled(tsk_bool_t iceturn_enabled){
__iceturn_enabled = iceturn_enabled;
return 0;
}
int tmedia_defaults_set_iceturn_enabled(tsk_bool_t iceturn_enabled){
__iceturn_enabled = iceturn_enabled;
return 0;
}
tsk_bool_t tmedia_defaults_get_iceturn_enabled(){
return __iceturn_enabled;
}
@ -532,10 +538,10 @@ tsk_size_t tmedia_defaults_get_avpf_tail_max(){
return __avpf_tail_max;
}
int tmedia_defaults_set_avpf_mode(enum tmedia_mode_e mode) {
__avpf_mode = mode;
return 0;
}
int tmedia_defaults_set_avpf_mode(enum tmedia_mode_e mode) {
__avpf_mode = mode;
return 0;
}
enum tmedia_mode_e tmedia_defaults_get_avpf_mode() {
return __avpf_mode;
}
@ -560,28 +566,53 @@ uint32_t tmedia_defaults_get_opus_maxplaybackrate(){
return __opus_maxplaybackrate;
}
int tmedia_defaults_set_ssl_certs(const char* priv_path, const char* pub_path, const char* ca_path, tsk_bool_t verify){
tsk_strupdate(&__ssl_certs_priv_path, priv_path);
tsk_strupdate(&__ssl_certs_pub_path, pub_path);
tsk_strupdate(&__ssl_certs_ca_path, ca_path);
__ssl_certs_verify = verify;
return 0;
}
int tmedia_defaults_get_ssl_certs(const char** priv_path, const char** pub_path, const char** ca_path, tsk_bool_t *verify){
if(priv_path) *priv_path = __ssl_certs_priv_path;
if(pub_path) *pub_path = __ssl_certs_pub_path;
if(ca_path) *ca_path = __ssl_certs_ca_path;
if(verify) *verify = __ssl_certs_verify;
return 0;
}
int tmedia_defaults_set_max_fds(int32_t max_fds) {
if (max_fds > 0 && max_fds < 0xFFFF) {
__max_fds = (tsk_size_t)max_fds;
return 0;
}
return -1;
}
tsk_size_t tmedia_defaults_get_max_fds() {
return __max_fds;
}
int tmedia_defaults_set_ssl_certs(const char* priv_path, const char* pub_path, const char* ca_path, tsk_bool_t verify){
tsk_strupdate(&__ssl_certs_priv_path, priv_path);
tsk_strupdate(&__ssl_certs_pub_path, pub_path);
tsk_strupdate(&__ssl_certs_ca_path, ca_path);
__ssl_certs_verify = verify;
return 0;
}
int tmedia_defaults_get_ssl_certs(const char** priv_path, const char** pub_path, const char** ca_path, tsk_bool_t *verify){
if(priv_path) *priv_path = __ssl_certs_priv_path;
if(pub_path) *pub_path = __ssl_certs_pub_path;
if(ca_path) *ca_path = __ssl_certs_ca_path;
if(verify) *verify = __ssl_certs_verify;
return 0;
}
int tmedia_defaults_set_max_fds(int32_t max_fds) {
if (max_fds > 0 && max_fds < 0xFFFF) {
__max_fds = (tsk_size_t)max_fds;
return 0;
}
return -1;
}
tsk_size_t tmedia_defaults_get_max_fds() {
return __max_fds;
}
int tmedia_defaults_set_webproxy_auto_detect(tsk_bool_t auto_detect) {
__webproxy_auto_detect = auto_detect;
return 0;
}
tsk_bool_t tmedia_defaults_get_webproxy_auto_detect() {
return __webproxy_auto_detect;
}
int tmedia_defaults_set_webproxy_info(const char* type, const char* host, unsigned short port, const char* login, const char* password) {
tsk_strupdate(&__webproxy_type, type);
tsk_strupdate(&__webproxy_host, host);
tsk_strupdate(&__webproxy_login, login);
tsk_strupdate(&__webproxy_password, password);
__webproxy_port = port;
return 0;
}
int tmedia_defaults_get_webproxy_info(const char** type, const char** host, unsigned short* port, const char** login, const char** password) {
if (type) *type = __webproxy_type;
if (host) *host = __webproxy_host;
if (port) *port = __webproxy_port;
if (login) *login = __webproxy_login;
if (password) *password = __webproxy_password;
return 0;
}

View File

@ -16,6 +16,9 @@ libtinyNET_la_SOURCES = \
src/tnet_auth.c\
src/tnet_endianness.c\
src/tnet_nat.c\
src/tnet_proxy_node_socks_plugin.c\
src/tnet_proxy_plugin.c\
src/tnet_proxydetect.c\
src/tnet_poll.c\
src/tnet_socket.c\
src/tnet_transport.c\

View File

@ -31,6 +31,7 @@
#include "tnet_utils.h"
#include "tnet_endianness.h"
#include "tnet_transport.h"
#include "tnet_proxydetect.h"
#include "stun/tnet_stun.h"
#include "stun/tnet_stun_message.h"
@ -267,6 +268,12 @@ typedef struct tnet_ice_ctx_s
tsk_bool_t verify;
} ssl;
struct {
tsk_bool_t auto_detect;
struct tnet_proxyinfo_s* info;
}
proxy;
struct {
tsk_condwait_handle_t* condwait;
struct tnet_turn_session_s* ss_nominated_rtp;
@ -434,6 +441,8 @@ static tsk_object_t* tnet_ice_ctx_dtor(tsk_object_t * self)
}
TSK_OBJECT_SAFE_FREE(ctx->servers);
TSK_OBJECT_SAFE_FREE(ctx->proxy.info);
TSK_FREE(ctx->ssl.path_priv);
TSK_FREE(ctx->ssl.path_pub);
TSK_FREE(ctx->ssl.path_ca);
@ -998,6 +1007,33 @@ const char* tnet_ice_ctx_get_pwd(const struct tnet_ice_ctx_s* self)
return (self && self->pwd) ? self->pwd : tsk_null;
}
int tnet_ice_ctx_set_proxy_auto_detect(struct tnet_ice_ctx_s* self, tsk_bool_t auto_detect)
{
if (!self) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
self->proxy.auto_detect = auto_detect;
return 0;
}
int tnet_ice_ctx_set_proxy_info(struct tnet_ice_ctx_s* self, enum tnet_proxy_type_e type, const char* host, tnet_port_t port, const char* login, const char* password)
{
if (!self) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
if (!self->proxy.info && !(self->proxy.info = tnet_proxyinfo_create())) {
return -2;
}
self->proxy.info->type = type;
self->proxy.info->port = port;
tsk_strupdate(&self->proxy.info->hostname, host);
tsk_strupdate(&self->proxy.info->username, login);
tsk_strupdate(&self->proxy.info->password, password);
return 0;
}
// cancels the ICE processing without stopping the process
int tnet_ice_ctx_cancel(tnet_ice_ctx_t* self)
{
@ -1566,6 +1602,13 @@ next_server:
if ((ret = tnet_turn_session_set_ssl_certs(candidate->turn.ss, self->ssl.path_priv, self->ssl.path_pub, self->ssl.path_ca, self->ssl.verify))) {
continue;
}
// WebProxy
if ((ret = tnet_turn_session_set_proxy_auto_detect(candidate->turn.ss, self->proxy.auto_detect))) {
continue;
}
if ((ret = tnet_turn_session_set_proxy_info(candidate->turn.ss, self->proxy.info))) {
continue;
}
// set TURN credentials
if ((ret = tnet_turn_session_set_cred(candidate->turn.ss, ice_server->str_username, ice_server->str_password))) {
continue;
@ -1793,7 +1836,7 @@ static int _tnet_ice_ctx_fsm_GatheringCompleted_2_ConnChecking_X_ConnCheck(va_li
struct timeval tv;
static const long rto = 160; // milliseconds
struct sockaddr_storage remote_addr;
uint64_t time_start, time_curr, time_end, concheck_timeout;
uint64_t time_start, time_curr = 0, time_end = 0, concheck_timeout = 0;
tsk_bool_t role_conflict, restart_conneck, check_rtcp, isset, got_hosts;
void* recvfrom_buff_ptr = tsk_null;
tsk_size_t recvfrom_buff_size = 0, tries_count = 0, tries_count_min = kIceConnCheckMinTriesMin;

View File

@ -103,6 +103,9 @@ TINYNET_API int tnet_ice_ctx_send_turn_rtcp(struct tnet_ice_ctx_s* self, const v
TINYNET_API const char* tnet_ice_ctx_get_ufrag(const struct tnet_ice_ctx_s* self);
TINYNET_API const char* tnet_ice_ctx_get_pwd(const struct tnet_ice_ctx_s* self);
TINYNET_API int tnet_ice_ctx_set_proxy_auto_detect(struct tnet_ice_ctx_s* self, tsk_bool_t auto_detect);
TINYNET_API int tnet_ice_ctx_set_proxy_info(struct tnet_ice_ctx_s* self, enum tnet_proxy_type_e type, const char* host, tnet_port_t port, const char* login, const char* password);
TINYNET_API int tnet_ice_ctx_cancel(struct tnet_ice_ctx_s* self);
TINYNET_API int tnet_ice_ctx_stop(struct tnet_ice_ctx_s* self);

View File

@ -1,7 +1,5 @@
/*
* Copyright (C) 2010-2011 Mamadou Diop.
*
* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
* Copyright (C) 2010-2015 Mamadou DIOP.
*
* This file is part of Open Source Doubango Framework.
*
@ -23,9 +21,6 @@
/**@file tinynet.h
* @brief API functions.
*
* @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
*
*/
#ifndef TNET_TINYNET_H
#define TNET_TINYNET_H
@ -40,6 +35,7 @@
#include "tnet_nat.h"
#include "tnet_socket.h"
#include "tnet_transport.h"
#include "tnet_proxy_plugin.h"
#include "stun/tnet_stun.h"

View File

@ -50,12 +50,14 @@
# define TNET_UNDER_WINDOWS_PHONE 1
# endif
# endif
# define TNET_UNDER_WINDOWS_DESKTOP (TNET_UNDER_WINDOWS && !TNET_UNDER_WINDOWS_CE && !TNET_UNDER_WINDOWS_RT && !TNET_UNDER_WINDOWS_PHONE)
#endif
// OS X or iOS
#if defined(__APPLE__)
# define TNET_UNDER_APPLE 1
# include <TargetConditionals.h>
# include <Availability.h>
#endif
#if TARGET_OS_MAC
# define TNET_UNDER_MAC 1
@ -139,6 +141,9 @@
# define HAVE_SYS_PARAM_H 1
# define TNET_HAVE_SS_LEN 1
# define TNET_HAVE_SA_LEN 0
# if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)
# define HAVE_GSSAPI_H 1
# endif
#endif
#endif /* _TINYNET_H_ */

View File

@ -587,7 +587,7 @@ int tnet_dtls_socket_do_handshake(tnet_dtls_socket_handle_t* handle, const struc
TSK_DEBUG_INFO("DTLS data handshake to send with len = %d, from(%.*s/%d) to(%.*s/%d)", len, (int)sizeof(socket->wrapped_sock->ip), socket->wrapped_sock->ip, socket->wrapped_sock->port, (int)sizeof(ip), ip, port);
//!\ IP fragmentation issues must be avoided even if the local transport is TCP/TLS because the relayed (TURN) transport could be UDP
while (records_len > 0 && (ret = tnet_dtls_socket_get_record_first(records_ptr, (tsk_size_t)records_len, &record_ptr, &record_size)) == 0) {
while (records_len > 0 && (ret = tnet_dtls_socket_get_record_first(records_ptr, (tsk_size_t)records_len, (const void**)&record_ptr, &record_size)) == 0) {
if (is_dgram) {
sentlen += tnet_sockfd_sendto(socket->wrapped_sock->fd, (const struct sockaddr *)&socket->remote.addr, record_ptr, record_size);
}

View File

@ -22,7 +22,9 @@
* @brief Network stack.
*/
#include "tnet.h"
#include "tnet_utils.h"
#include "tnet_utils.h"
#include "tnet_proxy_node_socks_plugin.h"
#include "tnet_proxy_plugin.h"
#include "tsk_time.h"
#include "tsk_debug.h"
@ -78,6 +80,10 @@ int tnet_startup()
if (__tnet_started) {
goto bail;
}
if ((err = tnet_proxy_node_plugin_register(tnet_proxy_node_socks_plugin_def_t)) != 0) {
goto bail;
}
// rand()
srand((unsigned int) tsk_time_epoch());
@ -152,6 +158,8 @@ int tnet_cleanup()
if (!__tnet_started){
goto bail;
}
tnet_proxy_node_plugin_unregister(tnet_proxy_node_socks_plugin_def_t);
#if TNET_UNDER_WINDOWS
__tnet_started = tsk_false;

View File

@ -0,0 +1,918 @@
/*
* Copyright (C) 2010-2015 Mamadou DIOP.
* Copyright (C) 2015 Doubango Telecom.
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DOUBANGO.
*
*/
// http://en.wikipedia.org/wiki/SOCKS
// SOCKS Protocol Version 5: http://tools.ietf.org/html/rfc1928
// Username/Password Authentication for SOCKS V5: https://www.ietf.org/rfc/rfc1929.txt
// GSS-API Authentication Method for SOCKS Version 5: https://tools.ietf.org/html/rfc1961
// SSPI/Kerberos Interoperability with GSSAPI: https://msdn.microsoft.com/en-us/library/ms995352.aspx
// Mozilla issue: https://bugzilla.mozilla.org/show_bug.cgi?id=122752
#include "tnet_proxy_node_socks_plugin.h"
#include "tnet_proxy_plugin.h"
#include "tnet_socket.h"
#include "tnet_utils.h"
#include "tnet_endianness.h"
#include "tsk_safeobj.h"
#include "tsk_buffer.h"
#include "tsk_memory.h"
#include "tsk_string.h"
#include "tsk_debug.h"
#if TNET_UNDER_APPLE
# if !defined(USING_CFSTREAM)
# define USING_CFSTREAM 0 // Works with HTTP streams only :(
# endif /* USING_CFSTREAM */
#endif /* TNET_UNDER_APPLE */
#if USING_CFSTREAM
# import <CFNetwork/CFNetwork.h>
# import <SystemConfiguration/SystemConfiguration.h>
#endif /* TNET_UNDER_APPLE */
#if HAVE_GSSAPI_H
# if TNET_UNDER_APPLE
# include <GSS/gssapi.h>
# else
# include <gssapi.h>
# endif
#endif /* HAVE_GSSAPI_H */
#define kSocks4StatusGranted 0x5a
#if !defined(TNET_SOCKS5_HAVE_AUTH_NONE)
# define TNET_SOCKS5_HAVE_AUTH_NONE 1
#endif /* TNET_SOCKS5_HAVE_AUTH_NONE */
#if !defined(TNET_SOCKS5_HAVE_AUTH_USRPWD)
# define TNET_SOCKS5_HAVE_AUTH_USRPWD 1
#endif /* TNET_SOCKS5_HAVE_AUTH_USRPWD */
#if !defined(TNET_SOCKS5_HAVE_AUTH_GSSAPI)
# if HAVE_GSSAPI_H // TODO: Add Microsoft SSPI
# define TNET_SOCKS5_HAVE_AUTH_GSSAPI 0
# endif /* HAVE_GSSAPI_H */
#endif /* TNET_SOCKS5_HAVE_AUTH_GSSAPI */
#if !TNET_SOCKS5_HAVE_AUTH_NONE && !TNET_SOCKS5_HAVE_AUTH_USRPWD && !TNET_SOCKS5_HAVE_AUTH_GSSAPI
# error "At least one Socks5 authentication method must be enabled"
#endif
#define kSocks5AuthMethodNone 0x00 // NO AUTHENTICATION REQUIRED
#define kSocks5AuthMethodGSSAPI 0x01 // GSSAPI
#define kSocks5AuthMethodUsrPwd 0x02 // USERNAME/PASSWORD
typedef int socks5_auth_method_t;
typedef enum socks5_state_e {
socks5_state_none,
socks5_state_greeting,
socks5_state_auth_req,
socks5_state_conn_req,
socks5_state_conn_accept,
socks5_state_error
}
socks5_state_t;
typedef struct tnet_proxy_node_socks_plugin_s
{
TNET_DECLARE_PROXY_NONE;
struct {
tsk_bool_t completed;
tsk_bool_t started;
tsk_buffer_t* buff;
uint8_t* pending_data_ptr;
tsk_size_t pending_data_len;
socks5_state_t socks5_state;
socks5_auth_method_t socks5_auth_method;
#if TNET_SOCKS5_HAVE_AUTH_GSSAPI
struct {
gss_ctx_id_t ctx;
OM_uint32 status_minor;
OM_uint32 status_major;
gss_name_t server_name;
tsk_bool_t init_sec_complete;
}gss;
#endif /* TNET_SOCKS5_HAVE_AUTH_GSSAPI */
} handshacking;
TSK_DECLARE_SAFEOBJ;
}
tnet_proxy_node_socks_plugin_t;
static const char* __socks5_state_to_string(socks5_state_t state);
static const char* __socks5_method_to_string(socks5_auth_method_t method);
#if TNET_SOCKS5_HAVE_AUTH_GSSAPI
static int __socks5_gss_import_name(tnet_proxy_node_t* self);
static int __socks5_gss_init_sec_context(tnet_proxy_node_t* self);
static void __socks_gss_print_error(const char* info, OM_uint32 status_major, OM_uint32 status_minor);
#endif /* TNET_SOCKS5_HAVE_AUTH_GSSAPI */
static int _tnet_proxy_node_socks_plugin_configure(tnet_proxy_node_t* self, ...)
{
tnet_proxy_node_socks_plugin_t* node = (tnet_proxy_node_socks_plugin_t*)self;
va_list ap;
int ret = 0;
// input parameters already checked by the caller
tsk_safeobj_lock(node);
// extract dst_host, dst_port, proxy_host, proxy_port, ipv6_enabled, ...
va_start(ap, self);
ret = tnet_proxy_node_configure_2(self, &ap);
va_end(ap);
tsk_safeobj_unlock(node);
return 0;
}
static int _tnet_proxy_node_socks_plugin_start_handshaking(tnet_proxy_node_t* self)
{
tnet_proxy_node_socks_plugin_t* node = (tnet_proxy_node_socks_plugin_t*)self;
int ret = 0;
// input parameters already checked by the caller
tsk_safeobj_lock(node);
if (node->handshacking.started) {
TSK_DEBUG_ERROR("handshaking already started");
goto bail;
}
if (tsk_strnullORempty(self->dst_host) || !self->dst_port) {
TSK_DEBUG_ERROR("Invalid proxy host and/or port for socks server %s:%hu", self->proxy_host, self->proxy_port);
ret = -2;
goto bail;
}
// reset pending data
TSK_FREE(node->handshacking.pending_data_ptr);
node->handshacking.pending_data_len = 0;
#if USING_CFSTREAM
if (tsk_strnullORempty(self->proxy_host) || !self->proxy_port) {
TSK_DEBUG_ERROR("Invalid proxy host and/or port for socks server %s:%hu", self->proxy_host, self->proxy_port);
ret = -2;
goto bail;
}
if (!self->cf_read_stream || !self->cf_write_stream) {
TSK_DEBUG_ERROR("Invalid CFStreams: read=%d write=%d", !!self->cf_read_stream, !!self->cf_write_stream);
ret = -3;
goto bail;
}
CFStringRef cfstrHost = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, self->proxy_host, kCFStringEncodingUTF8, NULL);
int intPort = (int)self->proxy_port;
CFNumberRef cfintPort = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &intPort);
CFDictionaryRef proxyDict = CFNetworkCopySystemProxySettings();
CFMutableDictionaryRef socksConfig = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, proxyDict);
CFDictionarySetValue(socksConfig, kCFStreamPropertySOCKSProxyHost, cfstrHost);
CFDictionarySetValue(socksConfig, kCFStreamPropertySOCKSProxyPort, cfintPort);
CFDictionarySetValue(socksConfig, kCFStreamPropertySOCKSVersion, self->type == tnet_proxy_type_socks4 ? kCFStreamSocketSOCKSVersion4 : kCFStreamSocketSOCKSVersion5);
if (!tsk_strnullORempty(self->login)) {
CFStringRef cfstrLogin = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, self->login, kCFStringEncodingUTF8, NULL);
CFDictionarySetValue(socksConfig, kCFStreamPropertySOCKSUser, cfstrLogin);
CFRelease(cfstrLogin);
}
if (!tsk_strnullORempty(self->password)) {
CFStringRef cfstrPassword = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, self->password, kCFStringEncodingUTF8, NULL);
CFDictionarySetValue(socksConfig, kCFStreamPropertySOCKSPassword, cfstrPassword);
CFRelease(cfstrPassword);
}
if (!CFReadStreamSetProperty(self->cf_read_stream, kCFStreamPropertySOCKSProxy, socksConfig)) {
CFStreamError error = CFReadStreamGetError(self->cf_read_stream);
TSK_DEBUG_INFO("CFReadStreamSetProperty(kCFStreamPropertySOCKSProxy) failed code=%d, domain=%ld", (int)error.error, error.domain);
ret = -4;
}
else if (!CFWriteStreamSetProperty(self->cf_write_stream, kCFStreamPropertySOCKSProxy, socksConfig)) {
CFStreamError error = CFWriteStreamGetError(self->cf_write_stream);
TSK_DEBUG_INFO("CFWriteStreamSetProperty(kCFStreamPropertySOCKSProxy) code=%d, domain=%ld", (int)error.error, error.domain);
ret = -5;
}
CFRelease(cfstrHost);
CFRelease(cfintPort);
CFRelease(socksConfig);
CFRelease(proxyDict);
node->handshacking.started = (ret == 0);
node->handshacking.completed = node->handshacking.started; // no handshaking data to send, up to the system
#else
{
tsk_size_t size_to_reserve, userid_len = tsk_strlen(self->login), domain_len = 0;
if (self->type == tnet_proxy_type_socks4 || self->type == tnet_proxy_type_socks4a) {
size_to_reserve = 1 /* version number */
+ 1 /* command code */
+ 2 /* network byte order port number */
+ 4 /* network byte order IP address */
+ userid_len + 1 /* the user ID string, variable length, terminated with a null (0x00) */
;
if (self->type == tnet_proxy_type_socks4a) {
domain_len = tsk_strlen(self->dst_host);
size_to_reserve += domain_len + 1/* the domain name of the host we want to contact, variable length, terminated with a null (0x00) */
;
}
}
else { // SOCKS5
if (node->handshacking.socks5_state != socks5_state_none) {
TSK_DEBUG_ERROR("Socks5 handshaking state mut start at none");
ret = -6;
goto bail;
}
// Greeting
size_to_reserve = 1 /* version number */
+ 1 /* number of authentication methods supported */
#if TNET_SOCKS5_HAVE_AUTH_NONE
+ 1
#endif
#if TNET_SOCKS5_HAVE_AUTH_USRPWD
+ 1
#endif
#if TNET_SOCKS5_HAVE_AUTH_GSSAPI
+ 1
#endif
;
}
node->handshacking.pending_data_ptr = (uint8_t*)tsk_realloc(node->handshacking.pending_data_ptr, size_to_reserve);
if (!node->handshacking.pending_data_ptr) {
TSK_DEBUG_ERROR("Failed to allocate buffer with size = %u", (unsigned)size_to_reserve);
node->handshacking.pending_data_len = 0;
ret = -5;
goto bail;
}
node->handshacking.pending_data_len = size_to_reserve;
if (self->type == tnet_proxy_type_socks4 || self->type == tnet_proxy_type_socks4a) {
node->handshacking.pending_data_ptr[0] = 0x04; // version number
node->handshacking.pending_data_ptr[1] = 0x01; // establish a TCP/IP stream connection (caller aldready check we're dealing with Streams)
node->handshacking.pending_data_ptr[2] = (self->dst_port >> 8) & 0xFF; // port, first byte
node->handshacking.pending_data_ptr[3] = self->dst_port & 0xFF; // port, second byte
if (self->type == tnet_proxy_type_socks4) {
struct sockaddr_storage addr;
// network byte order IPv4 address (SOCKS4 doesn't support hostnames)
if ((ret = tnet_sockaddr_init(self->dst_host, self->dst_port, self->socket.type, &addr)) != 0 || addr.ss_family != AF_INET) {
TSK_DEBUG_ERROR("tnet_sockaddr_init(%s, %d, %d) failed", self->dst_host, self->dst_port, self->socket.type);
ret = -6;
goto bail;
}
memcpy(&node->handshacking.pending_data_ptr[4], (void*)&((const struct sockaddr_in*)&addr)->sin_addr.s_addr, 4);
}
else {
// deliberate invalid IP address, 4 bytes, first three must be 0x00 and the last one must not be 0x00
node->handshacking.pending_data_ptr[4] = 0x00;
node->handshacking.pending_data_ptr[5] = 0x00;
node->handshacking.pending_data_ptr[6] = 0x00;
node->handshacking.pending_data_ptr[7] = 0x83; // non-zeo byte
}
// the user ID string, variable length, terminated with a null (0x00)
if (userid_len > 0) {
memcpy(&node->handshacking.pending_data_ptr[8], self->login, userid_len);
}
node->handshacking.pending_data_ptr[8 + userid_len] = 0x00;
// the domain name of the host we want to contact, variable length, terminated with a null (0x00)
if (self->type == tnet_proxy_type_socks4a) {
if (domain_len > 0) {
memcpy(&node->handshacking.pending_data_ptr[8 + userid_len + 1], self->dst_host, domain_len);
}
node->handshacking.pending_data_ptr[8 + userid_len + 1 + domain_len] = 0x00;
}
}
else { // SOCKS5
uint8_t* auths_ptr = &node->handshacking.pending_data_ptr[2];
node->handshacking.pending_data_ptr[0] = 0x05; // version number
node->handshacking.pending_data_ptr[1] = 0x00; // number of authentication methods supported
#if TNET_SOCKS5_HAVE_AUTH_NONE
node->handshacking.pending_data_ptr[1] += 1;
#endif
#if TNET_SOCKS5_HAVE_AUTH_USRPWD
node->handshacking.pending_data_ptr[1] += 1;
#endif
#if TNET_SOCKS5_HAVE_AUTH_GSSAPI
node->handshacking.pending_data_ptr[1] += 1;
#endif
#if TNET_SOCKS5_HAVE_AUTH_GSSAPI
*auths_ptr++ = kSocks5AuthMethodGSSAPI;
#endif
#if TNET_SOCKS5_HAVE_AUTH_USRPWD
*auths_ptr++ = kSocks5AuthMethodUsrPwd;
#endif
#if TNET_SOCKS5_HAVE_AUTH_NONE
*auths_ptr++ = kSocks5AuthMethodNone;
#endif
// change state from none to greeting
node->handshacking.socks5_state = socks5_state_greeting;
}
node->handshacking.started = tsk_true;
}
#endif
bail:
tsk_safeobj_unlock(node);
return ret;
}
static int _tnet_proxy_node_socks_plugin_set_handshaking_data(tnet_proxy_node_t* self, const void* data_ptr, tsk_size_t data_size)
{
tnet_proxy_node_socks_plugin_t* node = (tnet_proxy_node_socks_plugin_t*)self;
int ret = 0;
// input parameters already checked by the caller
tsk_safeobj_lock(node);
if (!node->handshacking.started) {
TSK_DEBUG_ERROR("handshaking not started");
ret = -3;
goto bail;
}
#if USING_CFSTREAM
TSK_DEBUG_ERROR("Up to CFStreams to handle handshaking");
ret = -2;
goto bail;
#else
if (!node->handshacking.buff) {
if (!(node->handshacking.buff = tsk_buffer_create(data_ptr, data_size))) {
ret = -2;
goto bail;
}
}
else {
if ((ret = tsk_buffer_append(node->handshacking.buff, data_ptr, data_size)) != 0) {
goto bail;
}
}
if (self->type == tnet_proxy_type_socks4 || self->type == tnet_proxy_type_socks4a) {
static const tsk_size_t min_bytes_count = 8;
const uint8_t* buff = (const uint8_t*)node->handshacking.buff->data;
uint8_t status;
if (node->handshacking.buff->size < min_bytes_count) {
TSK_DEBUG_INFO("Not enough data in the SOCKS4/4a handshaking buffer yet");
goto bail;
}
status = buff[1];
TSK_DEBUG_INFO("SOCKS4/4a status = 0x%x", status);
if (status != kSocks4StatusGranted) { // status == granted ?
TSK_DEBUG_ERROR("SOCKS response from the server has status equal to 0x%x", status);
ret = -3;
goto bail;
}
// remove already parsed data
tsk_buffer_remove(node->handshacking.buff, 0, min_bytes_count);
// handshaking is done ?
node->handshacking.completed = (status == kSocks4StatusGranted);
}
else if (self->type == tnet_proxy_type_socks5) {
const uint8_t* buff = (const uint8_t*)node->handshacking.buff->data;
TSK_DEBUG_INFO("Socks5 state = %s", __socks5_state_to_string(node->handshacking.socks5_state));
if (node->handshacking.socks5_state == socks5_state_greeting) {
// response to greeting
if (node->handshacking.buff->size < 2/*1-byte version + 1-byte authentication method*/) {
TSK_DEBUG_INFO("Not enough data in the SOCKS5 handshaking buffer yet");
goto bail;
}
if (buff[0] != 0x05) {
TSK_DEBUG_ERROR("Invalid version (%d)", buff[0]);
ret = -3; goto bail;
}
if (buff[1] == kSocks5AuthMethodNone || buff[1] == kSocks5AuthMethodUsrPwd || buff[1] == kSocks5AuthMethodGSSAPI) {
node->handshacking.socks5_auth_method = (socks5_auth_method_t)buff[1];
TSK_DEBUG_INFO("Server selected Socks5 authentication method = %s", __socks5_method_to_string(node->handshacking.socks5_auth_method));
tsk_buffer_remove(node->handshacking.buff, 0, 2); // remove parsed bytes
if (node->handshacking.socks5_auth_method == kSocks5AuthMethodNone) {
node->handshacking.socks5_state = socks5_state_conn_req;
// FIXME:
TSK_DEBUG_ERROR("Not implemented yet");
ret = -3; goto bail;
}
else {
if (node->handshacking.socks5_auth_method == kSocks5AuthMethodUsrPwd) {
tsk_size_t userlen = tsk_strlen(self->login), pwdlen = tsk_strlen(self->password);
tsk_size_t size_to_reserve = 1 /* version number */
+ 1 /* username length */
+ userlen /* username */
+ 1 /* password length */
+ pwdlen;
node->handshacking.pending_data_ptr = (uint8_t*)tsk_realloc(node->handshacking.pending_data_ptr, size_to_reserve);
if (!node->handshacking.pending_data_ptr) {
TSK_DEBUG_ERROR("Failed to allocate buffer with size = %u", (unsigned)size_to_reserve);
node->handshacking.pending_data_len = 0;
ret = -5;
goto bail;
}
node->handshacking.pending_data_len = size_to_reserve;
node->handshacking.pending_data_ptr[0] = 0x01;
node->handshacking.pending_data_ptr[1] = (userlen & 0xFF);
if (userlen > 0) {
memcpy(&node->handshacking.pending_data_ptr[2], self->login, userlen);
}
node->handshacking.pending_data_ptr[2 + userlen] = pwdlen;
if (pwdlen > 0) {
memcpy(&node->handshacking.pending_data_ptr[2 + userlen + 1], self->password, pwdlen);
}
node->handshacking.socks5_state = socks5_state_auth_req;
}
else if (node->handshacking.socks5_auth_method == kSocks5AuthMethodGSSAPI) {
#if TNET_SOCKS5_HAVE_AUTH_GSSAPI
if ((ret = __socks5_gss_import_name(self))) {
TSK_DEBUG_ERROR("gss_import_name() failed");
goto bail;
}
if ((ret = __socks5_gss_init_sec_context(self))) {
TSK_DEBUG_ERROR("gss_import_name() failed");
goto bail;
}
#else
TSK_DEBUG_ERROR("GSSAPI not supported");
ret = -3; goto bail;
#endif
}
else {
TSK_DEBUG_ERROR("Not implemented yet");
ret = -3; goto bail;
}
}
}
else {
TSK_DEBUG_ERROR("Invalid authentication method (%d)", buff[1]);
ret = -3; goto bail;
}
}
else if (node->handshacking.socks5_state == socks5_state_auth_req) {
// response to authentication
if (node->handshacking.socks5_auth_method == kSocks5AuthMethodUsrPwd) {
if (node->handshacking.buff->size < 2/*1-byte version + 1-byte status code*/) {
TSK_DEBUG_INFO("Not enough data in the SOCKS5 handshaking buffer yet");
goto bail;
}
if (buff[0] != 0x1) {
TSK_DEBUG_ERROR("Invalid version :%d", buff[0]);
ret = -3; goto bail;
}
TSK_DEBUG_INFO("Socks5 authentication status code: %d", buff[1]);
if (buff[1] != 0x00) {
TSK_DEBUG_ERROR("Authentication failed with status code :%d", buff[1]);
ret = -3; goto bail;
}
tsk_buffer_remove(node->handshacking.buff, 0, 2); // remove parsed bytes
node->handshacking.socks5_state = socks5_state_conn_req;
}
else if (node->handshacking.socks5_auth_method == kSocks5AuthMethodGSSAPI) {
// FIXME:
TSK_DEBUG_ERROR("Not implemented yet");
ret = -3; goto bail;
}
else {
TSK_DEBUG_ERROR("Invalid authentication method (%d)", buff[1]);
ret = -3; goto bail;
}
// State changed from "auth_req" to "conn_req" : build connection request
if (node->handshacking.socks5_state == socks5_state_conn_req) {
#define kAddrTypeIPv4 0x01
#define kAddrTypeDomaineName 0x03
#define kAddrTypeIPv6 0x04
struct sockaddr_storage addr;
tsk_bool_t is_ip = tsk_false; // ip or domain name
uint8_t addr_type = 0x00;
tsk_size_t dst_addr_len = 0, size_to_reserve = 1 /* version number */
+ 1 /* command code */
+ 1 /* reserved, must be 0x00 */
+ 1 /* address type */
+ 0 /* destination address (==to be computed later==) */
+ 2 /* port number in a network byte order */
;
if ((ret = tnet_sockaddr_init(self->dst_host, self->dst_port, self->socket.type, &addr)) != 0) {
TSK_DEBUG_WARN("tnet_sockaddr_init(%s, %d, %d) failed", self->dst_host, self->dst_port, self->socket.type);
// maybe DNS issue (e.g UDP blocked), do not exit, up to the server to resolve it
is_ip = tsk_false;
}
else {
tnet_ip_t ip = {0};
tnet_get_sockip((const struct sockaddr *)&addr, &ip);
is_ip = tsk_striequals(self->dst_host, ip);
}
if (is_ip) {
dst_addr_len = (addr.ss_family == AF_INET6) ? 16 : 4;
addr_type = (addr.ss_family == AF_INET6) ? kAddrTypeIPv6 : kAddrTypeIPv4;
}
else {
dst_addr_len = 1 /* length*/ + tsk_strlen(self->dst_host);
addr_type = kAddrTypeDomaineName;
}
size_to_reserve += dst_addr_len;
node->handshacking.pending_data_ptr = (uint8_t*)tsk_realloc(node->handshacking.pending_data_ptr, size_to_reserve);
if (!node->handshacking.pending_data_ptr) {
TSK_DEBUG_ERROR("Failed to allocate buffer with size = %u", (unsigned)size_to_reserve);
node->handshacking.pending_data_len = 0;
ret = -5; goto bail;
}
node->handshacking.pending_data_len = size_to_reserve;
node->handshacking.pending_data_ptr[0] = 0x05; // version number
node->handshacking.pending_data_ptr[1] = 0x01; // establish a TCP/IP stream connection
node->handshacking.pending_data_ptr[2] = 0x00; // reserved, must be 0x00
node->handshacking.pending_data_ptr[3] = addr_type;
if (addr_type == kAddrTypeIPv4) {
memcpy(&node->handshacking.pending_data_ptr[4], (void*)&((const struct sockaddr_in*)&addr)->sin_addr.s_addr, 4);
}
else if (addr_type == kAddrTypeIPv6) {
memcpy(&node->handshacking.pending_data_ptr[4], (void*)&((const struct sockaddr_in6*)&addr)->sin6_addr, 16);
}
else { // DomaineName
node->handshacking.pending_data_ptr[4] = (uint8_t)(dst_addr_len - 1);/* length */;
memcpy(&node->handshacking.pending_data_ptr[5], self->dst_host, (dst_addr_len - 1));
}
node->handshacking.pending_data_ptr[4 + dst_addr_len] = (self->dst_port >> 8) & 0xFF;
node->handshacking.pending_data_ptr[4 + dst_addr_len + 1] = (self->dst_port & 0xFF);
}
}
else if (node->handshacking.socks5_state == socks5_state_conn_req) {
// response to connection request
if (node->handshacking.buff->size < 2/*1-byte version + 1-byte status*/) {
TSK_DEBUG_INFO("Not enough data in the SOCKS5 handshaking buffer yet");
goto bail;
}
if (buff[0] != 0x05) {
TSK_DEBUG_ERROR("Invalid version (%d)", buff[0]);
ret = -3; goto bail;
}
TSK_DEBUG_INFO("Socks5 connection request status code: %d", buff[1]);
if (buff[1] != 0x00) {
TSK_DEBUG_ERROR("Socks5 connection request failed with status code :%d", buff[1]);
ret = -3; goto bail;
}
tsk_buffer_remove(node->handshacking.buff, 0, 2); // remove parsed bytes
node->handshacking.socks5_state = socks5_state_conn_accept;
node->handshacking.completed = tsk_true;
}
else if (node->handshacking.socks5_state == socks5_state_conn_accept) {
TSK_DEBUG_INFO("Socks5 connection accepted");
// nothing else to do
}
else {
// FIXME:
TSK_DEBUG_ERROR("Not implemented yet");
ret = -3;
goto bail;
}
}
else {
TSK_DEBUG_ERROR("Invalid proxy type:%d", self->type);
ret = -3;
goto bail;
}
#endif
bail:
if (ret != 0) {
if (self->type == tnet_proxy_type_socks5) {
node->handshacking.socks5_state = socks5_state_error;
}
}
tsk_safeobj_unlock(node);
return ret;
}
static int _tnet_proxy_node_socks_plugin_get_handshaking_pending_data(tnet_proxy_node_t* self, void** data_pptr, tsk_size_t* data_psize)
{
tnet_proxy_node_socks_plugin_t* node = (tnet_proxy_node_socks_plugin_t*)self;
int ret = -1;
// input parameters already checked by the caller
tsk_safeobj_lock(node);
#if USING_CFSTREAM
*data_psize = 0; // no pending data
ret = 0;
#else
if (node->handshacking.pending_data_ptr && node->handshacking.pending_data_len > 0) {
if ((*data_pptr = tsk_realloc(*data_pptr, node->handshacking.pending_data_len))) {
memcpy(*data_pptr, node->handshacking.pending_data_ptr, (tsk_size_t) node->handshacking.pending_data_len);
*data_psize = node->handshacking.pending_data_len;
ret = 0;
}
// reset the pending data. Up to the caller to hold the returned data and send it as many as required (e.g. when using unreliable transport)
TSK_FREE(node->handshacking.pending_data_ptr);
node->handshacking.pending_data_len = 0;
}
#endif
bail:
tsk_safeobj_unlock(node);
return ret;
}
static int _tnet_proxy_node_socks_plugin_get_handshaking_completed(tnet_proxy_node_t* self, tsk_bool_t* completed)
{
tnet_proxy_node_socks_plugin_t* node = (tnet_proxy_node_socks_plugin_t*)self;
int ret = 0;
// input parameters already checked by the caller
tsk_safeobj_lock(node);
*completed = node->handshacking.completed;
tsk_safeobj_unlock(node);
return ret;
}
static const char* __socks5_state_to_string(socks5_state_t state)
{
switch (state) {
case socks5_state_none: return "none";
case socks5_state_greeting: return "greeting";
case socks5_state_auth_req: return "auth_req";
case socks5_state_conn_req: return "conn_req";
case socks5_state_conn_accept: return "conn_accept";
case socks5_state_error: return "error";
default: return "unknown";
}
}
static const char* __socks5_method_to_string(socks5_auth_method_t method)
{
switch (method) {
case kSocks5AuthMethodNone: return "none";
case kSocks5AuthMethodGSSAPI: return "gssapi";
case kSocks5AuthMethodUsrPwd: return "usr/pwd";
default: return "unknown";
}
}
#if TNET_SOCKS5_HAVE_AUTH_GSSAPI
// rfc1961 - 3.1 Preparation
static int __socks5_gss_import_name(tnet_proxy_node_t* self)
{
int i, ret = 0;
tnet_proxy_node_socks_plugin_t* node = (tnet_proxy_node_socks_plugin_t*)self;
gss_buffer_desc input_name_buffer = GSS_C_EMPTY_BUFFER;
if (!self) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
// reset context
node->handshacking.gss.ctx = GSS_C_NO_CONTEXT;
// reset pending handshaking data
TSK_FREE(node->handshacking.pending_data_ptr);
node->handshacking.pending_data_len = 0;
/*
For example, when using Kerberos V5 naming, the imported name may be
of the form "SERVICE:socks@socks_server_hostname" where
"socks_server_hostname" is the fully qualified host name of the
server with all letters in lower case.
*/
input_name_buffer.length = tsk_sprintf((char**)&input_name_buffer.value, "SERVICE:socks@%s", self->proxy_host);
for (i = 0; i < input_name_buffer.length; ++i) {
((char*)input_name_buffer.value)[i] = tolower(((char*)input_name_buffer.value)[i]);
}
/*
The client should call gss_import_name to obtain an internal
representation of the server name. For maximal portability the
default name_type GSS_C_NULL_OID should be used to specify the
default name space, and the input name_string should treated by the
client's code as an opaque name-space specific input.
*/
node->handshacking.gss.status_major = gss_import_name(&node->handshacking.gss.status_minor, &input_name_buffer, GSS_C_NULL_OID, &node->handshacking.gss.server_name);
TSK_DEBUG_INFO("gss_import_name(%.*s, GSS_C_NULL_OID): minor_status = %u, major_status = %u", (int)input_name_buffer.length, (const char*)input_name_buffer.value, node->handshacking.gss.status_minor, node->handshacking.gss.status_major);
if (node->handshacking.gss.status_major != GSS_S_COMPLETE) {
__socks_gss_print_error("gss_import_name failed", node->handshacking.gss.status_major, node->handshacking.gss.status_minor);
ret = -2; goto bail;
}
/* debug */{
gss_OID output_name_type;
gss_buffer_desc output_name = GSS_C_EMPTY_BUFFER;
node->handshacking.gss.status_major = gss_display_name(&node->handshacking.gss.status_minor,
node->handshacking.gss.server_name,
&output_name,
&output_name_type);
TSK_DEBUG_INFO("gss_display_name(%.*s): minor_status = %u, major_status = %u, output = %.*s", (int)input_name_buffer.length, (const char*)input_name_buffer.value, node->handshacking.gss.status_minor, node->handshacking.gss.status_major, (int)output_name.length, (const char*)output_name.value);
node->handshacking.gss.status_major = gss_release_buffer(&node->handshacking.gss.status_minor, &output_name);
}
bail:
gss_release_buffer(&node->handshacking.gss.status_minor, &input_name_buffer);
return 0;
}
// 3.2 Client Context Establishment
// 3.3 Client Context Establishment Major Status codes
static int __socks5_gss_init_sec_context(tnet_proxy_node_t* self)
{
int ret = 0;
OM_uint32 req_flags;
tnet_proxy_node_socks_plugin_t* node = (tnet_proxy_node_socks_plugin_t*)self;
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER, *input_token_ptr = GSS_C_NO_BUFFER;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
/*
+------+------+------+.......................+
+ ver | mtyp | len | token |
+------+------+------+.......................+
+ 0x01 | 0x01 | 0x02 | up to 2^16 - 1 octets |
+------+------+------+.......................+
*/
#define kTokenMsgHdrLongLen 4 // with "len" field
#define kTokenMsgHdrShortLen 2 // without "len" field
if (!self) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
if (node->handshacking.buff && node->handshacking.buff->size >= kTokenMsgHdrShortLen) {
const uint8_t* buff = (const uint8_t*)node->handshacking.buff->data;
if (buff[1] == kSocks5AuthMethodGSSAPI) {
if (node->handshacking.buff->size >= kTokenMsgHdrLongLen) {
uint16_t len = ((buff[2] << 8) & 0x00FF) | (buff[3] & 0xFF);
if (len >= kTokenMsgHdrLongLen + node->handshacking.buff->size) {
input_token.value = (void*) (buff + kTokenMsgHdrLongLen);
input_token.length = (size_t)len;
input_token_ptr = &input_token;
TSK_DEBUG_INFO("GSS Input token: ver=%d, mtyp=%d, len=%d", buff[0], buff[1], len);
}
}
}
else {
TSK_DEBUG_ERROR("GSS invalid mtyp(%u)", buff[1]);
ret = -5; goto bail;
}
}
req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_DELEG_FLAG;
// However, GSS_C_SEQUENCE_FLAG should only be passed in for TCP-based clients, not for UDP-based clients.
if (TNET_SOCKET_TYPE_IS_STREAM(self->socket.type)) {
req_flags |= GSS_C_SEQUENCE_FLAG;
}
node->handshacking.gss.status_major = gss_delete_sec_context(&node->handshacking.gss.status_minor, &node->handshacking.gss.ctx, NULL);
node->handshacking.gss.ctx = GSS_C_NO_CONTEXT;
node->handshacking.gss.status_major = gss_init_sec_context(&node->handshacking.gss.status_minor,
GSS_C_NO_CREDENTIAL, // GSS_C_NO_CREDENTIAL into cred_handle to specify the default credential (for initiator usage)
&node->handshacking.gss.ctx,
node->handshacking.gss.server_name,
GSS_C_NULL_OID, // GSS_C_NULL_OID into mech_type to specify the default mechanism
req_flags,
GSS_C_INDEFINITE,
GSS_C_NO_CHANNEL_BINDINGS,
input_token_ptr,
tsk_null,
&output_token,
tsk_null,
tsk_null);
// Only "GSS_S_COMPLETE" and "GSS_S_CONTINUE_NEEDED" are acceptable
if (node->handshacking.gss.status_major != GSS_S_COMPLETE && node->handshacking.gss.status_major != GSS_S_CONTINUE_NEEDED) {
__socks_gss_print_error("gss_init_sec_context failed", node->handshacking.gss.status_major, node->handshacking.gss.status_minor);
ret = -2; goto bail;
}
// reset pending handshaking data
TSK_FREE(node->handshacking.pending_data_ptr);
node->handshacking.pending_data_len = 0;
if (output_token.length > 0 && output_token.value) {
node->handshacking.pending_data_len = kTokenMsgHdrLongLen + output_token.length;
node->handshacking.pending_data_ptr = tsk_realloc(node->handshacking.pending_data_ptr, node->handshacking.pending_data_len);
if (!node->handshacking.pending_data_ptr) {
TSK_DEBUG_ERROR("Failed to allocate buffer with size = %u", (unsigned)node->handshacking.pending_data_len);
node->handshacking.pending_data_len = 0;
ret = -3;
goto bail;
}
node->handshacking.pending_data_ptr[0] = 0x01;
node->handshacking.pending_data_ptr[1] = kSocks5AuthMethodGSSAPI;
node->handshacking.pending_data_ptr[2] = ((output_token.length >> 8) & 0xFF);
node->handshacking.pending_data_ptr[3] = output_token.length & 0xFF;
memcpy(&node->handshacking.pending_data_ptr[4], output_token.value, output_token.length);
}
if (input_token.length > 0) {
tsk_buffer_remove(node->handshacking.buff, 0, kTokenMsgHdrLongLen + input_token.length);
}
// update "init_sec_complete"
node->handshacking.gss.init_sec_complete = (ret == 0) && (node->handshacking.gss.status_major == GSS_S_COMPLETE);
bail:
return ret;
}
static void __socks_gss_print_error(const char* info, OM_uint32 status_major, OM_uint32 status_minor)
{
OM_uint32 m1, m2, s1, s2;
gss_buffer_desc out1 = {.value = tsk_null, .length = 0}, out2 = {.value = tsk_null, .length = 0};
// print the error
m1 = 0, s1 = 0;
gss_display_status(&s1, status_major, GSS_C_GSS_CODE, GSS_C_NULL_OID, &m1, &out1);
m2 = 0, s2 = 0;
gss_display_status(&s2, status_minor, GSS_C_MECH_CODE, GSS_C_NULL_OID, &m2, &out2);
TSK_DEBUG_ERROR("%s (status_major=%u, status_minor=%u): GSS_C_GSS_CODE(%u, %s), GSS_C_MECH_CODE(%u, %s)",
info,
status_major, status_minor,
s1, (const char*)out1.value, s2, (const char*)out2.value);
gss_release_buffer(&s1, &out1);
gss_release_buffer(&s2, &out2);
}
#endif /* TNET_SOCKS5_HAVE_AUTH_GSSAPI */
/* constructor */
static tsk_object_t* tnet_proxy_node_socks_plugin_ctor(tsk_object_t * self, va_list * app)
{
tnet_proxy_node_socks_plugin_t *node = self;
if (node) {
/* init base */
tnet_proxy_node_init(TNET_PROXY_NODE(node));
/* init self */
tsk_safeobj_init(node);
#if TNET_SOCKS5_HAVE_AUTH_GSSAPI
node->handshacking.gss.server_name = GSS_C_NO_NAME;
node->handshacking.gss.ctx = GSS_C_NO_CONTEXT;
#endif
TSK_DEBUG_INFO("Create SOCKS(4/4a/5) proxy node");
}
return self;
}
/* destructor */
static tsk_object_t* tnet_proxy_node_socks_plugin_dtor(tsk_object_t * self)
{
tnet_proxy_node_socks_plugin_t *node = self;
if (node) {
/* deinit base */
tnet_proxy_node_deinit(TNET_PROXY_NODE(node));
/* deinit self */
TSK_FREE(node->handshacking.pending_data_ptr);
TSK_OBJECT_SAFE_FREE(node->handshacking.buff);
#if TNET_SOCKS5_HAVE_AUTH_GSSAPI
gss_release_name(&node->handshacking.gss.status_minor, &node->handshacking.gss.server_name);
if (node->handshacking.gss.ctx != GSS_C_NO_CONTEXT) {
gss_delete_sec_context(&node->handshacking.gss.status_minor, &node->handshacking.gss.ctx, GSS_C_NO_BUFFER);
}
#endif /* TNET_SOCKS5_HAVE_AUTH_GSSAPI */
tsk_safeobj_deinit(node);
TSK_DEBUG_INFO("*** Socks(4/4a/5) proxy node destroyed ***");
}
return self;
}
/* object definition */
static const tsk_object_def_t thttp_proxy_node_def_s =
{
sizeof(tnet_proxy_node_socks_plugin_t),
tnet_proxy_node_socks_plugin_ctor,
tnet_proxy_node_socks_plugin_dtor,
tsk_null,
};
/* plugin definition*/
static const struct tnet_proxy_node_plugin_def_s tnet_proxy_node_socks_plugin_def_s =
{
&thttp_proxy_node_def_s,
(tnet_proxy_type_socks4 | tnet_proxy_type_socks4a | tnet_proxy_type_socks5),
"SOCKS(4/4a/5) proxy node plugin",
_tnet_proxy_node_socks_plugin_configure,
_tnet_proxy_node_socks_plugin_start_handshaking,
_tnet_proxy_node_socks_plugin_set_handshaking_data,
_tnet_proxy_node_socks_plugin_get_handshaking_pending_data,
_tnet_proxy_node_socks_plugin_get_handshaking_completed
};
const struct tnet_proxy_node_plugin_def_s *tnet_proxy_node_socks_plugin_def_t = &tnet_proxy_node_socks_plugin_def_s;

View File

@ -0,0 +1,34 @@
/*
* Copyright (C) 2010-2015 Mamadou DIOP.
* Copyright (C) 2015 Doubango Telecom.
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DOUBANGO.
*
*/
#if !defined(TNET_PROXY_SOCKS_PLUGIN_H)
#define TNET_PROXY_SOCKS_PLUGIN_H
#include "tinynet_config.h"
TNET_BEGIN_DECLS
TINYNET_GEXTERN const struct tnet_proxy_node_plugin_def_s* tnet_proxy_node_socks_plugin_def_t;
TNET_BEGIN_DECLS
#endif /* defined(TNET_PROXY_SOCKS_PLUGIN_H) */

View File

@ -0,0 +1,284 @@
/*
* Copyright (C) 2010-2015 Mamadou DIOP.
* Copyright (C) 2015 Doubango Telecom.
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DOUBANGO.
*
*/
#include "tnet_proxy_plugin.h"
#include "tsk_memory.h"
#include "tsk_string.h"
#include "tsk_debug.h"
#if !defined(TNET_PROXY_NODE_MAX_PLUGINS)
# define TNET_PROXY_NODE_MAX_PLUGINS 10
#endif
const tnet_proxy_node_plugin_def_t* __tnet_proxy_node_plugins[TNET_PROXY_NODE_MAX_PLUGINS] = {0};
tsk_bool_t tnet_proxy_node_is_nettransport_supported(enum tnet_proxy_type_e proxy_type, enum tnet_socket_type_e socket_type)
{
switch (proxy_type) {
case tnet_proxy_type_http:
case tnet_proxy_type_https:
return TNET_SOCKET_TYPE_IS_STREAM(socket_type);
case tnet_proxy_type_socks4:
case tnet_proxy_type_socks4a:
return TNET_SOCKET_TYPE_IS_STREAM(socket_type) && TNET_SOCKET_TYPE_IS_IPV4(socket_type);
case tnet_proxy_type_socks5: // SOCKS5 adds support for UDP and IPv6
return TNET_SOCKET_TYPE_IS_STREAM(socket_type) || TNET_SOCKET_TYPE_IS_DGRAM(socket_type);// for now we don't support socks for UDP (just like a browser)
default:
return tsk_false;
}
}
int tnet_proxy_node_init(tnet_proxy_node_t* self)
{
if (self) {
self->socket.fd = TNET_INVALID_FD;
self->socket.type = tnet_socket_type_invalid;
}
return 0;
}
int tnet_proxy_node_configure(tnet_proxy_node_t* self, ...)
{
va_list ap;
int ret = 0;
if (!self) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
va_start(ap, self);
ret = tnet_proxy_node_configure_2(self, &ap);
va_end(ap);
return ret;
}
int tnet_proxy_node_configure_2(tnet_proxy_node_t* self, va_list* app)
{
int ret = 0;
tnet_proxy_node_param_type_t ptype;
if (!self || !app) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
while ((ptype = va_arg(*app, tnet_proxy_node_param_type_t)) != tnet_proxy_node_param_type_null) {
switch (ptype) {
case tnet_proxy_node_param_type_destination_address:
case tnet_proxy_node_param_type_proxy_address: {
// (const char*)(HOST_STR), (int)(PORT_INT)
const char* HOST_STR = va_arg(*app, const char*);
int PORT_INT = va_arg(*app, int);
if (PORT_INT < 1 || PORT_INT > 0xFFF) {
TSK_DEBUG_ERROR("Invalid value for port number: %d", PORT_INT);
ret = -3;
goto bail;
}
if (ptype == tnet_proxy_node_param_type_destination_address) {
tsk_strupdate(&self->dst_host, HOST_STR);
self->dst_port = (tnet_port_t)PORT_INT;
}
else {
tsk_strupdate(&self->proxy_host, HOST_STR);
self->proxy_port = (tnet_port_t)PORT_INT;
}
break;
}
case tnet_proxy_node_param_type_ipv6: {
/* (tsk_bool_t)(IPV6_BOOL) */
self->ipv6 = va_arg(*app, tsk_bool_t);
break;
}
case tnet_proxy_node_param_type_credentials: {
/* (const char*)(LOGIN_STR), (const char*)(PASSWORD_STR) */
const char* LOGIN_STR = va_arg(*app, const char*);
const char* PASSWORD_STR = va_arg(*app, const char*);
tsk_strupdate(&self->login, LOGIN_STR);
tsk_strupdate(&self->password, PASSWORD_STR);
break;
}
case tnet_proxy_node_param_type_socket: {
/* (tnet_fd_t)(FD_FD), (enum tnet_socket_type_e)(type) */
self->socket.fd = va_arg(*app, tnet_fd_t);
self->socket.type = va_arg(*app, enum tnet_socket_type_e);
break;
}
#if TNET_UNDER_APPLE
case tnet_proxy_node_param_type_cfstreams: {
/* (CFReadStreamRef)(READ_CFSTREAM), (CFWriteStreamRef)(WRITE_CFSTREAM) */
CFReadStreamRef READ_CFSTREAM = va_arg(*app, CFReadStreamRef);
CFWriteStreamRef WRITE_CFSTREAM = va_arg(*app, CFWriteStreamRef);
if (self->cf_read_stream) CFRelease(self->cf_read_stream), self->cf_read_stream = tsk_null;
if (self->cf_write_stream) CFRelease(self->cf_write_stream), self->cf_write_stream = tsk_null;
if (READ_CFSTREAM) self->cf_read_stream = (CFReadStreamRef)CFRetain(READ_CFSTREAM);
if (WRITE_CFSTREAM) self->cf_write_stream = (CFWriteStreamRef)CFRetain(WRITE_CFSTREAM);
break;
}
#endif /* TNET_UNDER_APPLE */
default: {
TSK_DEBUG_ERROR("%d not valid param type", ptype);
ret = -2;
goto bail;
}
}
}
bail:
return ret;
}
int tnet_proxy_node_start_handshaking(tnet_proxy_node_t* self)
{
if (!self || !self->plugin || !self->plugin->start_handshaking) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
return self->plugin->start_handshaking(self);
}
int tnet_proxy_node_set_handshaking_data(tnet_proxy_node_t* self, const void* data_ptr, tsk_size_t data_size)
{
if (!self || !data_ptr || !data_size || !self->plugin || !self->plugin->set_handshaking_data) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
return self->plugin->set_handshaking_data(self, data_ptr, data_size);
}
int tnet_proxy_node_get_handshaking_pending_data(tnet_proxy_node_t* self, void** data_pptr, tsk_size_t* data_psize)
{
if (!self || !data_pptr || !data_psize || !self->plugin || !self->plugin->get_handshaking_pending_data) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
return self->plugin->get_handshaking_pending_data(self, data_pptr, data_psize);
}
int tnet_proxy_node_get_handshaking_completed(tnet_proxy_node_t* self, tsk_bool_t* completed)
{
if (!self || !completed || !self->plugin || !self->plugin->get_handshaking_completed) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
return self->plugin->get_handshaking_completed(self, completed);
}
int tnet_proxy_node_deinit(tnet_proxy_node_t* self)
{
if (self) {
TSK_FREE(self->dst_host);
TSK_FREE(self->proxy_host);
TSK_FREE(self->login);
TSK_FREE(self->password);
#if TNET_UNDER_APPLE
if (self->cf_read_stream) {
CFRelease(self->cf_read_stream), self->cf_read_stream = tsk_null;
}
if (self->cf_write_stream) {
CFRelease(self->cf_write_stream), self->cf_write_stream = tsk_null;
}
#endif
}
return 0;
}
int tnet_proxy_node_plugin_register(const tnet_proxy_node_plugin_def_t* plugin)
{
tsk_size_t i;
if (!plugin || tsk_strnullORempty(plugin->desc)) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
/* add or replace the plugin */
for (i = 0; i<TNET_PROXY_NODE_MAX_PLUGINS; i++) {
if (!__tnet_proxy_node_plugins[i] || (__tnet_proxy_node_plugins[i] == plugin)){
__tnet_proxy_node_plugins[i] = plugin;
TSK_DEBUG_INFO("Register network proxy node plugin: %s", plugin->desc);
return 0;
}
}
TSK_DEBUG_ERROR("There are already %d network proxy node plugins.", TNET_PROXY_NODE_MAX_PLUGINS);
return -2;
}
int tnet_proxy_node_plugin_unregister(const tnet_proxy_node_plugin_def_t* plugin)
{
tsk_size_t i;
tsk_bool_t found = tsk_false;
if (!plugin) {
TSK_DEBUG_ERROR("Invalid Parameter");
return -1;
}
/* find the plugin to unregister */
for (i = 0; i<TNET_PROXY_NODE_MAX_PLUGINS && __tnet_proxy_node_plugins[i]; i++) {
if (__tnet_proxy_node_plugins[i] == plugin) {
TSK_DEBUG_INFO("UnRegister network proxy node plugin: %s", plugin->desc);
__tnet_proxy_node_plugins[i] = tsk_null;
found = tsk_true;
break;
}
}
/* compact */
if (found) {
for (; i<(TNET_PROXY_NODE_MAX_PLUGINS - 1); i++) {
if (__tnet_proxy_node_plugins[i+1]) {
__tnet_proxy_node_plugins[i] = __tnet_proxy_node_plugins[i+1];
}
else {
break;
}
}
__tnet_proxy_node_plugins[i] = tsk_null;
}
return (found ? 0 : -2);
}
tsk_size_t tnet_proxy_node_plugin_registry_count()
{
tsk_size_t count;
for(count = 0;
count < TNET_PROXY_NODE_MAX_PLUGINS && __tnet_proxy_node_plugins[count];
++count) ;
return count;
}
tnet_proxy_node_t* tnet_proxy_node_create(enum tnet_proxy_type_e type)
{
tsk_size_t i;
tnet_proxy_node_t* node = tsk_null;
for (i = 0; i<TNET_PROXY_NODE_MAX_PLUGINS && __tnet_proxy_node_plugins[i]; i++) {
if ((__tnet_proxy_node_plugins[i]->type & type) == type) {
if ((node = tsk_object_new(__tnet_proxy_node_plugins[i]->objdef))) {
node->type = type;
node->plugin = __tnet_proxy_node_plugins[i];
break;
}
}
}
return node;
}

View File

@ -0,0 +1,125 @@
/*
* Copyright (C) 2010-2015 Mamadou DIOP.
* Copyright (C) 2015 Doubango Telecom.
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DOUBANGO.
*
*/
#if !defined(TNET_PROXY_PLUGIN_H)
#define TNET_PROXY_PLUGIN_H
#include "tinynet_config.h"
#include "tnet_types.h"
#include "tnet_socket.h"
#include "tsk_list.h"
#if TNET_UNDER_APPLE
# import <CFNetwork/CFNetwork.h>
#endif /* TNET_UNDER_APPLE */
TNET_BEGIN_DECLS
typedef struct tnet_proxy_node_s {
TSK_DECLARE_OBJECT;
enum tnet_proxy_type_e type;
tsk_bool_t ipv6;
char* dst_host;
tnet_port_t dst_port;
char* proxy_host;
tnet_port_t proxy_port;
char* login;
char* password;
struct {
tnet_fd_t fd;
tnet_socket_type_t type;
} socket;
#if TNET_UNDER_APPLE
CFReadStreamRef cf_read_stream;
CFWriteStreamRef cf_write_stream;
#endif /* TNET_UNDER_APPLE */
const struct tnet_proxy_node_plugin_def_s* plugin;
}
tnet_proxy_node_t;
#define TNET_PROXY_NODE(self) ((tnet_proxy_node_t*)(self))
#define TNET_DECLARE_PROXY_NONE tnet_proxy_node_t __node__
typedef enum tnet_proxy_node_param_type_e
{
tnet_proxy_node_param_type_null = 0,
tnet_proxy_node_param_type_destination_address,
tnet_proxy_node_param_type_proxy_address,
tnet_proxy_node_param_type_ipv6,
tnet_proxy_node_param_type_credentials,
tnet_proxy_node_param_type_socket,
#if TNET_UNDER_APPLE
tnet_proxy_node_param_type_cfstreams,
#endif
}
tnet_proxy_node_param_type_t;
#define TNET_PROXY_NODE_SET_NULL() tnet_proxy_node_param_type_null
#define TNET_PROXY_SET_DEST_ADDRESS(HOST_STR, PORT_INT) tnet_proxy_node_param_type_destination_address, (const char*)(HOST_STR), (int)(PORT_INT)
#define TNET_PROXY_SET_PROXY_ADDRESS(HOST_STR, PORT_INT) tnet_proxy_node_param_type_proxy_address, (const char*)(HOST_STR), (int)(PORT_INT)
#define TNET_PROXY_NODE_SET_IPV6(IPV6_BOOL) tnet_proxy_node_param_type_ipv6, (tsk_bool_t)(IPV6_BOOL)
#define TNET_PROXY_SET_CREDENTIALS(LOGIN_STR, PASSWORD_STR) tnet_proxy_node_param_type_credentials, (const char*)(LOGIN_STR), (const char*)(PASSWORD_STR)
#define TNET_PROXY_SET_SOCKET(FD_FD, type) tnet_proxy_node_param_type_socket, (tnet_fd_t)(FD_FD), (enum tnet_socket_type_e)(type)
#if TNET_UNDER_APPLE
# define TNET_PROXY_SET_CFSTREAM(READ_CFSTREAM, WRITE_CFSTREAM) tnet_proxy_node_param_type_cfstreams, (CFReadStreamRef)(READ_CFSTREAM), (CFWriteStreamRef)(WRITE_CFSTREAM)
#endif /* TNET_UNDER_APPLE */
/** Virtual table used to define a proxy node plugin */
typedef struct tnet_proxy_node_plugin_def_s
{
//! object definition used to create an instance of the plugin
const tsk_object_def_t* objdef;
//! plugin type
enum tnet_proxy_type_e type;
//! full description (usefull for debugging)
const char* desc;
int (* configure) (tnet_proxy_node_t* self, ...);
int (* start_handshaking) (tnet_proxy_node_t* self);
int (* set_handshaking_data) (tnet_proxy_node_t* self, const void* data_ptr, tsk_size_t data_size);
int (* get_handshaking_pending_data) (tnet_proxy_node_t* self, void** data_pptr, tsk_size_t* data_psize);
int (* get_handshaking_completed) (tnet_proxy_node_t* self, tsk_bool_t* completed);
}
tnet_proxy_node_plugin_def_t;
TINYNET_API tsk_bool_t tnet_proxy_node_is_nettransport_supported(enum tnet_proxy_type_e proxy_type, enum tnet_socket_type_e socket_type);
TINYNET_API int tnet_proxy_node_init(tnet_proxy_node_t* self);
TINYNET_API int tnet_proxy_node_configure(tnet_proxy_node_t* self, ...);
TINYNET_API int tnet_proxy_node_configure_2(tnet_proxy_node_t* self, va_list* app);
TINYNET_API int tnet_proxy_node_start_handshaking(tnet_proxy_node_t* self);
TINYNET_API int tnet_proxy_node_set_handshaking_data(tnet_proxy_node_t* self, const void* data_ptr, tsk_size_t data_size);
TINYNET_API int tnet_proxy_node_get_handshaking_pending_data(tnet_proxy_node_t* self, void** data_pptr, tsk_size_t* data_psize);
TINYNET_API int tnet_proxy_node_get_handshaking_completed(tnet_proxy_node_t* self, tsk_bool_t* completed);
TINYNET_API int tnet_proxy_node_deinit(tnet_proxy_node_t* self);
TINYNET_API int tnet_proxy_node_plugin_register(const tnet_proxy_node_plugin_def_t* plugin);
TINYNET_API int tnet_proxy_node_plugin_unregister(const tnet_proxy_node_plugin_def_t* plugin);
TINYNET_API tsk_size_t tnet_proxy_node_plugin_registry_count();
TINYNET_API tnet_proxy_node_t* tnet_proxy_node_create(enum tnet_proxy_type_e type);
TNET_END_DECLS
#endif /* TNET_PROXY_PLUGIN_H */

View File

@ -0,0 +1,310 @@
/*
* Copyright (C) 2010-2015 Mamadou DIOP.
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DOUBANGO.
*
*/
#include "tnet_proxydetect.h"
#include "tnet_utils.h"
#include "tsk_string.h"
#include "tsk_memory.h"
#include "tsk_debug.h"
/* "tnet_proxyinfo_t" object definition */
static tsk_object_t* _tnet_proxyinfo_ctor(tsk_object_t * self, va_list * app)
{
tnet_proxyinfo_t *info = (tnet_proxyinfo_t *)self;
if (info) {
}
return self;
}
static tsk_object_t* _tnet_proxyinfo_dtor(tsk_object_t * self)
{
tnet_proxyinfo_t *info = (tnet_proxyinfo_t *)self;
if (info){
TSK_FREE(info->autoconfig_url);
TSK_FREE(info->bypass_list);
TSK_FREE(info->hostname);
TSK_FREE(info->username);
TSK_FREE(info->password);
}
return self;
}
static const tsk_object_def_t tnet_proxyinfo_def_s =
{
sizeof(tnet_proxyinfo_t),
_tnet_proxyinfo_ctor,
_tnet_proxyinfo_dtor,
tsk_null,
};
const tsk_object_def_t *tnet_proxyinfo_def_t = &tnet_proxyinfo_def_s;
tnet_proxyinfo_t* tnet_proxyinfo_create()
{
tnet_proxyinfo_t* info = tsk_object_new(tnet_proxyinfo_def_t);
if (!info) {
TSK_DEBUG_ERROR("Failed to creatr 'tnet_proxyinfo_t' instance");
return info;
}
return info;
}
/******** Windows ************/
#if TNET_UNDER_WINDOWS && !TNET_UNDER_WINDOWS_RT
# include <Windows.h>
# include <winhttp.h>
tnet_proxyinfo_t* tnet_proxydetect_get_info(const char* url, tnet_socket_type_t socket_type, tsk_bool_t long_operation)
{
TSK_DEBUG_WARN("Proxy detection not supported on your OS");
return tsk_null;
}
/******** iOS and OSX ************/
#elif TNET_UNDER_APPLE
#import <CFNetwork/CFNetwork.h>
#define CFSafeRelease(pp_cf) if (pp_cf && *pp_cf) CFRelease(*pp_cf), *pp_cf = NULL;
#if !defined (TNET_AUTODETECT_RUNLOOP_MODE)
# define TNET_AUTODETECT_RUNLOOP_MODE "org.doubango.proxydetect.auto"
#endif
typedef struct appl_proxyinfo_xs {
CFStringRef host;
int port;
CFStringRef username;
CFStringRef password;
CFStringRef type;
}
appl_proxyinfo_xt;
static tnet_proxy_type_t _appl_proxy_type_convert(CFStringRef cfProxyType);
static void _appl_proxyinfo_release(appl_proxyinfo_xt * info);
static tsk_bool_t _appl_proxyinfo_is_valid(const appl_proxyinfo_xt * info);
static void _ProxyAutoConfigurationResultCallback(void *client, CFArrayRef proxyList, CFErrorRef error);
static void _appl_find_best_proxy(CFURLRef cfTargetURL, CFArrayRef _cfProxies, appl_proxyinfo_xt *_proxyInfo);
tnet_proxyinfo_t* tnet_proxydetect_get_info(const char* url, tnet_socket_type_t socket_type, tsk_bool_t long_operation)
{
tnet_proxyinfo_t* info = tsk_null;
CFStringRef cfUrl = CFStringCreateWithCString(CFAllocatorGetDefault(), url, kCFStringEncodingUTF8);
CFURLRef cfTargetUrl = CFURLCreateWithString(CFAllocatorGetDefault(), cfUrl, NULL);
CFDictionaryRef cfProxySettings = NULL;
CFArrayRef cfProxies = NULL;
appl_proxyinfo_xt _info = { 0 };
if (!cfTargetUrl) {
TSK_DEBUG_ERROR("Failed to create CFURLRef from %s", url);
goto resolve_done;
}
cfProxySettings = CFNetworkCopySystemProxySettings();
if (!cfProxySettings) {
TSK_DEBUG_ERROR("CFNetworkCopySystemProxySettings returned nil");
goto resolve_done;
}
cfProxies = CFNetworkCopyProxiesForURL(cfTargetUrl, cfProxySettings);
if (!cfProxies) {
TSK_DEBUG_ERROR("CFNetworkCopyProxiesForURL returned 0-array");
goto resolve_done;
}
// find best proxy
_appl_find_best_proxy(cfTargetUrl, cfProxies, &_info);
resolve_done:
if (cfUrl) {
CFRelease(cfUrl);
}
if (cfTargetUrl) {
CFRelease(cfTargetUrl);
}
if (cfProxySettings) {
CFRelease(cfProxySettings);
}
if (cfProxies) {
CFRelease(cfProxies);
}
if (_appl_proxyinfo_is_valid(&_info)) {
info = tnet_proxyinfo_create();
if (info) {
info->type = _appl_proxy_type_convert(_info.type);
info->socket_type = socket_type;
info->port = _info.port;
info->hostname = tsk_strdup(CFStringGetCStringPtr(_info.host, kCFStringEncodingUTF8));
info->username = tsk_strdup(CFStringGetCStringPtr(_info.username, kCFStringEncodingUTF8));
info->password = tsk_strdup(CFStringGetCStringPtr(_info.password, kCFStringEncodingUTF8));
}
}
_appl_proxyinfo_release(&_info);
return info;
}
static tsk_bool_t _appl_proxyinfo_is_valid(const appl_proxyinfo_xt * info)
{
if (info) {
return info->port
&& info->type && !CFEqual(info->type, kCFProxyTypeNone)
&& info->host && CFStringGetLength(info->host) > 0 && CFStringCompare(info->host, CFSTR("127.0.0.1"), 0) != kCFCompareEqualTo;
}
return tsk_false;
}
static tnet_proxy_type_t _appl_proxy_type_convert(CFStringRef cfProxyType)
{
if (CFEqual(cfProxyType, kCFProxyTypeHTTP) || CFEqual(cfProxyType, kCFProxyTypeHTTPS)) {
// kCFProxyTypeHTTPS: means the destination url is "https://" not the proxy connection type is HTTPS
return tnet_proxy_type_http;
}
else if(CFEqual(cfProxyType,kCFProxyTypeSOCKS)) {
return tnet_proxy_type_socks5;
}
else {
return tnet_proxy_type_none;
}
}
static void _appl_proxyinfo_release(appl_proxyinfo_xt * info)
{
if (info) {
CFSafeRelease(&info->host);
CFSafeRelease(&info->username);
CFSafeRelease(&info->password);
CFSafeRelease(&info->type);
info->port = 0;
}
}
static void _ProxyAutoConfigurationResultCallback(void *client, CFArrayRef proxyList, CFErrorRef error)
{
CFTypeRef* cfResult = (CFTypeRef*)client;
if (error != NULL) {
*cfResult = CFRetain(error);
} else {
*cfResult = CFRetain(proxyList);
}
CFRunLoopStop(CFRunLoopGetCurrent());
}
static void _appl_find_best_proxy(CFURLRef cfTargetURL, CFArrayRef _cfProxies, appl_proxyinfo_xt *_proxyInfo)
{
CFDictionaryRef cfProxy;
CFIndex index = 0;
CFIndex count = CFArrayGetCount(_cfProxies);
while (index < count && (cfProxy = CFArrayGetValueAtIndex(_cfProxies, index++)) && !_appl_proxyinfo_is_valid(_proxyInfo)) {
_appl_proxyinfo_release(_proxyInfo);
CFStringRef cfProxyType = CFDictionaryGetValue(cfProxy, (const void*)kCFProxyTypeKey);
if (!cfProxyType) {
continue;
}
TSK_DEBUG_INFO("Found at %li proxy type = %s", (index - 1), CFStringGetCStringPtr(cfProxyType, kCFStringEncodingUTF8));
if (CFEqual(cfProxyType, kCFProxyTypeNone)) {
continue;
}
else if (CFEqual(cfProxyType, kCFProxyTypeHTTP) || CFEqual(cfProxyType, kCFProxyTypeHTTPS) || CFEqual(cfProxyType, kCFProxyTypeSOCKS)) {
// "kCFProxyTypeHTTPS" means the url is "https://" not the connection should be TLS
CFStringRef cfHostName = (CFStringRef)CFDictionaryGetValue(cfProxy, (const void*)kCFProxyHostNameKey);
if (cfHostName) {
CFNumberRef cfPortNumber = (CFNumberRef)CFDictionaryGetValue(cfProxy, (const void*)kCFProxyPortNumberKey);
if (cfPortNumber) {
int port = 0;
if (!CFNumberGetValue(cfPortNumber, kCFNumberSInt32Type, &port)) {
continue;
}
_proxyInfo->port = port;
_proxyInfo->host = CFStringCreateCopy(CFAllocatorGetDefault(), cfHostName);
_proxyInfo->type = CFStringCreateCopy(CFAllocatorGetDefault(), cfProxyType);
CFStringRef cfStringName = (CFStringRef)CFDictionaryGetValue(cfProxy, (const void*)kCFProxyUsernameKey);
if (cfStringName) {
_proxyInfo->username = CFStringCreateCopy(CFAllocatorGetDefault(), cfStringName);
}
CFStringRef cfPassword = (CFStringRef)CFDictionaryGetValue(cfProxy, (const void*)kCFProxyPasswordKey);
if (cfPassword) {
_proxyInfo->password = CFStringCreateCopy(CFAllocatorGetDefault(), cfPassword);
}
}
}
}
else if (CFEqual(cfProxyType, kCFProxyTypeAutoConfigurationURL)) {
CFURLRef cfPACUrl = (CFURLRef)CFDictionaryGetValue(cfProxy, (const void*)kCFProxyAutoConfigurationURLKey);
if (cfPACUrl) {
TSK_DEBUG_INFO("Found at %li PACUrl = %s", (index - 1), CFStringGetCStringPtr(CFURLGetString(cfPACUrl), kCFStringEncodingUTF8));
CFTypeRef cfResult = NULL;
CFStreamClientContext context = { 0, &cfResult, NULL, NULL, NULL };
CFRunLoopSourceRef cfrunLoop = CFNetworkExecuteProxyAutoConfigurationURL(cfPACUrl,
cfTargetURL,
_ProxyAutoConfigurationResultCallback,
&context);
if (!cfrunLoop) {
TSK_DEBUG_ERROR("CFNetworkExecuteProxyAutoConfigurationURL(%li, %s) failed", (index - 1), CFStringGetCStringPtr(CFURLGetString(cfPACUrl), kCFStringEncodingUTF8));
continue;
}
static const CFStringRef kPrivateRunloopMode = CFSTR(TNET_AUTODETECT_RUNLOOP_MODE);
CFRunLoopAddSource(CFRunLoopGetCurrent(), cfrunLoop, kPrivateRunloopMode);
CFRunLoopRunInMode(kPrivateRunloopMode, DBL_MAX, false);
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), cfrunLoop, kPrivateRunloopMode);
if (cfResult == NULL) {
TSK_DEBUG_INFO("Result from ProxyAutoConfigurationResultCallback is nil");
continue;
}
if (CFGetTypeID(cfResult) == CFErrorGetTypeID()) {
CFStringRef cfErrorDescription = CFErrorCopyDescription ((CFErrorRef)cfResult);
TSK_DEBUG_ERROR("Result from ProxyAutoConfigurationResultCallback is error: %s", CFStringGetCStringPtr(cfErrorDescription, kCFStringEncodingUTF8));
CFRelease(cfErrorDescription);
}
else if (CFGetTypeID(cfResult) == CFArrayGetTypeID()) {
TSK_DEBUG_INFO("Result from ProxyAutoConfigurationResultCallback is array");
_appl_find_best_proxy(cfTargetURL, (CFArrayRef)cfResult, _proxyInfo);
}
CFRelease(cfResult);
}
else {
TSK_DEBUG_INFO("PACUrl at %li is nil", (index - 1));
}
}
}
}
/******** Not supported ************/
#else
tnet_proxyinfo_t* tnet_proxydetect_get_info(const char* url, tnet_socket_type_t socket_type, tsk_bool_t long_operation)
{
TSK_DEBUG_WARN("Proxy detection not supported on your OS");
return tsk_null;
}
#endif /* END-OF-CONDITIONAL-OS-IMPL */
tsk_bool_t tnet_proxyinfo_is_valid(const tnet_proxyinfo_t* self)
{
if (self) {
return self->port && self->type != tnet_proxy_type_none && !tsk_strnullORempty(self->hostname);
}
return tsk_false;
}

View File

@ -0,0 +1,51 @@
/*
* Copyright (C) 2010-2015 Mamadou DIOP.
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DOUBANGO.
*
*/
#ifndef TNET_PROXYDETECT_H
#define TNET_PROXYDETECT_H
#include "tinynet_config.h"
#include "tnet_types.h"
#include "tnet_socket.h"
TNET_BEGIN_DECLS
typedef struct tnet_proxyinfo_s {
TSK_DECLARE_OBJECT;
tnet_proxy_type_t type;
char* autoconfig_url;
tsk_bool_t autodetect;
char* bypass_list;
char* hostname;
tnet_socket_type_t socket_type;
tnet_port_t port;
char* username;
char* password;
}
tnet_proxyinfo_t;
TINYNET_API tnet_proxyinfo_t* tnet_proxyinfo_create();
TINYNET_API tnet_proxyinfo_t* tnet_proxydetect_get_info(const char* url, tnet_socket_type_t socket_type, tsk_bool_t long_operation);
#define tnet_proxydetect_get_info_fast(url, socket_type) tnet_proxydetect_get_info((url), (socket_type), tsk_false)
TINYNET_API tsk_bool_t tnet_proxyinfo_is_valid(const tnet_proxyinfo_t* self);
TNET_END_DECLS
#endif /* TNET_PROXYDETECT_H */

View File

@ -1,8 +1,6 @@
/*
* Copyright (C) 2010-2011 Mamadou Diop.
*
* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
@ -23,9 +21,6 @@
/**@file tnet.h
* @brief Protocol agnostic socket.
*
* @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
*
*/
#ifndef TNET_SOCKET_H
#define TNET_SOCKET_H
@ -132,6 +127,7 @@ tnet_socket_type_t;
#define TNET_SOCKET_TYPE_IS_SECURE(type) ( TNET_SOCKET_TYPE_IS_IPSEC(type) || TNET_SOCKET_TYPE_IS_TLS(type) || TNET_SOCKET_TYPE_IS_DTLS(type) || TNET_SOCKET_TYPE_IS_WSS(type) )
#define TNET_SOCKET_TYPE_UNSET(type, OP) (type = TNET_SOCKET_TYPE_IS_##OP(type) ? type ^= TNET_SOCKET_TYPE_##OP : type)
#define TNET_SOCKET_TYPE_SET(type, OP) (type |= TNET_SOCKET_TYPE_##OP)
#define TNET_SOCKET_TYPE_SET_IPV4(type) (type |= TNET_SOCKET_TYPE_IPV4)
#define TNET_SOCKET_TYPE_SET_IPV4Only(type) (type = TNET_SOCKET_TYPE_IS_IPV6(type) ? (type ^TNET_SOCKET_TYPE_IPV6)|TNET_SOCKET_TYPE_IPV4 : type)

File diff suppressed because it is too large Load Diff

View File

@ -78,6 +78,7 @@ typedef struct tnet_transport_event_s
tnet_transport_event_t;
typedef int (*tnet_transport_cb_f)(const tnet_transport_event_t* e);
struct tnet_proxyinfo_s;
TINYNET_API int tnet_transport_tls_set_certs(tnet_transport_handle_t *self, const char* ca, const char* pbk, const char* pvk, tsk_bool_t verify);
TINYNET_API int tnet_transport_start(tnet_transport_handle_t* transport);
@ -92,6 +93,7 @@ TINYNET_API int tnet_transport_isconnected(const tnet_transport_handle_t *handle
TINYNET_API int tnet_transport_have_socket(const tnet_transport_handle_t *handle, tnet_fd_t fd);
TINYNET_API const tnet_tls_socket_handle_t* tnet_transport_get_tlshandle(const tnet_transport_handle_t *handle, tnet_fd_t fd);
TINYNET_API int tnet_transport_add_socket(const tnet_transport_handle_t *handle, tnet_fd_t fd, tnet_socket_type_t type, tsk_bool_t take_ownership, tsk_bool_t isClient, tnet_tls_socket_handle_t* tlsHandle);
TINYNET_API int tnet_transport_add_socket_2(const tnet_transport_handle_t *handle, tnet_fd_t fd, tnet_socket_type_t type, tsk_bool_t take_ownership, tsk_bool_t isClient, tnet_tls_socket_handle_t* tlsHandle, const char* dst_host, tnet_port_t dst_port, struct tnet_proxyinfo_s* proxy_info);
TINYNET_API int tnet_transport_pause_socket(const tnet_transport_handle_t *handle, tnet_fd_t fd, tsk_bool_t pause);
TINYNET_API int tnet_transport_remove_socket(const tnet_transport_handle_t *handle, tnet_fd_t* fd);
TINYNET_API tnet_fd_t tnet_transport_connectto(const tnet_transport_handle_t *handle, const char* host, tnet_port_t port, tnet_socket_type_t type);
@ -102,6 +104,9 @@ TINYNET_API tsk_size_t tnet_transport_sendto(const tnet_transport_handle_t *hand
TINYNET_API int tnet_transport_set_callback(const tnet_transport_handle_t *handle, tnet_transport_cb_f callback, const void* callback_data);
TINYNET_API int tnet_transport_set_proxy_auto_detect(tnet_transport_handle_t *handle, tsk_bool_t auto_detect);
TINYNET_API int tnet_transport_set_proxy_info(tnet_transport_handle_t *handle, enum tnet_proxy_type_e type, const char* host, tnet_port_t port, const char* login, const char* password);
TINYNET_API const char* tnet_transport_dtls_get_local_fingerprint(const tnet_transport_handle_t *handle, tnet_dtls_hash_type_t hash);
#define tnet_transport_dtls_set_certs(self, ca, pbk, pvk, verify) tnet_transport_tls_set_certs((self), (ca), (pbk), (pvk), (verify))
#define tnet_transport_dtls_srtp_set_certs(self, ca, pbk, pvk, verify) tnet_transport_dtls_set_certs((self), (ca), (pbk), (pvk), (verify))
@ -160,6 +165,13 @@ typedef struct tnet_transport_s
struct ssl_ctx_st *ctx;
tnet_fingerprint_t fingerprints[TNET_DTLS_HASH_TYPE_MAX];
}dtls;
/* PROXY */
struct {
tsk_bool_t auto_detect;
struct tnet_proxyinfo_s* info; // manually set value
}
proxy;
}
tnet_transport_t;

File diff suppressed because it is too large Load Diff

View File

@ -24,6 +24,9 @@
*
*/
#include "tnet_transport.h"
#include "tnet_proxy_plugin.h"
#include "tnet_proxydetect.h"
#include "tsk_memory.h"
#include "tsk_string.h"
#include "tsk_debug.h"
@ -40,7 +43,11 @@
#endif
#if !defined(TNET_MAX_FDS)
# define TNET_MAX_FDS 0xFFFF /* Default "FD_SIZE". WIll be updated using the OS limit when the transport starts. */
# if defined(FD_SETSIZE)
# define TNET_MAX_FDS FD_SETSIZE
# else
# define TNET_MAX_FDS (0xFFFF - 1)
# endif
#endif
/*== Socket description ==*/
@ -77,6 +84,15 @@ static int addSocket(tnet_fd_t fd, tnet_socket_type_t type, tnet_transport_t *tr
static int removeSocket(int index, transport_context_t *context);
int tnet_transport_add_socket_2(const tnet_transport_handle_t *handle, tnet_fd_t fd, tnet_socket_type_t type, tsk_bool_t take_ownership, tsk_bool_t isClient, tnet_tls_socket_handle_t* tlsHandle, const char* dst_host, tnet_port_t dst_port, struct tnet_proxyinfo_s* proxy_info)
{
// TODO: support for web-proxies not added yet
(void)(dst_host);
(void)(dst_port);
(void)(proxy_info);
return tnet_transport_add_socket(handle, fd, type, take_ownership, isClient, tlsHandle);
}
int tnet_transport_add_socket(const tnet_transport_handle_t *handle, tnet_fd_t fd, tnet_socket_type_t type, tsk_bool_t take_ownership, tsk_bool_t isClient, tnet_tls_socket_handle_t* tlsHandle)
{
tnet_transport_t *transport = (tnet_transport_t*)handle;

View File

@ -1,8 +1,6 @@
/*
* Copyright (C) 2010-2015 Mamadou DIOP.
*
* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
@ -25,6 +23,9 @@
*
*/
#include "tnet_transport.h"
#include "tnet_proxy_plugin.h"
#include "tnet_proxydetect.h"
#include "tsk_memory.h"
#include "tsk_string.h"
#include "tsk_debug.h"
@ -117,6 +118,15 @@ const tnet_tls_socket_handle_t* tnet_transport_get_tlshandle(const tnet_transpor
return 0;
}
int tnet_transport_add_socket_2(const tnet_transport_handle_t *handle, tnet_fd_t fd, tnet_socket_type_t type, tsk_bool_t take_ownership, tsk_bool_t isClient, tnet_tls_socket_handle_t* tlsHandle, const char* dst_host, tnet_port_t dst_port, struct tnet_proxyinfo_s* proxy_info)
{
// TODO: support for web-proxies not added yet
(void)(dst_host);
(void)(dst_port);
(void)(proxy_info);
return tnet_transport_add_socket(handle, fd, type, take_ownership, isClient, tlsHandle);
}
/*
* Add new socket to the watcher.
*/

View File

@ -91,6 +91,16 @@ typedef enum tnet_dtls_setup_e
}
tnet_dtls_setup_t;
typedef enum tnet_proxy_type_e {
tnet_proxy_type_none = 0x00,
tnet_proxy_type_http = (0x01 << 0), // CONNECT using HTTP then starting SSL handshaking if needed
tnet_proxy_type_https = (0x01 << 1), // CONNECT using HTTPS then starting SSL handshaking if needed
tnet_proxy_type_socks4 = (0x01 << 2),
tnet_proxy_type_socks4a = (0x01 << 3),
tnet_proxy_type_socks5 = (0x01 << 4),
}
tnet_proxy_type_t;
static const char* TNET_DTLS_SETUP_NAMES[TNET_DTLS_SETUP_MAX] =
{
"UNKNOWN", "actpass", "active", "passive"

File diff suppressed because it is too large Load Diff

View File

@ -159,6 +159,9 @@ TINYNET_API tnet_fd_t tnet_sockfd_accept(tnet_fd_t fd, struct sockaddr *addr, so
TINYNET_API int tnet_sockfd_close(tnet_fd_t *fd);
TINYNET_API int tnet_sockfd_shutdown(tnet_fd_t fd);
TINYNET_API tnet_proxy_type_t tnet_proxy_type_from_string(const char* type);
TINYNET_API const char* tnet_proxy_type_to_string(tnet_proxy_type_t type);
/**Prints last network error to @b stderr.
*/
#define TNET_PRINT_LAST_ERROR(FMT, ...) \

View File

@ -22,6 +22,7 @@
#include "stun/tnet_stun_utils.h"
#include "tinynet.h"
#include "tnet_proxydetect.h"
#include "tsk_string.h"
#include "tsk_timer.h"
@ -30,8 +31,9 @@
#include "tsk_debug.h"
#define kTurnTransportFriendlyName "TURN transport"
#define kTurnTransportConnectTimeout 1500 // 1.5sec to wait until socket get connected - FIXME: save "alloc" data and delay sending
#define kTurnTransportConnectTimeout 1500 // 1.5sec to wait until socket get connected
#define kTurnStreamChunckMaxSize 0xFFFF // max size of a chunck to form a valid STUN message. Used as a guard.
#define kTurnStreamOutMaxSize 0xFFFF // max pending data size
#define TNET_TURN_SESSION_TIMOUT_GET_BEST_SEC(u_timeout_in_sec) ((u_timeout_in_sec)*950) // add small delay for code execution
#define TNET_TURN_SESSION_TIMOUT_GET_BEST_MILLIS(u_timeout_in_millis) (((u_timeout_in_millis)*950)/1000) // add small delay for code execution
@ -58,7 +60,8 @@ typedef struct tnet_turn_peer_s
uint16_t u_addr_port;
tsk_bool_t b_ipv6;
tsk_bool_t b_stream_connected;
tsk_buffer_t *p_stream_buff;
tsk_buffer_t *p_stream_buff_in;
tsk_buffer_t *p_stream_buff_out;
tnet_stun_state_t e_createperm_state;
tnet_stun_state_t e_chanbind_state;
@ -127,6 +130,12 @@ typedef struct tnet_turn_session_s
char* p_srflx_ip;
uint16_t u_srflx_port;
tsk_bool_t b_srflx_ipv6;
struct {
tsk_bool_t auto_detect;
struct tnet_proxyinfo_s* info;
}
proxy;
struct {
char* path_priv;
@ -164,8 +173,9 @@ typedef struct tnet_turn_session_s
char* p_srv_host;
uint16_t u_srv_port;
tsk_bool_t b_stream_connected;
tsk_buffer_t* p_stream_buff_out; // data pending until the socket is connected
tsk_bool_t b_stream_error; // Unrecoverable error occured
tsk_buffer_t *p_stream_buff;
tsk_buffer_t *p_stream_buff_in;
struct sockaddr_storage srv_addr;
struct tnet_transport_s* p_transport;
@ -327,7 +337,7 @@ int tnet_turn_session_create(struct tnet_socket_s* p_lcl_sock, enum tnet_turn_tr
ret = -3;
goto bail;
}
if (TNET_SOCKET_TYPE_IS_STREAM(p_lcl_sock->type) && !(p_self->p_stream_buff = tsk_buffer_create_null())) {
if (TNET_SOCKET_TYPE_IS_STREAM(p_lcl_sock->type) && !(p_self->p_stream_buff_in = tsk_buffer_create_null())) {
TSK_DEBUG_ERROR("Failed to stream buffer");
ret = -4;
goto bail;
@ -424,6 +434,31 @@ int tnet_turn_session_set_ssl_certs(struct tnet_turn_session_s* p_self, const ch
return 0;
}
int tnet_turn_session_set_proxy_auto_detect(struct tnet_turn_session_s* p_self, tsk_bool_t auto_detect)
{
if (!p_self) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
tsk_safeobj_lock(p_self);
p_self->proxy.auto_detect = auto_detect;
tsk_safeobj_unlock(p_self);
return 0;
}
int tnet_turn_session_set_proxy_info(struct tnet_turn_session_s* p_self, struct tnet_proxyinfo_s* info)
{
if (!p_self) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
tsk_safeobj_lock(p_self);
TSK_OBJECT_SAFE_FREE(p_self->proxy.info);
p_self->proxy.info = tsk_object_ref(info);
tsk_safeobj_unlock(p_self);
return 0;
}
int tnet_turn_session_prepare(tnet_turn_session_t* p_self)
{
int ret = 0;
@ -518,6 +553,18 @@ int tnet_turn_session_start(tnet_turn_session_t* p_self)
goto bail;
}
}
// Proxy info
if ((ret = tnet_transport_set_proxy_auto_detect(p_self->p_transport, p_self->proxy.auto_detect))) {
TSK_DEBUG_ERROR("Failed to set proxy autodetect option");
goto bail;
}
if (p_self->proxy.info) {
if ((ret = tnet_transport_set_proxy_info(p_self->p_transport, p_self->proxy.info->type, p_self->proxy.info->hostname, p_self->proxy.info->port, p_self->proxy.info->username, p_self->proxy.info->password))) {
TSK_DEBUG_ERROR("Failed to set proxy info");
goto bail;
}
}
// start network transport
if ((ret = tnet_transport_start(p_self->p_transport))) {
@ -759,7 +806,7 @@ int tnet_turn_session_createpermission(struct tnet_turn_session_s* p_self, const
goto bail;
}
if (TNET_SOCKET_TYPE_IS_STREAM(p_self->p_lcl_sock->type)) {
if (!p_peer->p_stream_buff && !(p_peer->p_stream_buff = tsk_buffer_create_null())) {
if (!p_peer->p_stream_buff_in && !(p_peer->p_stream_buff_in = tsk_buffer_create_null())) {
TSK_DEBUG_ERROR("Failed to create stream buffer for peer with id=%ld", p_peer->id);
ret = -5;
goto bail;
@ -980,7 +1027,7 @@ int tnet_turn_session_send_data(tnet_turn_session_t* p_self, tnet_turn_peer_id_t
}
/*** ChannelData ***/
if ((pc_peer->e_chanbind_state == tnet_stun_state_ok)) {
if (pc_peer->e_chanbind_state == tnet_stun_state_ok) {
ret = _tnet_turn_session_send_chandata(p_self, pc_peer, pc_data_ptr, u_data_size);
goto bail;
}
@ -1496,7 +1543,7 @@ static int _tnet_turn_session_send_buff_0(tnet_turn_session_t* p_self, const tne
}
if (TNET_SOCKET_TYPE_IS_DGRAM(p_self->p_lcl_sock->type)) {
#if 0
#if 1
u_sent_bytes = tnet_transport_sendto(p_self->p_transport, p_self->p_lcl_sock->fd, (const struct sockaddr *)&p_self->srv_addr, pc_buff_ptr, u_buff_size);
#else
u_sent_bytes = tnet_sockfd_sendto(p_self->p_lcl_sock->fd, (const struct sockaddr *)&p_self->srv_addr, pc_buff_ptr, u_buff_size);
@ -1506,20 +1553,41 @@ static int _tnet_turn_session_send_buff_0(tnet_turn_session_t* p_self, const tne
if (pc_peer && pc_peer->b_stream_connected && pc_peer->conn_fd != TNET_INVALID_FD) {
// Send using Peer connection if connected
// Should never be called because for now requested transport is always equal to UDP
#if 0
#if 1
u_sent_bytes = tnet_transport_send(p_self->p_transport, pc_peer->conn_fd, pc_buff_ptr, u_buff_size);
#else
u_sent_bytes = tnet_sockfd_send(pc_peer->conn_fd, pc_buff_ptr, u_buff_size, 0);
#endif
}
else {
tsk_bool_t b_delay_send = tsk_false;
// Connect if not already done
if (!p_self->b_stream_connected) {
ret = tnet_sockfd_waitUntilWritable(p_self->p_lcl_sock->fd, kTurnTransportConnectTimeout);
p_self->b_stream_connected = (ret == 0);
if (ret == 0) { // socket is valid but not connected (e.g. Proxy handshaking not completed yet)
TSK_DEBUG_INFO("Saving %u TURN bytes and waiting for 'connected' event before sending", (unsigned)u_buff_size);
if (!p_self->p_stream_buff_out && !(p_self->p_stream_buff_out = tsk_buffer_create_null())) {
TSK_DEBUG_ERROR("Failed to create buffer");
ret = -3;
goto bail;
}
if ((p_self->p_stream_buff_out->size + u_buff_size) > kTurnStreamOutMaxSize) {
TSK_DEBUG_ERROR("To much pending data");
ret = -5;
goto bail;
}
tsk_buffer_append(p_self->p_stream_buff_out, pc_buff_ptr, u_buff_size);
b_delay_send = tsk_true;
u_sent_bytes = u_buff_size;
}
else {
TSK_DEBUG_ERROR("Socket in invalid state");
ret = -6;
goto bail;
}
}
if (p_self->b_stream_connected) {
#if 0
if (!b_delay_send && p_self->b_stream_connected) {
#if 1
u_sent_bytes = tnet_transport_send(p_self->p_transport, p_self->p_lcl_sock->fd, pc_buff_ptr, u_buff_size);
#else
u_sent_bytes = tnet_socket_send_stream(p_self->p_lcl_sock, pc_buff_ptr, u_buff_size);
@ -1528,7 +1596,7 @@ static int _tnet_turn_session_send_buff_0(tnet_turn_session_t* p_self, const tne
}
}
if (u_sent_bytes != u_buff_size) {
TSK_DEBUG_ERROR("Failed to send %u bytes. Only %u sent", u_buff_size, u_sent_bytes);
TSK_DEBUG_ERROR("Failed to send %u bytes. Only %u sent", (unsigned)u_buff_size, (unsigned)u_sent_bytes);
ret = -2;
goto bail;
}
@ -1561,7 +1629,7 @@ static int _tnet_turn_session_send_pkt_0(tnet_turn_session_t* p_self, const tnet
u_min_size += kStunBuffMinPad;
if (p_self->u_buff_send_size < u_min_size) {
if (!(p_self->p_buff_send_ptr = tsk_realloc(p_self->p_buff_send_ptr, u_min_size))) {
TSK_DEBUG_ERROR("Failed to allocate buffer with size = %u", u_min_size);
TSK_DEBUG_ERROR("Failed to allocate buffer with size = %u", (unsigned)u_min_size);
ret = -3;
p_self->u_buff_send_size = 0;
goto bail;
@ -1660,7 +1728,7 @@ static int _tnet_turn_session_process_incoming_pkt(struct tnet_turn_session_s* p
case tnet_stun_pkt_type_connectionbind_success_response:
case tnet_stun_pkt_type_connectionbind_error_response:
{
const tnet_stun_attr_error_code_t* pc_attr_err;
const tnet_stun_attr_error_code_t* pc_attr_err = tsk_null;
uint16_t u_code = 0;
tnet_turn_pkt_t *pc_pkt_req = tsk_null;
tnet_turn_peer_t* pc_peer = tsk_null;
@ -1946,17 +2014,24 @@ static int _tnet_turn_session_transport_layer_process_cb(const tnet_transport_ev
tnet_turn_session_t* p_ss = (tnet_turn_session_t*)e->callback_data;
int ret = 0;
tnet_turn_pkt_t* p_pkt = tsk_null;
tsk_buffer_t* p_stream_buff = tsk_null;
tsk_buffer_t* p_stream_buff_in = tsk_null;
const void* pc_data = tsk_null;
tsk_size_t u_data_size = 0;
tsk_bool_t b_stream = tsk_false, b_stream_appended = tsk_false, b_pkt_is_complete, b_got_msg;
tsk_bool_t b_stream = tsk_false, b_stream_appended = tsk_false, b_pkt_is_complete = tsk_false, b_got_msg= tsk_false;
tnet_turn_peer_t* pc_peer = tsk_null;
switch(e->type){
case event_data:
break;
case event_connected:
if (p_ss->p_lcl_sock && p_ss->p_lcl_sock->fd == e->local_fd) {
p_ss->b_stream_connected = tsk_true;
tsk_safeobj_lock(p_ss);
p_ss->b_stream_connected = tsk_true;
if (p_ss->p_stream_buff_out && p_ss->p_stream_buff_out->size > 0) {
TSK_DEBUG_INFO("Sending %u TURN pending bytes", (unsigned)p_ss->p_stream_buff_out->size);
_tnet_turn_session_send_buff(p_ss, p_ss->p_stream_buff_out->data, (uint16_t)p_ss->p_stream_buff_out->size);
TSK_OBJECT_SAFE_FREE(p_ss->p_stream_buff_out);
}
tsk_safeobj_unlock(p_ss);
}
else {
tsk_safeobj_lock(p_ss);
@ -1973,10 +2048,14 @@ static int _tnet_turn_session_transport_layer_process_cb(const tnet_transport_ev
return 0;
case event_error:
case event_closed:
case event_removed:
/* case event_removed: */
if (p_ss->p_lcl_sock && p_ss->p_lcl_sock->fd) {
if (p_ss->p_lcl_sock && p_ss->p_lcl_sock->fd == e->local_fd) {
tsk_safeobj_lock(p_ss);
p_ss->b_stream_connected = tsk_false;
p_ss->b_stream_error = (e->type == event_error);
TSK_OBJECT_SAFE_FREE(p_ss->p_stream_buff_out);
tsk_safeobj_unlock(p_ss);
}
else {
tsk_safeobj_lock(p_ss);
@ -2011,26 +2090,26 @@ handle_data:
tsk_safeobj_lock(p_ss);
pc_peer = (tnet_turn_peer_t*)tsk_list_find_object_by_pred(p_ss->p_list_peers, __pred_find_peer_by_fd, &e->local_fd);
if (pc_peer) {
p_stream_buff = tsk_object_ref(pc_peer->p_stream_buff);
p_stream_buff_in = tsk_object_ref(pc_peer->p_stream_buff_in);
}
else {
p_stream_buff = tsk_object_ref(p_ss->p_stream_buff);
p_stream_buff_in = tsk_object_ref(p_ss->p_stream_buff_in);
}
tsk_safeobj_unlock(p_ss);
if ((ret = tsk_buffer_append(p_stream_buff, e->data, e->size))) {
if ((ret = tsk_buffer_append(p_stream_buff_in, e->data, e->size))) {
goto bail;
}
b_stream_appended = tsk_true;
}
// Guard
if (p_stream_buff->size > kTurnStreamChunckMaxSize) {
TSK_DEBUG_ERROR("Too much data in the stream buffer: %lld", p_stream_buff->size);
tsk_buffer_cleanup(p_stream_buff);
if (p_stream_buff_in->size > kTurnStreamChunckMaxSize) {
TSK_DEBUG_ERROR("Too much data in the stream buffer: %u", (unsigned)p_stream_buff_in->size);
tsk_buffer_cleanup(p_stream_buff_in);
goto bail;
}
pc_data = p_stream_buff->data;
u_data_size = p_stream_buff->size;
pc_data = p_stream_buff_in->data;
u_data_size = p_stream_buff_in->size;
}
if (!TNET_STUN_BUFF_IS_STUN2(((const uint8_t*)pc_data), u_data_size)) {
@ -2048,7 +2127,7 @@ handle_data:
if (u_len <= (u_data_size - kChannelDataHdrSize)) {
b_got_msg = tsk_true;
_tnet_turn_session_raise_event_recv_data(p_ss, pc_peer->id, &_p_data[kChannelDataHdrSize], u_len);
if (p_stream_buff) {
if (p_stream_buff_in) {
/* The padding is not reflected in the length
field of the ChannelData message, so the actual size of a ChannelData
message (including padding) is (4 + Length) rounded up to the nearest
@ -2056,7 +2135,7 @@ handle_data:
included.
*/
tsk_size_t u_pad_len = (u_len & 3) ? (4 - (u_len & 3)) : 0;
tsk_buffer_remove(p_stream_buff, 0, kChannelDataHdrSize + u_len + u_pad_len);
tsk_buffer_remove(p_stream_buff_in, 0, kChannelDataHdrSize + u_len + u_pad_len);
}
}
else {
@ -2075,8 +2154,8 @@ handle_data:
tsk_safeobj_unlock(p_ss);
}
_tnet_turn_session_raise_event_recv_data(p_ss, pc_peer ? pc_peer->id : kTurnPeerIdInvalid, pc_data, u_data_size);
if (p_stream_buff) {
tsk_buffer_cleanup(p_stream_buff);
if (p_stream_buff_in) {
tsk_buffer_cleanup(p_stream_buff_in);
}
goto bail;
} // if (!TNET_STUN_BUFF_IS_STUN2...
@ -2100,9 +2179,9 @@ handle_data:
}
// Remove the parsed data
if (p_stream_buff && p_pkt) {
if (p_stream_buff_in && p_pkt) {
tsk_size_t u_pad_len = (p_pkt->u_length & 3) ? (4 - (p_pkt->u_length & 3)) : 0;
tsk_buffer_remove(p_stream_buff, 0, kStunPktHdrSizeInOctets + p_pkt->u_length + u_pad_len);
tsk_buffer_remove(p_stream_buff_in, 0, kStunPktHdrSizeInOctets + p_pkt->u_length + u_pad_len);
}
if (p_pkt) {
@ -2150,12 +2229,12 @@ handle_data:
bail:
// Handle next message in the stream buffer
if (ret == 0 && b_got_msg && b_stream && p_stream_buff && p_stream_buff->size > 0) {
if (ret == 0 && b_got_msg && b_stream && p_stream_buff_in && p_stream_buff_in->size > 0) {
goto handle_data;
}
TSK_OBJECT_SAFE_FREE(p_pkt);
TSK_OBJECT_SAFE_FREE(p_stream_buff);
TSK_OBJECT_SAFE_FREE(p_stream_buff_in);
p_ss->cb.e.pc_enet = tsk_null;
return ret;
}
@ -2318,7 +2397,10 @@ static tsk_object_t* tnet_turn_session_dtor(tsk_object_t * self)
}
TSK_OBJECT_SAFE_FREE(p_ss->p_list_peers);
TSK_OBJECT_SAFE_FREE(p_ss->p_stream_buff);
TSK_OBJECT_SAFE_FREE(p_ss->p_stream_buff_in);
TSK_OBJECT_SAFE_FREE(p_ss->p_stream_buff_out);
TSK_OBJECT_SAFE_FREE(p_ss->proxy.info);
TSK_FREE(p_ss->ssl.path_priv);
TSK_FREE(p_ss->ssl.path_pub);
@ -2363,7 +2445,8 @@ static tsk_object_t* tnet_turn_peer_dtor(tsk_object_t * self)
TSK_OBJECT_SAFE_FREE(p_peer->p_pkt_sendind);
TSK_OBJECT_SAFE_FREE(p_peer->p_pkt_connect);
TSK_OBJECT_SAFE_FREE(p_peer->p_pkt_connbind);
TSK_OBJECT_SAFE_FREE(p_peer->p_stream_buff);
TSK_OBJECT_SAFE_FREE(p_peer->p_stream_buff_in);
TSK_OBJECT_SAFE_FREE(p_peer->p_stream_buff_out);
#if PRINT_DESTROYED_MSG || 1
TSK_DEBUG_INFO("*** TURN peer destroyed ***");
#endif

View File

@ -29,6 +29,7 @@ TNET_BEGIN_DECLS
struct tnet_turn_session_s;
struct tnet_socket_s;
struct tnet_proxyinfo_s;
enum tnet_socket_type_e;
#define kTurnPeerIdInvalid -1
@ -74,6 +75,8 @@ TINYNET_API int tnet_turn_session_create_4(struct tnet_socket_s* p_lcl_sock, enu
TINYNET_API int tnet_turn_session_set_cred(struct tnet_turn_session_s* p_self, const char* pc_usr_name, const char* pc_pwd);
TINYNET_API int tnet_turn_session_set_callback(struct tnet_turn_session_s* p_self, tnet_turn_session_callback_f f_fun, const void* pc_usr_data);
TINYNET_API int tnet_turn_session_set_ssl_certs(struct tnet_turn_session_s* p_self, const char* path_priv, const char* path_pub, const char* path_ca, tsk_bool_t verify);
TINYNET_API int tnet_turn_session_set_proxy_auto_detect(struct tnet_turn_session_s* p_self, tsk_bool_t auto_detect);
TINYNET_API int tnet_turn_session_set_proxy_info(struct tnet_turn_session_s* p_self, struct tnet_proxyinfo_s* info);
TINYNET_API int tnet_turn_session_prepare(struct tnet_turn_session_s* p_self);
TINYNET_API int tnet_turn_session_start(struct tnet_turn_session_s* p_self);
TINYNET_API int tnet_turn_session_allocate(struct tnet_turn_session_s* p_self);

View File

@ -37,6 +37,7 @@
TRTP_BEGIN_DECLS
struct trtp_rtp_packet_s;
struct tnet_proxyinfo_s;
/** RTP/RTCP manager */
typedef struct trtp_manager_s
@ -67,6 +68,12 @@ typedef struct trtp_manager_s
tmedia_rtcweb_type_t local;
tmedia_rtcweb_type_t remote;
} rtcweb_type;
struct {
tsk_bool_t auto_detect;
struct tnet_proxyinfo_s* info;
}
proxy;
struct{
uint16_t start;
@ -199,6 +206,8 @@ TINYRTP_API int trtp_manager_set_rtp_remote(trtp_manager_t* self, const char* re
TINYRTP_API int trtp_manager_set_rtcp_remote(trtp_manager_t* self, const char* remote_ip, tnet_port_t remote_port);
TINYRTP_API int trtp_manager_set_port_range(trtp_manager_t* self, uint16_t start, uint16_t stop);
TINYRTP_API int trtp_manager_set_rtcweb_type_remote(trtp_manager_t* self, tmedia_rtcweb_type_t rtcweb_type);
TINYRTP_API int trtp_manager_set_proxy_auto_detect(trtp_manager_t* self, tsk_bool_t auto_detect);
TINYRTP_API int trtp_manager_set_proxy_info(trtp_manager_t* self, enum tnet_proxy_type_e type, const char* host, tnet_port_t port, const char* login, const char* password);
TINYRTP_API int trtp_manager_start(trtp_manager_t* self);
TINYRTP_API tsk_size_t trtp_manager_send_rtp(trtp_manager_t* self, const void* data, tsk_size_t size, uint32_t duration, tsk_bool_t marker, tsk_bool_t last_packet);
TINYRTP_API tsk_size_t trtp_manager_send_rtp_packet(trtp_manager_t* self, const struct trtp_rtp_packet_s* packet, tsk_bool_t bypass_encrypt);

View File

@ -71,7 +71,9 @@
# define __C99__
#endif
#define TRTP_RTP_VERSION 2
#if !defined(TRTP_RTP_VERSION)
# define TRTP_RTP_VERSION 2
#endif /* TRTP_RTP_VERSION */
#include <stdint.h>
#ifdef __SYMBIAN32__
@ -80,14 +82,9 @@
#if defined(__APPLE__)
# include <TargetConditionals.h>
# include <Availability.h>
#endif
// http://code.google.com/p/idoubs/issues/detail?id=111
//#if TARGET_IPHONE_SIMULATOR
//# undef HAVE_SRTP
//# define HAVE_SRTP 0
//#endif
#if HAVE_CONFIG_H
#include <config.h>
#endif

View File

@ -28,6 +28,7 @@
#include "tinyrtp/rtcp/trtp_rtcp_packet.h"
#include "tinyrtp/rtcp/trtp_rtcp_session.h"
#include "tnet_proxydetect.h"
#include "turn/tnet_turn_session.h"
#include "ice/tnet_ice_candidate.h"
@ -484,7 +485,7 @@ static int _trtp_manager_send_turn_dtls(struct tnet_ice_ctx_s* ice_ctx, const vo
{
const uint8_t *record_ptr, *records_ptr = handshaking_data_ptr;
tsk_size_t record_size;
int records_len = (int)handshaking_data_size, sentlen = 0, ret;
int records_len = (int)handshaking_data_size, ret = 0;
int(*_ice_ctx_send_turn_data)(struct tnet_ice_ctx_s* self, const void* data, tsk_size_t size) = use_rtcp_channel ? tnet_ice_ctx_send_turn_rtcp : tnet_ice_ctx_send_turn_rtp;
if (!ice_ctx || !handshaking_data_ptr || !handshaking_data_size) {
TSK_DEBUG_ERROR("Invalid parameter");
@ -1286,6 +1287,33 @@ int trtp_manager_set_rtcweb_type_remote(trtp_manager_t* self, tmedia_rtcweb_type
return 0;
}
int trtp_manager_set_proxy_auto_detect(trtp_manager_t* self, tsk_bool_t auto_detect)
{
if (!self){
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
self->proxy.auto_detect = auto_detect;
return 0;
}
int trtp_manager_set_proxy_info(trtp_manager_t* self, enum tnet_proxy_type_e type, const char* host, tnet_port_t port, const char* login, const char* password)
{
if (!self){
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
if (!self->proxy.info && !(self->proxy.info = tnet_proxyinfo_create())) {
return -2;
}
self->proxy.info->type = type;
self->proxy.info->port = port;
tsk_strupdate(&self->proxy.info->hostname, host);
tsk_strupdate(&self->proxy.info->username, login);
tsk_strupdate(&self->proxy.info->password, password);
return 0;
}
/** Starts the RTP/RTCP manager */
int trtp_manager_start(trtp_manager_t* self)
{
@ -1323,6 +1351,19 @@ int trtp_manager_start(trtp_manager_t* self)
ret = -2;
goto bail;
}
/* Proxy */
// Proxy info
if ((ret = tnet_transport_set_proxy_auto_detect(self->transport, self->proxy.auto_detect))) {
TSK_DEBUG_ERROR("Failed to set proxy autodetect option");
goto bail;
}
if (self->proxy.info) {
if ((ret = tnet_transport_set_proxy_info(self->transport, self->proxy.info->type, self->proxy.info->hostname, self->proxy.info->port, self->proxy.info->username, self->proxy.info->password))) {
TSK_DEBUG_ERROR("Failed to set proxy info");
goto bail;
}
}
/* Flush buffers and re-enable sockets */
if(self->transport->master && self->is_socket_disabled){
@ -1583,7 +1624,6 @@ bail:
tsk_size_t trtp_manager_send_rtp_raw(trtp_manager_t* self, const void* data, tsk_size_t size)
{
tsk_size_t ret = 0;
static int aaaaaa = 0;
if(!self || !self->transport || !self->transport->master || !data || !size){
TSK_DEBUG_ERROR("Invalid parameter");
@ -1821,6 +1861,9 @@ static tsk_object_t* trtp_manager_dtor(tsk_object_t * self)
if (manager->ice_ctx) {
TSK_OBJECT_SAFE_FREE(manager->ice_ctx);
}
/* Proxy */
TSK_OBJECT_SAFE_FREE(manager->proxy.info);
tsk_safeobj_deinit(manager);

View File

@ -58,6 +58,7 @@
#if defined(__APPLE__)
# define TSK_UNDER_APPLE 1
# include <TargetConditionals.h>
# include <Availability.h>
#endif
#if TARGET_OS_MAC
# define TSK_UNDER_MAC 1

View File

@ -35,6 +35,22 @@
# endif
#endif
// OS X or iOS
#if defined(__APPLE__)
# define TSDP_UNDER_APPLE 1
# include <TargetConditionals.h>
# include <Availability.h>
#endif
#if TARGET_OS_MAC
# define TSDP_UNDER_MAC 1
#endif
#if TARGET_OS_IPHONE
# define TSDP_UNDER_IPHONE 1
#endif
#if TARGET_IPHONE_SIMULATOR
# define TSDP_UNDER_IPHONE_SIMULATOR 1
#endif
#if (TSDP_UNDER_WINDOWS || defined(__SYMBIAN32__)) && defined(TINYSDP_EXPORTS)
# define TINYSDP_API __declspec(dllexport)