Add support for HTTP and SOCKS proxies\nSincity final iOS features
This commit is contained in:
parent
aac1d07d67
commit
91cf3de635
|
@ -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])
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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, ...);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 */
|
|
@ -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;
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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\
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
|
@ -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) */
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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 */
|
|
@ -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
|
@ -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
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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
|
@ -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, ...) \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue